feat(verification): brand.json + /verification per-version detail (#3524 stage 5)#3604
Merged
feat(verification): brand.json + /verification per-version detail (#3524 stage 5)#3604
Conversation
stage 5) Final stage of #3524. brand.json enrichment now includes a `badges[]` array — one entry per (role, adcp_version) with full per-version detail. Per Q6 of the resolved-decisions thread, this is the canonical forward-compat shape: adding future axes to a badge won't change the array. `roles[]` and `modes_by_role` stay as deprecated aliases for one release. Their values reflect "the current best mark" — highest- version badge per role. Clients reading them today keep working when parallel-version badges ship; new clients should read `badges[]` for the full picture. Removal target: AdCP 4.0. The /verification endpoint (decentralized public verifier surface) now includes adcp_version on each badge entry, validated through the same shape regex the /compliance endpoint uses. After this PR, #3524 is fully shipped. Deferred panel polish (role grouping, "show all versions" disclosure) tracked in #3603. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Security review: - MED: brand.json adcp_version asymmetry. /verification and /compliance both gate adcp_version through isValidAdcpVersionShape; brand.json emitted it raw. Fixed — now mirrored across all three surfaces. The DB CHECK is the actual gate, but brand.json is the public buyer- facing surface, so any future code path that bypasses the CHECK (raw SQL backfill, restored snapshot) MUST NOT leak through. - LOW: modes_by_role is a real footgun for buyers pinned to a specific AdCP version — they could read "spec+live" and infer the wrong contract for their version. Added a `deprecation_notice` field that ships alongside the data so long-tail crawlers see the warning without tracking release notes. Code review: - Schema gap: brand.json enrichment had no documented contract for the new badges[] array. Added an `AaoVerificationBlock` TS interface inline so future contributors don't drift the wire format. Strict type on the assignment so a missing field fails at compile time. - Comment wording: "first row per role is the highest" was misleading because roles can interleave in the SQL sort. Clarified the dedupe invariant. - Deprecation horizon: documented "≥6 months from this PR's merge per the cadence policy in #2359" alongside the "AdCP 4.0" target so buyer agents have a calendar reference rather than a moving version. Code review nit on extracting enrichAgentEntries to a module-level pure function for unit testing — agreed it's worth doing as a follow-up but didn't want to expand this PR's blast radius. Filing separately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7b696a1 to
2f05141
Compare
This was referenced Apr 30, 2026
bokelley
added a commit
that referenced
this pull request
Apr 30, 2026
The Stage 5 brand.json changeset (PR #3604) was committed with `\`\`\`jsonc` (literal backslash-backticks) for its code fence, which trips Mintlify's acorn parser on every CI run: parsing error ./.changeset/per-version-badges-stage5-brand-json.md:15:13 - Could not parse expression with acorn Replacing the escaped fence with a plain `\`\`\`jsonc` block clears the warning. Documentation/content unchanged. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
Apr 30, 2026
Three follow-ups to #3524: 1. Docs (docs/building/aao-verified.mdx). Last updated for the orthogonal-axes framing in #3536; didn't mention the per-version model that just shipped. Added a "Per-version badges" section, updated the SVG/embed sections with both URL shapes (legacy auto-upgrade and version-pinned), added adcp_version to the JWT claim block with explicit verifier guidance ("verifiers MUST check adcp_version against the AdCP version they care about" — closes the cross-version replay concern from Stage 2's security review), and added a brand.json enrichment subsection documenting the badges[] array and the deprecation policy. 2. Refactor for testability. The shaping logic that builds the aao_verification block was a closure inside the brand.json route handler — unreachable from unit tests. Extracted to services/aao-verification-enrichment.ts as buildAaoVerificationBlock(badges). The route handler keeps the JSON traversal and assignment; the builder is pure with 14 new unit tests covering empty input, single-badge, multi-version dedupe with caller-ordering preserved, modes_by_role flattening (the "buyer pinned to 3.0 sees the wrong contract" footgun), adcp_version shape filtering (defense in depth), and the deprecation notice content. Code-review nit on PR #3604. 3. PROTOCOL_LABELS audit comment in dashboard-agents.html. The `${protocol} Agent${versionSegment}` label construction relies on PROTOCOL_LABELS values not ending in "Agent" — added a comment pinning the invariant so a future contributor adding a new protocol doesn't accidentally produce "Foo Agent Agent 3.1". DX expert nit from #3603. No wire-format changes — brand.json output is byte-for-byte identical to what shipped in #3604. Panel UX changes (role grouping, "show all versions" disclosure) explicitly defer until parallel-version badges land in production with real buyer feedback to design against. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
Apr 30, 2026
…3654) Three follow-ups to #3524: 1. Docs (docs/building/aao-verified.mdx). Last updated for the orthogonal-axes framing in #3536; didn't mention the per-version model that just shipped. Added a "Per-version badges" section, updated the SVG/embed sections with both URL shapes (legacy auto-upgrade and version-pinned), added adcp_version to the JWT claim block with explicit verifier guidance ("verifiers MUST check adcp_version against the AdCP version they care about" — closes the cross-version replay concern from Stage 2's security review), and added a brand.json enrichment subsection documenting the badges[] array and the deprecation policy. 2. Refactor for testability. The shaping logic that builds the aao_verification block was a closure inside the brand.json route handler — unreachable from unit tests. Extracted to services/aao-verification-enrichment.ts as buildAaoVerificationBlock(badges). The route handler keeps the JSON traversal and assignment; the builder is pure with 14 new unit tests covering empty input, single-badge, multi-version dedupe with caller-ordering preserved, modes_by_role flattening (the "buyer pinned to 3.0 sees the wrong contract" footgun), adcp_version shape filtering (defense in depth), and the deprecation notice content. Code-review nit on PR #3604. 3. PROTOCOL_LABELS audit comment in dashboard-agents.html. The `${protocol} Agent${versionSegment}` label construction relies on PROTOCOL_LABELS values not ending in "Agent" — added a comment pinning the invariant so a future contributor adding a new protocol doesn't accidentally produce "Foo Agent Agent 3.1". DX expert nit from #3603. No wire-format changes — brand.json output is byte-for-byte identical to what shipped in #3604. Panel UX changes (role grouping, "show all versions" disclosure) explicitly defer until parallel-version badges land in production with real buyer feedback to design against. Co-authored-by: Claude Opus 4.7 (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.
Summary
Final stage of #3524. brand.json enrichment and the public
/verificationendpoint now carry per-version badge detail.What ships
brand.json
aao_verificationblock```jsonc
"aao_verification": {
"verified": true,
"verified_at": "2026-04-30T...",
"badges": [
{ "role": "media-buy", "adcp_version": "3.1", "verification_modes": ["spec", "live"], "verified_at": "..." },
{ "role": "media-buy", "adcp_version": "3.0", "verification_modes": ["spec"], "verified_at": "..." }
],
"roles": ["media-buy"],
"modes_by_role": { "media-buy": ["spec", "live"] }
}
```
`badges[]` is the canonical forward-compat shape (Q6 of the resolved decisions). One entry per parallel-version badge; preserved order matches the API's version-DESC sort. Adding future axes won't change the array shape.
`roles[]` and `modes_by_role` are kept as deprecated aliases for one release. Values reflect "the current best mark" — highest-version badge per role. Clients reading them today keep working when parallel-version badges ship. Removal target: AdCP 4.0.
/verification endpoint
`GET /api/registry/agents/{url}/verification` (the decentralized public verifier surface) now includes `adcp_version` on each `badges[]` entry. Validated through the same shape regex the `/compliance` endpoint uses for defense in depth — a poisoned DB row returns `null` rather than passing through unchecked.
Live verification
Tested locally with seeded parallel-version badges:
```bash
$ curl /api/registry/agents/https%3A%2F%2Fbuyer.acme-adtech.dev/verification | jq .badges
[
{ "role": "media-buy", "adcp_version": "3.1", "verification_modes": ["spec","live"], ... },
{ "role": "media-buy", "adcp_version": "3.0", "verification_modes": ["spec"], ... }
]
```
What this PR does NOT change
Stage tracker (closing #3524)
After this PR merges, #3524 is fully shipped. Deferred panel polish (role grouping, "show all versions" disclosure when 4+ badges, PROTOCOL_LABELS audit) tracked in #3603.
Test plan
/verificationendpoint returnsadcp_versionon each badge with proper shape validationCloses #3524.
🤖 Generated with Claude Code