feat(certification): S7 Brand Protocol specialist module + credential#5525
feat(certification): S7 Brand Protocol specialist module + credential#5525bokelley wants to merge 1 commit into
Conversation
…tial Adds the AdCP Brand specialist credential — module S7 (track S, capstone, prereq Practitioner), the specialist_brand credential (tier 3), the adcp_specialist_brand badge, and 9 reasoning/hands-on gated criteria via the _append_criterion semantic-ID pattern (migration 514). Brand Protocol had no specialist credential despite being a first-class domain. Two-speed maturity: gates the stable identity/verification layer (brand.json resolution, brand_refs[]<->house_domain reciprocity, authorized_operators[] operator authorization, bilateral adagents.json, verify_brand_claim trust interpretation, trademark disambiguation, identity->creative-generation, and the trust-knowability spine); teaches the experimental rights lifecycle without gating it. The sandbox brand tenant did not serve verify_brand_claim, so this builds the fixture: verify_brand_claim/verify_brand_claims on the brand tenant returning a payload-envelope JWS signed_response (new response-signing key published in the aggregated JWKS), anchored to the verification-walkthrough entities; plus authorized_operators[] on the served brand.json + Sportshaus fixture. The credential is pitched at brand-judgment altitude — the cryptographic verification mechanics are deferred to the S6 Security specialist. Docs: docs/learning/specialist/brand.mdx + overview.mdx + docs.json nav. Sage: two global teaching-quality fixes (no meta-narration; affirmation on stacked corrections) + a capstone brevity guardrail. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Clean module. The protocol surface conforms and the experimental rights lifecycle is fenced off from the gate in all three places it could leak. Holding the stamp because you marked it not-for-auto-merge — this is the second-reviewer pass, not the merge gate.
Things I checked
- JWS envelope conforms.
ad-tech-protocol-expert: sound-with-caveats. The signedresponsemember foldssandbox:trueinto both the unsigned body andpayload.response(brand-claim-handlers.ts:909,:965), so the body==payload.responseinvariant holds — locked by the single-target test attraining-agent-brand-claim-signing.test.ts:1368-1371.responseisadditionalProperties:true, so the marker validates. - The bulk shaping handles the schema's subtlest trap.
verify-brand-claims-response.jsonrequiresstatuson the success arm (notverification_status) and forbidsstatus/claim_typeon the error arm. The handler remapsverification_status->statusand emits{error}-only failures (brand-claim-handlers.ts:953-959). Right shape. The single-target arm correctly keepsverification_status. - Migration 514 is idempotent and converges to exactly 9 criteria. The
INSERT ... ON CONFLICT DO UPDATEresetsexercise_definitionsto the base (emptysuccess_criteria) on every re-run (514:419-424), then the nine_append_criterioncalls re-append from zero with analready_presentguard — no double-count. FK order is correct (module -> badge -> credential), and the define/call/DROP FUNCTIONpattern matches prior migrations. 514 is the next free number after 513. - No key leakage, cross-purpose isolation holds.
security-reviewer: no production-impacting issues.brand-response-signing.tsdestructures only{kty,crv,x}from the public export — no path fordto reach the JWKS — and the brand key carries a distinctadcp_use: response-signing+training-brand-resp-kid, asserted attraining-agent-brand-claim-signing.test.ts:1341-1351. TheADCP_AUTH_TOKENinbrand.mdx:226is the pre-existing public sandbox token, not a new leak. - Curriculum alignment is clean.
education-expert: sound-with-caveats. Nine gated criteria map onto the four assessment dimensions with no orphans and nothing double-gated; the experimental rights lifecycle is taught-not-gated in all three places (empty inlinesuccess_criteria, no_append_criterionfor rights, explicit "not assessed" prose). Stable semantic criterion IDs — the right shape for re-cert triggering.
Follow-ups (non-blocking — file as issues)
- Bulk
brand_domaincan mix subjects.brand-claim-handlers.ts:962-963picksbrand_domainfrom the first resolved claim, but subsidiary claims resolve tosportshaus-holdings.examplewhile parent/property/trademark resolve tostreamhaus.example. A mixed batch signs one envelope whosebrand_domaindoesn't describe everyresults[]entry. Spec-legal (bulk is intra-agent, onebrand_domainper envelope) and intentional per the "one tenant answers for several houses" header note — but it's a fixture smell that would be wrong if one agent ever mapped to one brand. Worth a comment at minimum. expected_resolution_window_daysemitted at the public tier. The spec text is self-contradictory (verify_brand_claim.mdx:76/:133: REQUIRED onpending_reviewyet listed Authorized-only). The handler always emits it (brand-claim-handlers.ts:679), which satisfies the MUST but exposes an authorized-tier field name to unauthenticated callers. Flag for the spec owner, not this PR.- Criterion 8 vs its weight (
education-expert).s7_ex1_sc_identity_drives_generationis the most conceptually loaded criterion but shares the lowest-weighted 20% dimension with identity resolution — a learner can fail generation-mapping entirely and still clear the dimension. Either split the dimension or down-scope the criterion text.
Minor nits (non-blocking)
- Real brand name in a new fixture.
brand-claim-handlers.ts:697usesnikeinc.example("Nike") as thedisputedparent claim. CLAUDE.md forbids real company names in new examples — use a fictional house (apex-holdings.example). Mitigating:static/schemas/source/brand.jsonalready shipsnikeinc.com, so it's not novel in the repo, but it's net-new here and a one-token fix. - Criterion 5 wording straddles the S6 boundary (
education-expert).s7_ex1_sc_signed_response_trust_interpretationasks the learner to reject "a response whose signed payload does not match" — detecting a mismatch is the S6 cryptography the module disclaims. The scoring guide already softens this to "knowing the response must be verified is required"; align the criterion text to match so the gated/not-gated seam stays clean. - Bulk invariant untested. The single-target test asserts body==
payload.response; the bulk test (training-agent-brand-claim-signing.test.ts:1423-1432) only checks envelope verification and count. Construction is identical, but oneexpect(signed_response.payload.response).toEqual({results, sandbox:true})would lock it.
Code is clean and CI-green per the PR body. Flipping to approve needs only the human merge gate you asked for plus the nikeinc.example rename.
Held for human approval: PR body marks this "Not for auto-merge — needs a human/second-reviewer pass before merge." No blocking label set; respecting the author's stated merge gate.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
What
Adds the AdCP Brand specialist certification — the first specialist credential for a first-class protocol domain that had none. Module S7 (track S, capstone, prereq Practitioner), the
specialist_brandcredential (tier 3), theadcp_specialist_brandbadge, and 9 gated reasoning/hands-on criteria via the_append_criterionsemantic-ID pattern (migration 514).Why
Brand Protocol (brand.json identity, distributed publishing, brand hierarchy,
verify_brand_claim, trademarks, rights) is a headline 3.1 domain with no specialist track —build-an-agent.mdxliterally said "no skill today." The domain is two-speed, and so is this module:brand.jsonidentity (public vs authorized tiers) · distributed-publishing mutual assertion (brand_refs[]↔house_domain) · operator authorization (authorized_operators[]) · bilateraladagents.jsonconfirmation ·verify_brand_claimtrust interpretation · direction-asymmetric trust · trademark disambiguation · identity→creative-generation · the trust-knowability spine (authorship≠truth, consistency≠standing).get_rights/acquire_rights/update_rights,brand.rights_lifecycle).It's pitched at brand-judgment altitude, not cryptography — the signature-verification mechanics (key resolution, canonicalization, signature checking) are deferred to the S6 Security specialist; here the gate is knowing what a verification outcome means for trust.
The sandbox fixture (was missing)
The training agent's brand tenant did not serve
verify_brand_claim. This builds it:verify_brand_claim/verify_brand_claimson the brand tenant, returning a payload-envelope JWSsigned_response(perresponse-payload-jws-envelope.json) under a newadcp_use: response-signingkey published in the aggregated JWKS, anchored to the verification-walkthrough entities (Sportshaus / StreamHaus / Northwind).authorized_operators[]added to the servedbrand.json+ the Sportshaus walkthrough fixture so operator authorization is demonstrable.result_entryshapes (statuson success,{error}on failure).Also
docs/learning/specialist/brand.mdx+overview.mdxrow +docs.jsonnav.certification-tools.ts.Validation
Not for auto-merge
Reviewable diff; needs a human/second-reviewer pass before merge.
🤖 Generated with Claude Code