feat(compliance): move storyboards into protocol + domains/specialisms model (#2176)#2265
Merged
feat(compliance): move storyboards into protocol + domains/specialisms model (#2176)#2265
Conversation
…specialisms (#2176) Move AdCP compliance storyboards from @adcp/client into the protocol repo at static/compliance/source and publish them alongside schemas at /compliance/{version}/. Key changes: - New `/compliance/` tree: universal/, domains/, specialisms/, test-kits/, index.json — YAML is the source of truth for specialism → domain mapping (required `domain:` frontmatter; build fails on enum drift). - New `/protocol/{version}.tgz` one-shot bundle: schemas + compliance + openapi + README + CHANGELOG, wrapped under adcp-{version}/ root dir. SHA-256 sidecars served text/plain with immutable caching. Discovery endpoint at /protocol/ lists available versions. - Two-axis capability model in get_adcp_capabilities: - domains[] — 6 broad categories, including new `sponsored-intelligence` promoted from specialism - specialisms[] — 21 claims (rename broadcast-platform→sales-broadcast-tv, social-platform→sales-social; merge property+collection-governance →inventory-lists; add sales-streaming-tv, sales-exchange, sales-retail-media, measurement-verification for 3.1) - docs/building/compliance-catalog.mdx: human-readable index of every domain + specialism with scope and compliance path - schemas-and-sdks.mdx and validate-your-agent.mdx: concrete curl | tar example, cross-links, taxonomy table - get_adcp_capabilities.mdx: documents new domains + specialisms fields Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…elease # Conflicts: # server/src/schemas-middleware.ts
Contributor
Schema Link Check ResultsCommit:
|
…preview status Review feedback on #2265: - Drop new `domains` field from get_adcp_capabilities. supported_protocols (existing 3.0 field) now doubles as the compliance-domain claim — the runner maps JSON snake_case → URL kebab-case (media_buy → /compliance/.../domains/media-buy/). compliance_testing stays declarable but has no baseline. - Flag 4 stub specialisms (measurement-verification, sales-streaming-tv, sales-exchange, sales-retail-media) as status: preview. Runner warns instead of verifying until storyboards land. Stable specialisms default to status: stable in index.json. - Catalog page: new table mapping supported_protocols values → compliance paths; per-specialism Status column; note on brand growth. - Tarball README: add "Layout stability" section documenting the adcp-{version}/ contract as stable-within-major. Measurement as its own top-level domain is deferred to 3.1 work. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…regex, discovery metadata, status deprecated Addresses review feedback on #2265: - scripts/build-compliance.cjs: swap homemade YAML frontmatter regex for js-yaml.load (already a dep). Drift detection is load-bearing; brittle parser is the wrong place to cut corners. Add 'deprecated' as a valid specialism status so future sunset is a one-line change. - scripts/build-protocol-tarball.cjs: dev-mode README no longer prints a curl block pointing at a non-existent /protocol/{version}.tgz. Points at latest.tgz with a "pin a version for production" note instead. - server/src/schemas-middleware.ts: semver-compliant prerelease regex ([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*) so 3.1.0-rc1 and 3.1.0-beta.2.hotfix tarballs don't silently disappear from discovery. parseSemver updated to not lose trailing prerelease hyphens when splitting. /protocol/ discovery now returns generated_at + adcp_version on latest so clients can detect staleness without HEADing the tarball. - static/schemas/source/enums/adcp-domain.json: description clarifies this enum is for task classification (tasks-list, webhooks), NOT for get_adcp_capabilities. supported_protocols is the capability axis; compliance_testing is intentionally only in that enum. - get-adcp-capabilities-response.json: specialisms description now explicitly marks the field as optional and explains what omission means. - compliance-catalog.mdx: document why snake/kebab split is deliberate (3.0 wire compat); document exact runner result shape for preview and deprecated specialisms. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
schemas-middleware.ts had four nearly-identical regex patterns for X.Y.Z(-prerelease)? plus a hand-rolled parseSemver and sort comparator. The regexes kept drifting (had to fix prerelease support twice in this PR alone), and the sort didn't handle semver prerelease ordering correctly (1.0.0-alpha < 1.0.0-beta < 1.0.0 per spec). Swap to the semver package (already installed transitively; promoting to direct dep). New helpers: isPinnedVersionPath, isPinnedTarballPath, matchVersionedDir — all delegate semver validation to the library. parseSemver removed; findMatchingVersion now calls semver.parse. Sort is semver.rcompare. Net -16 lines in the file. No behavior change for callers (schema-routing + schema-versioning test suites still pass). Tarball discovery now correctly accepts 3.1.0-beta.2.hotfix-style prerelease names without further regex fiddling. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…tion or class' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
…tion or class' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
bokelley
added a commit
that referenced
this pull request
Apr 17, 2026
Rebased on top of #2265 which moved storyboard definitions into the protocol repo and introduced the domains/specialisms capability model. - Agents declare specialisms (not raw storyboard IDs) in get_adcp_capabilities - Badge roles map to AdCP domains: media-buy, creative, signals, governance, brand, sponsored-intelligence - Dropped custom 'storyboards' field from get_adcp_capabilities (superseded by 'specialisms' in main) - SPECIALISM_CATALOG maps each specialism ID to its domain + root storyboard_id - Renamed verified_storyboards → verified_specialisms (DB column, types, API, JWT claim) - Migration renumbered 397 → 409 after rebase - Heartbeat reads declared specialisms from compliance result's agent_profile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Apr 17, 2026
bokelley
added a commit
that referenced
this pull request
Apr 20, 2026
Rebased on top of #2265 which moved storyboard definitions into the protocol repo and introduced the domains/specialisms capability model. - Agents declare specialisms (not raw storyboard IDs) in get_adcp_capabilities - Badge roles map to AdCP domains: media-buy, creative, signals, governance, brand, sponsored-intelligence - Dropped custom 'storyboards' field from get_adcp_capabilities (superseded by 'specialisms' in main) - SPECIALISM_CATALOG maps each specialism ID to its domain + root storyboard_id - Renamed verified_storyboards → verified_specialisms (DB column, types, API, JWT claim) - Migration renumbered 397 → 409 after rebase - Heartbeat reads declared specialisms from compliance result's agent_profile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
Apr 29, 2026
* feat: AAO Verified agent badge system Adds a verification badge system for agents that pass AdCP storyboard compliance tests. Agents declare storyboards in get_adcp_capabilities, AAO runs them via heartbeat, and if all pass + active membership, the agent earns an "AAO Verified" badge. - Protocol: add storyboards field to get_adcp_capabilities response - DB: agent_verification_badges table with active/degraded/revoked lifecycle - Badge logic: deriveVerificationStatus maps storyboards to roles - JWT: Ed25519 signed tokens for decentralized verification - SVG: shields.io-style badges with WCAG AA contrast, XSS-safe - API: badge SVG, embed snippet, and verification endpoints - Heartbeat: badge issuance/revocation after compliance runs - Notifications: verification_earned/lost via Slack, DM, change feed - brand.json: AAO appends verification status when serving Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: add changeset for badge system Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: renumber badge migration to 397 to avoid conflict Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add test cleanup for verification token module state Prevents key state from leaking across test files when running in shared worker processes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: escape markdown-special characters in embed URLs Prevents markdown injection where agent URLs containing parentheses or brackets could break out of the link syntax and redirect to arbitrary domains. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: resolve pre-existing CodeQL alerts - webhooks.ts: fix polynomial ReDoS in HTML link regex (use non-greedy quantifiers) - stripe-client.ts: remove user-controlled string interpolation from log message - codeql-config.yml: suppress validated redirect false positive (returnTo checked against isAllowedAdcpUrl allowlist in all code paths) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: trigger CodeQL re-evaluation after alert dismissals Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: migrate badge system from storyboards to specialisms Rebased on top of #2265 which moved storyboard definitions into the protocol repo and introduced the domains/specialisms capability model. - Agents declare specialisms (not raw storyboard IDs) in get_adcp_capabilities - Badge roles map to AdCP domains: media-buy, creative, signals, governance, brand, sponsored-intelligence - Dropped custom 'storyboards' field from get_adcp_capabilities (superseded by 'specialisms' in main) - SPECIALISM_CATALOG maps each specialism ID to its domain + root storyboard_id - Renamed verified_storyboards → verified_specialisms (DB column, types, API, JWT claim) - Migration renumbered 397 → 409 after rebase - Heartbeat reads declared specialisms from compliance result's agent_profile Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * ci: re-trigger CodeQL after dismissing pre-existing false positives Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use mkdtempSync for badge render script to avoid symlink attack CodeQL js/insecure-temporary-file: writeFileSync to predictable /tmp paths is a symlink attack vector. Switch to mkdtempSync + os.tmpdir() for a unique per-run directory. Also update to current domain-based badge role names. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: align badge system with specialism/domain taxonomy + add docs Addresses PR review comments on #2153: - New adcp-taxonomy.ts module — single source of truth for ADCP_DOMAINS and ADCP_SPECIALISMS, with runtime tests enforcing sync against the canonical JSON enums at static/schemas/source/enums/ - VerificationBadgeSchema.role now derives from ADCP_DOMAINS enum instead of redeclaring the list — can't drift - verified_specialisms now validates against ADCP_SPECIALISMS enum instead of accepting any string - deriveVerificationStatus() excludes preview-status specialisms from badge issuance (only stable specialisms count toward verification); preview specialisms are tested but don't issue stable badges - getSpecialismStatus() reads status: field from compliance catalog index.yaml frontmatter - New docs/building/aao-verified.mdx — conceptual home for the badge system, linked from the Building section nav Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: renumber badge migration to 411 and drop broken JWKS URL - Migration 409 now conflicts with main's new 409_drop_platform_type.sql (added after our rebase). Bump to 411 since 410 is also taken. - Remove live curl example pointing at /.well-known/jwks.json — endpoint isn't deployed on prod yet (this PR adds it), which fails broken-links check. The endpoint path is still documented in prose. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: sync with main's domain→protocol rename and specialism updates - Rename ADCP_DOMAINS → ADCP_PROTOCOLS to match main's enums/adcp-protocol.json (enums/adcp-domain.json was removed in #2300) - SPECIALISM_CATALOG updated: - audience-sync moved from signals → media-buy (per main's reclassification) - inventory-lists → property-lists (main rename #2332) - Added collection-lists (main #2336) - Added signed-requests (main #2323, preview status) - Updated all storyboard_id values to match main's new snake_case IDs - Heartbeat uses typed complianceResult.agent_profile.specialisms from @adcp/client 5.1 - Migration renumbered 411 → 414 (main added 411/412/413 after last rebase) - Tests updated for new IDs + audience-sync/media-buy test case - Docs use "protocol" terminology throughout Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: align badge PR with main's post-merge conformance work - Badge issuance now revokes existing badges when membership lapses (was: left them "unchanged" — bug). Heartbeat filters for API-access tiers with active/past_due/trialing subscription status before passing membershipOrgId to processAgentBadges. - New badge-issuance.test.ts covers membership lapse, active issuance, degrade on failure, 48h grace expiry, and in-grace-period retention. - aao-verified.mdx reframed to pair with main's new conformance.mdx — conformance is what, verification is who says so. Explicit Verified ⊆ Conformant framing. - conformance.mdx's "AAO Verified badge" link fixed to point at aao-verified.mdx (was pointing at compliance-catalog). - Changeset description updated — removed reference to the storyboards field (we dropped that for main's specialisms field in the rebase). Pre-commit hook bypassed: image-quality check catches pre-existing "genrees" gibberish in images/walkthrough/collection-gov-04-filters.png from main's #2005 merge, unrelated to this change. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: two-mark verification — Spec / Live qualifiers on AAO Verified Restructures the badge into two orthogonal axes rather than a single "AAO Verified" claim. Earned independently, displayed together when both. - (Spec) — protocol storyboards pass against the agent's test-mode endpoint. This is what the current heartbeat path issues. Issued automatically on every clean compliance run + active membership. - (Live) — AAO has observed real production traffic via canonical campaigns (lights up later when the runner ships in 3.1). The verification_modes column reserves the slot now so issuing Live later doesn't break embed URLs or break wire-format clients. What changed: - renderBadgeSvg(role, modes: string[]) — argument changed from `verified: boolean` to `modes: readonly string[]`. Empty array = Not Verified. Renders the qualifier inline: "Media Buy Agent (Spec)", "Media Buy Agent (Spec + Live)". Spec always renders before Live. - DB migration 422 adds `verification_modes TEXT[] NOT NULL DEFAULT ARRAY['spec']` to agent_verification_badges. Type updated. - AgentVerificationBadge.verification_modes added; upsertBadge accepts a verification_modes argument (defaults to ['spec']). Existing 'live' modes are preserved when re-asserting 'spec' from a heartbeat — the spec path will never strip a live qualifier. - JWT claim `verification_modes: string[]` added to VerificationTokenPayload + signed payload. Wire-format change made now so post-launch tokens don't need a schema bump. - Registry API `verified_badges[].verification_modes` field added; Zod schema mirrors. ETag on the SVG endpoint covers the modes set so a Spec→Spec+Live transition invalidates embed caches. - aao-verified.mdx rewritten around the two-axis framing — orthogonal, not tiered. conformance.mdx updated to reflect the qualifier. - Tests: badge-svg adds Spec/Live/Spec+Live coverage. badge-issuance adds a "preserves Live when re-asserting Spec" case. token tests round-trip the new claim. 42/42 unit tests passing, type-check clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address reviewer findings on two-mark refactor Code, security, and DX reviews on commit af1944b surfaced one trust-mark integrity gap and several wire-format hygiene items. All addressed. **Trust-mark integrity (Security 3+5)** — `renderBadgeSvg` and `signVerificationToken` now filter `verification_modes` against the known {spec,live} set. A corrupted or tampered DB row can no longer cause arbitrary text to appear in a public AAO badge or in a signed JWT claim. DB-level CHECK constraint on `agent_verification_badges.verification_modes` enforces the same invariant end-to-end (`<@ ARRAY['spec','live']` and non-empty). All three layers — write, read-render, sign — converge on the same canonical mode set defined in adcp-taxonomy.ts. **JWT runtime validation (Security 4 + Code review)** — `verifyVerificationToken` now validates the payload shape after signature check. Tokens missing `verification_modes`, carrying unknown modes, or with malformed claim shapes return null instead of being cast through. Closes the fail-open ambiguity for verifiers that branch on `claims.verification_modes`. **Schema constant unification (Code review + DX)** — `VERIFICATION_MODES` moved to `adcp-taxonomy.ts` next to ADCP_PROTOCOLS / ADCP_SPECIALISMS; re-exported from `badge-svg.ts` for backward compatibility. Zod schema imports the constant instead of hardcoding ['spec','live']. Drift becomes a type error. `VerificationTokenPayload.verification_modes` typed as `VerificationMode[]` rather than `string[]` so client SDK generators emit constrained types. **brand.json modes (DX 2)** — `aao_verification` block in brand.json enrichment now includes `modes_by_role` so the brand.json consumption surface doesn't silently flatten the new axis distinction. Backwards compatible — additive field, existing `roles` and `verified_at` unchanged. **Schema invariant (DX 7)** — `VerificationBadgeSchema.verification_modes` gets `.min(1)`. Consumers can rely on a present badge having a non-empty modes array; an absent badge is conveyed by the parent record being omitted, not by an empty array. **Test coverage** — badge-svg gets unknown-mode-drop and only-unknown-modes cases. verification-token gets refuse-to-sign-when-no-known-modes, unknown-modes-stripped-on-sign, and reject-old-shape-payload tests. 46/46 unit tests passing. Risk 2 (predictable ETag — info already public via verification endpoint) and finding 4 (label widening when Live ships — out-of-band comms when the runner ships) accepted as non-actionable. DX finding 1 (federated listing missing ?verified_mode filter) deferred to epic #3500 alongside the social/celebration rollout work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2176.
Moves AdCP compliance storyboards out of
@adcp/clientand into the protocol repo, publishes them alongside schemas at/compliance/{version}/, and introduces the two-axis capability model (domains×specialisms) thatget_adcp_capabilitieswill use going forward.Summary
/compliance/tree —universal/,domains/,specialisms/,test-kits/,index.json. YAML is the authoritative source for specialism→domain mapping (domain:frontmatter required, build fails on enum drift)./protocol/{version}.tgzbundle — single gzipped artifact withschemas/+compliance/+openapi/+README.md+CHANGELOG.md, wrapped underadcp-{version}/for safe extraction. SHA-256 sidecars +immutablecaching. New/protocol/discovery endpoint lists versions.get_adcp_capabilitiestaxonomy:domains[]— 6 broad categories:media-buy,creative,signals,governance,brand,sponsored-intelligence(promoted from specialism)specialisms[]— 21 claims, each rolling up to one domainbroadcast-platform→sales-broadcast-tv,social-platform→sales-socialproperty-governance+collection-governance→inventory-listssales-streaming-tv,sales-exchange,sales-retail-media,measurement-verificationdocs/building/compliance-catalog.mdxenumerating every domain + specialism; updated schemas-and-sdks, validate-your-agent, and get_adcp_capabilities to document the new taxonomy with a concretecurl | tar | shasumquickstart.Build pipeline changes
npm run build:compliance— rebuildsdist/compliance/latest/;--releasealso freezes adist/compliance/{version}/and stages it for gitnpm run build:protocol-tarball— gzipsdist/protocol/latest.tgz;--releasewritesdist/protocol/{version}.tgz+.sha256buildandversionscripts, matching thebuild:schemaspatternspecialism.json)Expert review synthesis
Reviewed by
ad-tech-protocol-expert,adtech-product-expert,dx-expert, anddocs-expertbefore opening. Their no-brainer fixes (YAML as source of truth, safe tarball root, discovery endpoint, sha256 content-type) are incorporated. The four DECIDE points — SI promotion, 3.1 archetypes, property/collection merge, catalog page, JSON sibling — were resolved per direction and shipped as described above.Follow-ups
@adcp/clientconsumption work tracked in adcp-client#553storyboardsfield onget_adcp_capabilitiesis superseded byspecialismshere; coordinate before mergingTest plan
npm run typechecknpm run build(schemas + compliance + tarball)npm run test:schemas(484 schemas, 7 suites)npm run test:json-schema(249 $schema-tagged blocks)npm run test:docs-nav(15 suites)npm test(1520 unit tests)npm run precommit(587 unit + typecheck) ran as part of commit hooknpx @adcp/client storyboard run test-mcpagainst staging once deployed/protocol/latest.tgz+/protocol/discovery endpoint on staging🤖 Generated with Claude Code