AdCP / Advertising Context Protocol.
A practical guide to the open protocol layer for agentic advertising workflows: discovery, planning, buying, creative, signals, governance, accounts, and measurement.
AdCP gives advertising agents a shared task and schema layer. It helps buyers, sellers, platforms, data providers, and creative systems communicate through standardized advertising actions rather than bespoke dashboard workflows and one-off integrations.
AdCP is not a replacement for OpenRTB, clean rooms, or human judgment. It is a protocol layer for making advertising work discoverable, executable, governable, and auditable by agents.
- AdCP 3.2 GA · v3.2.8 · Oct 2026
- AdCP 3.1 maintained · v3.1.30 · Oct 2026
- AdCP 3.0 maintained · v3.0.28 · Oct 2026
- v2 deprecated Aug 1, 2026
- Sponsored Intelligence experimental
Part of AdCP on No Fluff Advisory — the spec explainer
- What it is
- An open agentic advertising protocol that defines domain-specific tasks and schemas for advertising agents.
- What it does
- Helps agents discover inventory, set up accounts, buy media, manage creative, activate signals, apply governance, and report results through standardized calls.
- What it is not
- Not a media exchange, identity graph, DSP, SSP, clean room, or replacement for OpenRTB.
- Why it matters
- Agentic workflows need repeatable, machine-readable actions. Without a shared protocol, every agent has to learn every platform separately.
- Current state
-
AdCP 3.2 is the general-availability line, wire pin "3.2".
Full current state · 2676 words
AdCP 3.2 is the general-availability line, wire pin "3.2": GA release v3.2.1 (the docs date it September 30, 2026; the GitHub release was published October 1 at 05:54 UTC), patched to v3.2.2 on October 4 (17:19 UTC; two documentation changes — account references before provisioning, and the trust-and-verification pages aligned with the spec) and to v3.2.3 on October 7 at 20:38 UTC, security errata for advisory GHSA-2pm6-6mc8-8xcm (adcp#7820; medium severity, published 11:45 UTC on October 8). Over A2A,
request_signing.required_forwas not enforced, so a seller that required signatures accepted unsignedcreate_media_buyand other spend-committing calls; 3.2.3 matchesrequired_for,supported_forandwarn_foragainst the AdCP operation resolved from the request (the tool name over MCP, theskillof the sole DataPart in an A2A message), fails closed when a request does not resolve to exactly one operation, and also carries storyboard fixes. v3.2.4 followed at 09:29 UTC on October 12, a patch of conformance and schema fixes: new request-signing vectors (negatives 029 to 031, positive guards 013 to 015, and profile-3.2 mirrors of every remaining root vector but 018, for verifiers that require content-digest coverage, which every 3.2 verifier does), negative 026 now expectingrequest_target_uri_malformed, four URL-canonicalization vectors, five storyboard fixes, arequires: [controller]gate on storyboards that seed fixtures through the test controller, aREVISION_UNREADABLEissue code on the experimental Reliable Reporting surface and bundled schemas about a fifth smaller. v3.2.5 followed at 19:05 UTC on October 16, two compliance-storyboard fixes with no schema or wire change: the canonical-formats storyboard’s direct selector now sends every parameter the product constrains, andinline_creatives_without_syncgrades each seller on the creative representation it publishes, with complete seeded products for the three provenance scenarios. v3.2.6 followed at 04:39 UTC on October 23, compliance-storyboard fixes and schema annotations with no field, type or requirement change: the fixes for three 3.2 storyboard filings from this site (#8251 to #8253: stale prerelease pins, a refine phase run without abuying_modesgate, and optional-tool steps with no gate that implies the tool), a real public conformance verifier and served fixture images for the provenance storyboards (#8218), the signed 3.1.29 records, other storyboard corrections and advisory AdCP 4.0 compatibility checks that fail no 3.x run. v3.2.7 followed at 19:47 UTC on October 24, one compliance change: both off-list evaluator rejection phases are now mandatory for agents that advertise evaluator support. v3.2.8 followed at 20:56 UTC on October 28: shared media-buy lifecycle and measurement-term rejection checks that auction-only sellers can now run without fixed-price inventory, two storyboard scoping fixes, and the 3.2 proposal-flow schemas that had been closed against DR-0019 reopened at 24 locations, a correction recorded in DR-0036 under which every valid payload stays valid and no field is added. The first 3.3 prerelease, v3.3.0-beta.0, was tagged at 12:36 UTC on October 22, and v3.3.0-beta.1 at 05:39 UTC on October 29. 3.2.0 was never released — an accidental cut on June 30 committed 3.2.0 from 3.1-era main, #5769 reverted it within hours, and its signed artifacts stay on the CDN under immutable caching, so the number is withdrawn and the first stable 3.2 is 3.2.1. Since the September 30 flip the docs call 3.1 a maintained line (security and critical fixes on 3.1.x for a maintainer-decided window) and 3.0 Maintained too (compatibility and security patches for existing integrations); both took a patch on October 1 at 14:15 UTC — v3.1.25 (five media-buy compliance storyboards seed non-guaranteed fixture products, backported from #7770 and #7756) and v3.0.27 (a default governance_agent_url in the 3.0 governance storyboards) — and more since: v3.0.28 (October 7, 14:13 UTC; a 3.0 storyboard fix that keeps deterministic media-buy phases independent of the Sponsored Intelligence session phase), v3.1.26 (October 7, 17:30 UTC; the creative evaluator-auth storyboard no longer fails every agent that declares creative.evaluator, and the four 3.1 media-buy governance scenarios take the hosted grader’s buyer brand from #7803), v3.1.27 (October 8, 11:48 UTC; macro-bearing URL validation backported from 3.2, so VAST, DAAST, URL and tracker assets accept ad-server tokens such as %%CACHEBUSTER%% without pre-encoding their delimiters; #8004, fixing #7993), v3.1.28 (October 16, 16:02 UTC; storyboard fixes, among them the inline-creative regrade that 3.2.5 also carries, schema-valid seeded products and a format for the three provenance scenarios (which makesseed_creative_formata controller scenario they require) and operation-valid bodies for all 37 routed request-signing vectors), v3.1.29 (October 16, 18:03 UTC; two request-signingSignaturetokens re-encoded as unpadded base64url for the 3.1 profile, with nothing re-signed) and v3.1.30 (October 28, 16:34 UTC; the public provenance conformance verifier and served fixture images backported from #8218, the auction-only seller checks that 3.2.8 also carries, and an erratum that projects the HTML-markup legacy formats todisplay_tag). Neither these nor v3.2.2 to v3.2.8 are in the versions reference on main or on the published docs site as of 22:30 UTC on October 29 (both name 3.2.1, 3.1.24 and 3.0.26). 3.2 is additive over 3.1 apart from a recorded exception list; the two with the widest impact are that body-levelstatusis gone fromcreate_media_buyandupdate_media_buysuccess (readmedia_buy_status), and that a 3.2 signing endpoint requirescontent-digestcoverage on every signed body and RFC 8941 padded Base64, with the profile selected by the endpoint rather than the request pin, so a 3.1-profile signer is rejected by a shared endpoint that advertises 3.2 even on a "3.1" pin. The docs say@adcp/sdk14.0.0 andadcp8.0.0 ship alongside 3.2.1, and both shipped on October 1: 14.0.0 reached npm at 12:39 UTC and 8.0.0 reached PyPI at 18:12 UTC. Since then npmlatesthas moved through 14.1.0 (October 4), 14.2.0 (October 5), 14.3.0 and 14.4.0 (both October 6) and on to 15.0.0 at 00:27 UTC on October 7, a new SDK major whose breaking change is OAuth issuer binding, not the protocol, and 15.2.0 at 16:35 UTC that day, the SDK-side fix for the same A2A signing gap; every one of these declares 3.2.1 and bundles the 3.0.25, 3.1.24 and 3.2.1 compliance lines, andadcp-3.2pointed at 14.4.0, a build the advisory lists as affected, as late as October 24 and points at 16.5.0 (declares 3.2.7) as of 22:30 UTC on October 29. 15.2.1 (02:01 UTC on October 8) was the first build to declare 3.2.3 and bundle the 3.2.3 line, and every build after it through 15.8.1 declares 3.2.3 too:latestmoved through 15.3.0 and 15.4.0 on October 8 and 15.5.0 and 15.6.0 on October 9 to 15.7.0 at 23:59 UTC that day, 15.7.1 (October 10) and 15.8.0 and 15.8.1 (October 11) followed, and 15.2.2 sits under a newsdk-15.2tag; 15.9.0 (02:41 UTC on October 13) was the first build to declare 3.2.4 and bundle the 3.2.4 line; 15.9.1 (11:53 UTC on October 14), 15.9.2 (20:04 UTC on October 15) and 15.9.3 (15:12 UTC on October 16) declare 3.2.4 and bundle the 3.0.25, 3.1.24 and 3.2.4 lines; 15.9.4 (23:54 UTC on October 16) declares 3.2.5 and bundles the 3.0.25, 3.1.29 and 3.2.5 lines, and every build after it through 16.4.0 declares 3.2.5 too: 15.9.5 (October 17), 15.9.6 (October 19), 16.0.0 and 16.1.0 (October 20), 16.2.0 and 16.3.0 (October 22) and 16.4.0 (01:21 UTC on October 24), as does 15.9.7 (October 28); 16.5.0 (15:20 UTC on October 25) and 16.6.0 (October 27) declare 3.2.7, and 16.6.1 (00:24 UTC on October 29;latestas of 22:30 UTC on October 29) and 15.9.8 (October 28, under a newsdk-15.9tag) declare 3.2.8, while the 17.0.0 betas from 17.0.0-beta.0 (October 25) declare the 3.3 betas, with 17.0.0-beta.6 (21:15 UTC on October 29; declares 3.3.0-beta.1) under thebetatag;rcis on 14.0.0 andadcp-3.1on 13.1.4 (October 28; declares 3.1.30) as of 22:30 UTC on October 29. The PyPI default moved to 8.1.0 on October 6, to 8.1.1 at 11:33 UTC on October 8, which adopts the signed 3.2.3 bundle, to 8.1.3 at 14:10 UTC on October 15, which adopts the signed 3.2.4 bundle (8.1.2 has a GitHub release but was never uploaded to PyPI), to 9.0.0, a new SDK major still on 3.2.4, at 13:56 UTC on October 24, and on through 9.1.0, 9.2.0 and 9.3.0 (11:17 UTC on October 27, the first to embed 3.2.7) to 9.3.1 at 13:41 UTC on October 29, beside the prereleases 10.0.0b0 to 10.0.0b2 (October 28 and 29) on the 3.3 betas, all as of 22:30 UTC on October 29. The newest tagged Go module, adcp/v3.2.1, still embeds rc.1, though adcp-go main took the final 3.2.2 schema bundle at 10:24 UTC on October 7, not in any tagged module and not moved on to 3.2.3 or any later 3.2 bundle, and Java has no released SDK, as of 22:30 UTC on October 29; the docs list Go and Java support for final 3.2 as planned follow-ups. Hosted grading moved to 3.2.1 with #7841 (merged 14:58 UTC on October 1: an agent that advertises 3.2 is graded on the 3.2 bundle, a 3.1-only agent stayed on 3.1.24, and the badge lines are now 3.2, 3.1 and 3.0) and its runner to the stable 14.0.0 from 17:00 UTC, then to 14.2.0 from 16:20 UTC on October 6, to 15.0.0 from 10:39 UTC on October 9 (a heartbeat on Triton Digital), after #7968 (merged 09:04 UTC that day) moved the registry to SDK 15.0.0, to 15.2.0 from 16:32 UTC that day, after #8012 (merged 12:23 UTC) moved it to SDK 15.2.0, to 15.8.1 from 10:36 UTC on October 12, after #8148 (merged 09:38 UTC that day) moved it to SDK 15.8.1, to 15.9.2 from 10:07 UTC on October 16, after #8231 (merged 09:00 UTC that day) moved it to SDK 15.9.2, to 15.9.5 from 23:03 UTC on October 17, after #8284 (merged 19:03 UTC that day), to 16.1.0 from 12:06 UTC on October 21, after #8355 (merged 09:21 UTC), to 16.2.0 from 03:12 UTC on October 22, after #8393 (merged 02:22 UTC), to 16.3.0 from 19:38 UTC that day, after #7925 (merged 18:11 UTC), a schema change that also moved the pin to 16.3.0, to 17.0.0-beta.0, a prerelease SDK line that carries the 3.3 beta bundle, from 03:28 UTC on October 26 (a heartbeat on this site’s signals agent), after #8489 (merged 01:54 UTC that day), to 17.0.0-beta.2 from 16:22 UTC on October 27, after #8528 (merged 15:19 UTC), and to 17.0.0-beta.3 from 17:36 UTC on October 28, after #8550 (merged 15:11 UTC), a compliance fix that also moved the pin. #8242 (merged 22:45 UTC on October 16) moved hosted 3.1 grading from 3.1.24 to 3.1.27, and the first public 3.1.27 run came at 23:53 UTC that day; #8262 (merged 23:20 UTC) registered 3.0.28 for 3.0 grading; #8278 (merged 16:47 UTC on October 17) moved hosted 3.1 grading to 3.1.29 and 3.2 grading to 3.2.5, whose first public runs came at 17:47 UTC that day and 01:29 UTC on October 18; #8429 (merged 07:52 UTC on October 23), which brought the v3.2.6 release records into main, moved 3.2 grading to 3.2.6, whose first public run, a heartbeat on this site’s signals agent, came at 15:01 UTC that day; and #8464 (merged 22:43 UTC on October 24), with the v3.2.7 records, moved it to 3.2.7, whose first public run, again a heartbeat on this site’s signals agent, came at 03:17 UTC on October 25. #8561 (merged 20:43 UTC on October 28) moved hosted 3.1 grading to 3.1.30, whose first public run came at 23:05 UTC that day, and #8566 (merged 23:54 UTC) moved 3.2 grading to 3.2.8, whose first came at 01:01 UTC on October 29. As of 22:30 UTC on October 29 agents that advertise 3.2 are graded on 3.2.8 and 3.1-only agents on 3.1.30. The first public run on a 3.2 bundle came at 21:48 UTC on October 1, the day #7841 merged — one registered agent, gotom.io's dev-demo endpoint, on 3.2.1, then on 3.2.2 from October 5, on 3.2.3 from 17:57 UTC on October 8 and on 3.2.4 from 19:50 UTC on October 13, the day after each of those tags, the hosted grader loading its bundles from the adcp repo, not from the runner package (whose 15.8.1 build bundles 3.2.3), and #8153, merged 12:41 UTC on October 13, registering 3.2.4 there; this site’s signals agent was graded on 3.2.4 from 13:58 UTC on October 16, on 3.2.5 from 01:57 UTC on October 18, on 3.2.6 from 15:01 UTC on October 23, on 3.2.7 from 03:17 UTC on October 25 and on 3.2.8 from 03:48 UTC on October 29, and the gotom.io agent on 3.2.5 from October 19, on 3.2.6 from October 23, on 3.2.7 from October 25 and on 3.2.8 from October 29; Publink’s agent, which has joined the public listing since October 17, has public 3.2 runs from 10:41 UTC on October 4, briefable’s idm demo agent, which has joined it since October 24, from October 13, and BidMachine’s seller agent and the seven agents behind Optimera’s Prebid seller gateway from October 28 and 29 — and between October 2 and 22:30 UTC on October 29 the registry returns 1,318 public runs (a floor: its history endpoint returns at most 100 runs per agent, which now leaves out the first day or so of the window for three agents; of the runs, 52 are on Triton Digital and 82 on TeqBlaze’s adcp.teqblaze.io, whose run histories are public at their own URLs though the public listing has dropped both, the second since October 17; the daily public count fell from 58 on October 3 to 27 on October 11, then rose to 35 on October 12, 44 on October 13 and 55 on October 14, and has been between 45 and 55 a day from October 15 to 28; by the maintainer’s database counts on #7355 the public history undercounts the heartbeats the grader runs): 592 on 3.1.24, 33 on 3.1.27, 502 on 3.1.29, 24 on 3.1.30, 157 on the 3.2 bundles (six on 3.2.1, nine on 3.2.2, 15 on 3.2.3, 21 on 3.2.4, 37 on 3.2.5, 12 on 3.2.6, 32 on 3.2.7 and 25 on 3.2.8: 48 from Publink’s agent, 35 from briefable’s, 27 from this site’s signals agent, 25 from the gotom.io agent, 20 from the seven Optimera gateway agents and two from BidMachine’s), 10 on 3.0.25, the last all on October 2 and 3 on the two TeqBlaze agents, which the grader has run on the 3.1 line since (the registry does not publish what an agent advertises on the wire). As of 22:30 UTC on October 29 the sales agent is on "3.1": it advertises only "3.1" and answers a "3.2" pin with 3.1, echoing "3.1", while refusing "3.0", "4.0", a bare "3" and patch or prerelease pins outside 3.1. The signals agent has served "3.2" by default since 13:18 UTC on October 16 (adcp-signals-adaptor#348) and advertises "3.0", "3.1" and "3.2" as of 22:30 UTC on October 29. Its 3.2 support was built behind a switch (adcp-signals-adaptor#345, merged October 15) and held back while the grader ran 15.8.1, which cannot build three of the 3.2.4signed_requestsnegatives (029 to 031) and so fails them for any agent graded on 3.2.4 that supports request signing; #8221, filed from here at 15:10 UTC on October 15, asked for 15.9.x; the switch went on once #8231 had moved the grader to 15.9.2, and #8221 was closed from here at 19:08 UTC on October 16 as resolved by #8231. An owner test at 13:58 UTC on October 16 graded the agent on the 3.2.4 bundle, passing, and the registry issued it a 3.2 signals badge the same minute (details in the implementer-seat section below). Claiming 3.2 would mean advertising and echoing "3.2", status-free create and update success bodies, error.recovery on every error and the tightened request rules; the compact tools stay optional, the signing profile binds only agents that support request signing, which since October 10 includes the signals agent (it verifies signed requests, and since it switched to 3.2 it advertisescovers_content_digest"required", up from "either" on 3.1, and verifies every signed request under the 3.2 profile whatever version the request pins) but not the sales agent, and since #7841 a 3.2 badge is open to any agent that advertises 3.2 and passes on the 3.2 bundle. - Best next step
- Understand the domains, then decide which workflow your company should expose or consume first.
What AdCP is.
AdCP is a shared advertising task layer for AI agents. It defines what agents can ask for, what systems should return, and how actions move through the advertising workflow.
| Layer | Job | Example |
|---|---|---|
| Transport | Carries messages | MCP / A2A |
| AdCP | Defines advertising tasks and schemas | get_products, create_media_buy, get_signals, activate_signal |
| Execution system | Performs the action | publisher, SSP, DSP, creative system, data provider |
| Governance | Controls authority and audit | approvals, signatures, idempotency, audit logs |
| Existing ad standards | Power underlying objects and transactions | OpenRTB, AdCOM, OpenDirect, Deals API |
Why AdCP exists.
Fragmented workflows
Media buying, creative, data activation, accounts, and reporting still live across disconnected systems and dashboards.
Agents need actions, not screens
An agent cannot scale by clicking through every platform UI. It needs task-level interfaces.
Common schemas reduce ambiguity
Natural-language briefs need to resolve into structured, auditable requests.
Human oversight needs architecture
Agentic buying requires approvals, limits, audit logs, and accountability built into the flow.
Platform participation needs standards
Publishers, data providers, creative systems, and ad platforms need a shared way to expose what agents are allowed to do.
What’s new in 3.2 — and what claiming it asks of a 3.1 adopter.
AdCP 3.2 is the current general-availability line (GA release v3.2.1, patched to v3.2.2 on October 4, to v3.2.3, security errata for request signing over A2A, on October 7, to v3.2.4, conformance fixes, on October 12, to v3.2.5, compliance-storyboard fixes, on October 16, to v3.2.6, more compliance-storyboard fixes, on October 23, to v3.2.7, one more compliance fix, on October 24 and to v3.2.8, compliance fixes and reopened proposal-flow schemas, on October 28; wire pin "3.2"; 3.2.0 was never released), additive over 3.1 apart from a recorded exception list. 3.1 is a maintained line at v3.1.30 (October 28): a seller that stays on "3.1" keeps serving 3.2 buyers, and the docs say keep the pin as long as you need. Separate what shipped from the obligations claiming 3.2 brings, from what is experimental, and from what Go and Java had not delivered as of 22:30 UTC on October 29.
Confirmed — AdCP 3.2 (GA · v3.2.8) · 6
- AdCP 3.2 went GA as v3.2.1 — the docs date it September 30, 2026, the GitHub release was published October 1 at 05:54 UTC, and the wire pin is "3.2". 3.2.0 is a withdrawn number: an accidental Version Packages cut on June 30 (09:13 UTC) committed 3.2.0 from 3.1-era main, #5769 reverted it about four hours later (merged 13:37 UTC) and the tag and GitHub release were deleted, but its signed schema, compliance and protocol artifacts remain on the artifact CDN under immutable caching, so GA ships as 3.2.1 and anything at a 3.2.0 URL is 3.1-era content not to be used. The versions reference, the 3.1→3.2 migration guide and the What's New page on main flipped to GA at 16:24 UTC on September 30 (#7775), and as of 22:30 UTC on October 29 the published docs site, which serves the v3.2.8 snapshot (its versions page resolves under /dist/docs/3.2.8/ since #8569 merged at 00:27 UTC that day; the v3.2.7 snapshot PR, #8460, is open, as are #8241 and #8248 for v3.1.28 and v3.1.29, and its version switcher also offers 3.3.0-beta.1, 3.1.30 and 3.0.28 snapshots), shows the same stable rows as main: Active 3.2 (3.2.1), Maintained 3.1 (3.1.24) and 3.0 (3.0.26), 4.0 planned for early 2027 (main has also listed 3.3 as a pending beta since October 6, #7952, and still does after v3.3.0-beta.0 and v3.3.0-beta.1 were tagged on October 22 and 29; the published versions table does not show that row, and the 3.3 preview group that the 3.2.3 snapshot, #7999, merged 08:48 UTC on October 8, added to the published navigation (What’s new in 3.3, the 3.3 beta program and the 3.2→3.3 migration guide) is gone from the 3.2.8 snapshot, which leaves 3.3 to its own beta snapshot) — the version tables are fifteen tags behind GitHub, where v3.2.2 (October 4, a Version Packages cut with two documentation patches; its docs snapshot landed the same evening, #7920, without touching the version tables), the v3.2.3 security errata (October 7), the v3.2.4 to v3.2.8 patches (October 12 to 28; conformance fixes and, in v3.2.8, reopened proposal-flow schemas) and the maintenance patches v3.1.25 to v3.1.30 and v3.0.27 to v3.0.28 (October 1 to 28) are already published.
- What 3.2 is for, in the docs' own framing: agents run media with the full power of the platform (seller-optimized shared budgets with a capability per control, hard daily budget caps, outcome targets with an optional cost-per target, and availability-horizon planning), a MediaBuy-level frequency cap counted across packages, Reliable Reporting as a discoverable contract (experimental), data collaboration, where products declare how audience data reaches them, clean rooms included (experimental, media_buy.audience_activation), a compact seven-tool lifecycle — list_products, request_proposals, refine_proposals, decline_proposals, buy_products, accept_proposal, control_media_buy — with the subset a seller serves advertised through media_buy.lifecycle_tools while get_products, create_media_buy and update_media_buy stay as 3.x facades, and runtime hardening: error.recovery on every 3.2 error, integer retry_after, 28 new standard error codes, identity never derived from Mcp-Session-Id, and idempotency ledgers that outlive the resources they create (COMMITTED_RESOURCE_PURGED). By the docs' numbers, 3.1.0 → 3.2.1: manifested tools 64 → 78, specialisms 21 → 31, standard error codes 92 → 120, source JSON Schema files 648 → 1,017, compliance YAML files 137 → 237.
Show all 6
- Additive over 3.1 apart from a recorded exception list: the ratified removal of body-level status from create_media_buy and update_media_buy success (DR-0011; read media_buy_status), the request-signing profile correction (3.2 endpoints that support signing require content-digest coverage on every signed body and RFC 8941 padded Base64; the endpoint selects the profile, so a 3.1-profile signer is rejected by a shared endpoint that advertises 3.2 even when the request pins "3.1"), eight tightened request rules (among them: create_media_buy takes exactly one of explicit packages or proposal_id plus total_budget; total_budget.currency must be uppercase ISO; sync_governance registers Bearer as the only authentication scheme; preview_creative takes creative_manifest or creative_id, not both) and three producer-side tightenings. The same section clarifies that delivery dates are calendar dates in the products' reporting timezone, echoed in reporting_period.timezone, and that a dated request spanning two reporting timezones is rejected with VALIDATION_ERROR (a "3.1" pin may keep 3.1 behaviour). A structural diff of the 3.1.24 and 3.2 schemas found no other stable-surface change that rejects a 3.1-valid payload (widened response shapes that a strict 3.1 parser may reject are listed separately in the migration guide, #7718); experimental surfaces changed under their own notice contract and are not exceptions.
- The SDKs caught up on October 1. The versions reference, the migration guide and the What's New page all say @adcp/sdk 14.0.0 (the What's New page and the migration guide add: npm latest) and adcp 8.0.0 ship alongside 3.2.1 and embed it. 14.0.0 was published at 12:39 UTC on October 1 (npm latest, rc and adcp-3.2 all pointed at it that day; its package.json declares adcp_version 3.2.1) and 8.0.0 at 18:12 UTC (the PyPI default until 8.1.0 on October 6), after a morning on which the newest builds were still 14.0.0-rc.53 and 8.0.0rc3 on the 3.2.0-rc.7 bundle; adcp-3.1 is on 13.1.4 (October 28; declares 3.1.30) as of 22:30 UTC on October 29, and npm latest moved through 14.1.0 (October 4), 14.2.0 (October 5), 14.3.0 and 14.4.0 (both October 6), then to 15.0.0 at 00:27 UTC on October 7, a new SDK major for an OAuth issuer-binding change, and 15.2.0 that afternoon, the SDK-side fix for the A2A signing advisory, while adcp-3.2 stayed on 14.4.0 until at least October 24; every one of these declares 3.2.1 and bundles the 3.0.25, 3.1.24 and 3.2.1 lines; none of these builds bundles 3.2.2. 15.2.1 (02:01 UTC on October 8) moved the pinned bundle to 3.2.3, and latest moved on through 15.3.0 and 15.4.0 (October 8) and 15.5.0 and 15.6.0 (October 9) to 15.7.0 (23:59 UTC on October 9), all declaring 3.2.3, as do 15.7.1 (October 10) and 15.8.0 and 15.8.1 (October 11), with 15.2.2 under a new sdk-15.2 tag; 15.9.0 (02:41 UTC on October 13) moved the pinned bundle to 3.2.4, and 15.9.1 (October 14), 15.9.2 (October 15) and 15.9.3 (October 16) keep it; 15.9.4 (23:54 UTC on October 16) moved it to 3.2.5 and the bundled 3.1 line to 3.1.29, and every build after it through 16.4.0 declares 3.2.5 too: 15.9.5 and 15.9.6 (October 17 and 19), 16.0.0 and 16.1.0 (October 20), 16.2.0 and 16.3.0 (October 22) and 16.4.0 (October 24), as does 15.9.7 (October 28); 16.5.0 (October 25; adcp-3.2 as of 22:30 UTC on October 29) and 16.6.0 (October 27) declare 3.2.7, 16.6.1 (October 29; latest as of 22:30 UTC on October 29) and 15.9.8 (October 28, under a new sdk-15.9 tag) declare 3.2.8, and the 17.0.0 betas declare the 3.3 betas, with 17.0.0-beta.6 (October 29; 3.3.0-beta.1) under the beta tag. On PyPI the default moved to 8.1.0 on October 6, a backport of provisioning-aware accounts and signing fixes, to 8.1.1 on October 8, which adopts the signed 3.2.3 bundle, to 8.1.3 on October 15, which adopts the signed 3.2.4 bundle (8.1.2 never reached PyPI), to 9.0.0, a new major still on 3.2.4, on October 24 and on through 9.1.0, 9.2.0 and 9.3.0 (October 27, the first to embed 3.2.7) to 9.3.1 on October 29; 9.0.0b1 to 9.0.0b4 prereleases came out on October 6, 8, 10 and 18, and 10.0.0b0 to 10.0.0b2, on the 3.3 betas, on October 28 and 29. The newest Go module, adcp/v3.2.1 (September 5), embeds 3.2.0-rc.1 and defaults to the "3.1" pin — the guide says it is not AdCP 3.2.1 — and Go and Java support for final 3.2 are planned follow-ups; adcp-go main took the final 3.2.2 schema bundle at 10:24 UTC on October 7, not in any tagged module and not moved on to 3.2.3 or any later 3.2 bundle, as of 22:30 UTC on October 29.
- AdCP 3.1 is a maintained line since the September 30 flip, 3.0 the other: GA at v3.1.0 (June 18, 2026); v3.1.3 (July 13) withdrawn four days later for adding a stable schema field in a patch and an incorrect publisher-domain filter; patched through v3.1.24 (September 23, 2026), v3.1.25 (October 1; five media-buy compliance storyboards seed non-guaranteed fixture products so sales-non-guaranteed sellers no longer fail create_media_buy, backported from #7770 and #7756), v3.1.26 (October 7; the creative evaluator-auth storyboard no longer fails every agent that declares creative.evaluator, and the four media-buy governance scenarios take the hosted grader’s buyer brand from #7803), v3.1.27 (October 8; macro-bearing URL validation backported from 3.2, #8004 for #7993), v3.1.28 (October 16; inline-creative storyboards graded on the representation each seller publishes, schema-valid seeded products for the provenance scenarios and operation-valid bodies for the routed request-signing vectors), v3.1.29 (October 16; two request-signing Signature tokens re-encoded for the 3.1 profile) and v3.1.30 (October 28; the provenance conformance verifier backported from #8218, auction-only seller checks and an erratum for the legacy HTML-markup formats), none of these six in the versions reference as of 22:30 UTC on October 29; 3.1.24 is eight conformance-suite changes — the governance scenarios gated on media_buy.governance_aware (#7625) and routed with the synchronized plan_id (#7555, resolving #7547, filed from here), the universal lifecycle and non-governance specialisms kept ungoverned on isolated account keys (#7627, from #7585, also filed from here) and the sales baselines likewise (#7665 resolving #7628), provenance tool prerequisites (#7586), agent_url canonicalization (#7367, partial), format_kind in the creative-generative storyboard, and VERSION_UNSUPPORTED recovery fatal → correctable (#7376 backport). Per the versions reference it receives security and critical fixes on 3.1.x for a maintainer-decided window (the versioning page still says older 3.x releases get no routine backports); the versions FAQ gives 3.0 an explicit floor of at least 12 months after 4.0 GA, the previous-major policy states that floor for the 3.x generation as security patches only, and nothing names a 3.1-specific window. Existing "3.1" sellers keep working: a 3.2 buyer downshifts to an advertised 3.1 release and the 3.x facades, and the docs say keep the pin as long as you need; the guide's buyer-side exceptions are signed requests to a seller that has added 3.2 on the same endpoint, and strict parsers on 3.2 shapes.
- Feature detail here is sourced from the official What's New in 3.2, the 3.1→3.2 migration guide and the versioning reference on the adcp main branch, plus the repo CHANGELOG — the GitHub release body is the string "AdCP 3.2.1".
Obligations when you claim 3.2 · 6
- Advertise "3.2" in supported_versions only when you serve the 3.2 contract, keep advertising every earlier release you still serve, and echo the release actually served: the 3.2 grader makes a missing supported_versions or a missing echo a blocking failure, even though both fields stay schema-optional. A prerelease pin ("3.2-rc.N") never resolves to "3.2".
- Emit media_buy_status, and no body-level status, on 3.2 create and update success; keep the 3.0/3.1 handlers and the get_products / create_media_buy / update_media_buy facades. Emit error.recovery on every error and an integer retry_after.
- If you advertise request signing, advertise covers_content_digest: "required" and verify RFC 8941 padded Base64 on 3.2 endpoints. Decide how signed 3.1 callers keep working before you add "3.2" to a shared URL: AdCP has no capability field pointing from one endpoint to another (a second endpoint is published as its own agents[] entry), so the options are a separate endpoint or a coordinated cutover.
- Declare only the capabilities you implement — media_buy.lifecycle_tools, aggregate_frequency_capping, anonymous_discovery, seller_optimized_budget and its sub-capabilities, and specialisms — and reject an undeclared root frequency_cap, seller-optimized package control or outcome target (media_buy.outcome_target, the key the 3.2.1 storyboard gates on) with UNSUPPORTED_FEATURE rather than dropping it.
- Buyers: upgrade to a 3.2-capable SDK with the pin still at "3.1"; sign with the 3.2 profile for any endpoint that advertises 3.2, including requests you still pin to "3.1"; gate compact tools on lifecycle_tools and the root frequency_cap on aggregate_frequency_capping; send budget_allocation explicitly (omission still means fixed allocation); never infer 3.2 from a major-only declaration.
- Report delivery dates in the products' reporting timezone and echo reporting_period.timezone, report currency at media-buy or package grain, and stop producing new aggregated_totals. Discover creative through format_options[] and creative.supported_formats[]: list_creative_formats is compatibility-only and plural format_ids are deprecated for removal in 4.0.
Experimental and dated in 3.2 · 4
- Experimental in 3.2, each under its own notice contract, includes campaign governance (governance.campaign), Trusted Match (trusted_match.core), audience activation (media_buy.audience_activation), product identity (media_buy.product_identity), execution requirements (media_buy.execution_requirements), Reliable Reporting (media_buy.reporting_delivery, reliable_reporting_version "1.0"), principals (protocol.principal), the account change feed (account.change_feed, RFC #6810 open), measurement.core and measurement.gateway, the static OOH delivery block, and the seller_rendered_stateful_display and coordinated_placements formats. Two changed in 3.2 under that contract per the What's New page, governance and Trusted Match; the release notes also record changes to brand rights and audience activation, and the experimental-status registry lists more surfaces (brand rights, verified identity, sponsored intelligence, two creative surfaces).
- Dated: sync_reporting_status is opt-in in 3.2 and becomes required Core in the next eligible minor, no earlier than October 24, 2026. Since #7841 (merged 14:58 UTC on October 1) hosted grading grades an agent that advertises 3.2 on the 3.2 compliance bundle, and 3.2 badges are issued only from those runs, with a "3.2" advertisement and echo; prerelease runs stay diagnostic. Further stable 3.x minors come at least eight weeks apart; patches are not bound by that.
Show all 4
- Still open as of 22:30 UTC on October 29: no stable release of the spec states normatively that a seller serving "3.1" must emit 3.1-valid response shapes — the migration guide lists six 3.2 shapes that fail a strict 3.1 validator and the release notes name more (#7718; on October 12 the working group decided that a seller MUST shape a response to the negotiated release or stop advertising that release (milestone 3.3.0), recorded as DR-0024 by #8117, merged to main on October 20 and first released in the v3.3.0-beta.0 prerelease); post-acceptance commercials (invoicing, payment rails, FX, makegoods, disputes) are deferred to the 3.3 commercial-completion workstream (#7064, on the Spec Backlog milestone); per-account execution readiness, a 3.3 candidate (#7763, on the 3.3.0 milestone), reached main as an experimental surface when #7934, an opt-in execution_readiness map on get_products kept outside the cache layer, merged on October 27, and no stable release has it (its first release is the v3.3.0-beta.1 prerelease).
- How it got here: a beta cycle from August 17 (beta.0, the protocol-first input) to beta.11 on September 3, rc.0 the same day, seven published candidates to rc.7 on September 27 (rc.5 withheld), the docs flipped to GA on September 30 and the tag followed on October 1.
Watch next (the 3.3 beta, 4.0 and beyond) · 4
- 3.3 runs a short beta cycle. Since October 6 the versions reference on main lists 3.3 as a pending beta (#7952): beta.0 publishes the signed protocol bundle first, the SDKs then ingest it, and beta.1 converges protocol and SDKs, with exact prerelease wire pins such as "3.3-beta.0" for development and staging only. v3.3.0-beta.0 was tagged at 12:36 UTC on October 22 (#7897) and v3.3.0-beta.1 at 05:39 UTC on October 29 (#8419), and both are listed among the bundles approved for hosted grading; as of 22:30 UTC on October 29 no public run on a 3.3 bundle exists (the hosted grader has run on the 17.0.0 beta SDKs, which carry the 3.3 beta bundles, since October 26), the versions reference on main still lists 3.3 as a pending beta and the published docs site does not show the 3.3 row in its versions table; it serves 3.3 as a separate beta snapshot in its version switcher (3.3.0-beta.1, #8577, merged 07:26 UTC on October 29), and its 3.2.8 snapshot drops the 3.3 preview group that the stable navigation carried from the 3.2.3 snapshot (#7999, merged 08:48 UTC on October 8). Milestone assignment marks a candidate for the 3.3 line, not a commitment: the 3.3.0 milestone carries per-account execution readiness (#7763) and the seller-shape gap this site reported (#6303), which the working group approved on October 19 as a sales-fixed-rate specialism; #7963 added it to main on October 20 as a preview specialism, which earns no badge until it is marked stable, and #8445, the maintainer’s draft of October 24, would mark it stable for 3.3 GA (still a draft as of 22:30 UTC on October 29). The A2A signing question was settled for 3.2 by the 3.2.3 errata (#7820, closed October 8), whose hardening rules are SHOULD on 3.2.x and MUST from 3.3.
- The 4.0 deprecation set carries 3.2 additions, among them plural format_ids, the list_creative_formats pricing request fields and media-buy account field (the tool itself is compatibility-only and on its own schedule), cross-buy delivery aggregates, targeting-like product filters, bid_price and max_bid, and 3.1 terms_ref, with inline asset-access credentials to follow at 4.0 or later; alongside Format.pricing_options, legacy SignalId / flat signal targeting and data_provider_signals. Request signing becomes required for spend-committing operations in 4.0.
Show all 4
- Release policy: majors at least 18 months apart, 4.0 targeted for early 2027, a previous major receiving security patches for at least 12 months after its successor, and deprecations announced at least six months before removal.
- AdCP Verified independent auditing, signals-protocol maturity, registry adoption, SDKs, and reference implementations.
Migrating 3.1 → 3.2 — the checklist
A 3.1 seller keeps working untouched: 3.2 is additive apart from the recorded exceptions, and a 3.2 buyer downshifts to an advertised 3.1 release. The guide names two buyer-side exceptions, signed requests to an endpoint that adds 3.2 and strictly typed 3.1 parsers on 3.2 shapes. “Migrating” means claiming 3.2; each step below comes from the upgrade checklist in the 3.1→3.2 migration guide , where the hard requirements are exact advertisement and echo, media_buy_status, the 3.2 signing profile, error.recovery and integer retry_after, and the rollout order and reporting changes are its recommended practice. The one step to settle before you claim anything is signing: adding "3.2" to a shared endpoint changes the verifier for every signed caller on it.
- Upgrade the SDK, keep the pin — Move to a 3.2 SDK line with the wire pin still at "3.1", deploy and verify, then opt into "3.2" one peer at a time after each peer advertises it; existing "3.1" traffic keeps working.
- Advertise and echo exactly — Sellers advertise "3.2" in supported_versions only when they serve it, keep every earlier release they still serve, and echo the served release; buyers send "3.2" only to peers that advertise it and replace any "3.2-rc.N" pin, which never resolves to "3.2".
- Read media_buy_status — Create and update success carry media_buy_status and no body-level status; root status is the task-envelope state.
- Re-encode signatures — On any endpoint that advertises 3.2 and supports signing, every signed request with a body covers content-digest, and Signature and Content-Digest values are RFC 8941 padded Base64. The endpoint selects the profile, so a 3.1-profile signer is rejected there even on a "3.1" pin: separate the endpoint or coordinate the cutover first.
- Make errors self-describing — error.recovery on every error and an integer retry_after; buyers classify on recovery, fall back to the code registry when it is absent, and apply retry_after only to transient errors.
- Gate the new controls — Compact tools on media_buy.lifecycle_tools, the root frequency_cap on aggregate_frequency_capping, shared budgets on seller_optimized_budget and its sub-capabilities, outcome targets on media_buy.outcome_target (the key the 3.2.1 storyboard gates on); reject an undeclared frequency cap, package control or outcome target with UNSUPPORTED_FEATURE.
- Fix the shapes 3.2 rejects — create_media_buy takes packages, or proposal_id plus total_budget, not both and not neither; total_budget.currency is uppercase ISO; preview_creative takes a manifest or a creative_id; sync_governance registers Bearer as the only authentication scheme; a dated or windowed delivery request spanning more than one reporting timezone is rejected with VALIDATION_ERROR, and dates read as calendar dates in the echoed reporting timezone.
- Certify on the 3.2 bundle — Grade 3.2 claims against the 3.2 compliance bundle — 3.2.1 per the guide; the first agent graded on 3.2, gotom.io's, moved to 3.2.2 on October 5, to 3.2.3 on October 8, to 3.2.4 on October 13, to 3.2.5 on October 19, to 3.2.6 on October 23, to 3.2.7 on October 25 and to 3.2.8 on October 29, and this site's signals agent was graded on 3.2.4 from October 16, on 3.2.5 from October 18, on 3.2.6 from October 23, on 3.2.7 from October 25 and on 3.2.8 from October 29. The docs name @adcp/sdk 14.0.0 or adcp 8.0.0 for it, both published on October 1 (npm adcp-3.2 is 16.5.0, declaring 3.2.7, and latest 16.6.1, declaring 3.2.8, as of 22:30 UTC on October 29), and hosted grading has graded agents that advertise 3.2 on the 3.2 bundle since #7841 (merged 14:58 UTC on October 1); the first public 3.2 run landed at 21:48 UTC on October 1.
Migrating 3.0 → 3.1 — the checklist (still the floor for the 3.1 line)
A 3.0 integration keeps working untouched — 3.1 is additive at the wire. “Migrating” means claiming 3.1, and each step below is a real obligation (per the 3.0→3.1 migration guide ), not optional polish. Backward compatibility is “mostly”: 3.0 clients are not broken, but adoption plus the Go SDK upgrade impose real changes — plan it.
- Advertise versions — Return supported_versions[] from get_adcp_capabilities and echo the served version at the envelope root; make transports tolerate unknown envelope-root fields (this replaces prior reject-on-unknown validation).
- Sign brand verification — If you implement verify_brand_claim(s), return a signed (JWS) response bound to the task, resolved tenant, agent URL, and freshness window.
- Adopt canonical creative — Move creative metadata to the canonical format_kind set and discover build units through the new list_transformers catalog; format-attached pricing moves to the transformer.
- Fix typed fields — trademarks[].status must be a typed enum and trademarks[].countries ISO 3166-1 alpha-2 — former free-text values now error; add the now-required cache_scope on get_products and get_signals.
- Require idempotency — Send idempotency_key on every state-mutating request (sellers may reject missing keys there; reads keep it optional as of v3.1.21); treat IDEMPOTENCY_IN_FLIGHT as transient rather than minting a fresh key.
- Upgrade the SDK, then certify — The Go SDK (adcp-go) has breaking type changes — re-decode against the new shapes; it is not a drop-in. Then pass the 3.1 compliance storyboard bundle to earn the 3.1 badge.
Contributions to the protocol.
This page is written from inside the work: No Fluff Advisory builds and operates a live AdCP signals agent (listed in the AAO registry) with all-pass conformance runs across the AdCP 3.0, 3.1 and 3.2 GA suites up to the hosted 3.2.8 grade of 03:48 UTC on October 29 (75 of 75 scenarios on the tracks run; the next, at 15:51 UTC, read 74 of 75 on one aborted signing probe, as the request-signing entry below records), and co-leads the Signals & Measurement working group. For the live state of the registry itself — every registered agent, its actual tool catalog, and real request/response captures — see the AdCP Ecosystem Tracker. For the buy side of the same protocol run end to end — discovery, audience targeting, brief, media buy, creatives, the human gate, delivery reporting — with every call copy-pasteable against a live seller agent, see the AdCP Buyer’s Guide.
Merged into the protocol & tooling
- last_updated on signal-definition — schema field for verifiable signal-record freshness (#5248 → PR #5249, in the 3.1.0 line since 3.1.0-rc.7).
- Root-cause diagnostics for the conformance runner — a schema-invalid
get_adcp_capabilitiesresponse used to surface as N unrelated track failures with nothing linking them; reported it (#6254) and wrote the protocol-side contract, which now defines thecapabilities_response_schema_invalidnotice and an RFC 6901capability_pointerfield so one cause is reported once, ahead of the results it explains (PR #6256, merged August 8, 2026). - Runtime signal-quality attestations — our RFC for verifier-signed signal-quality evidence on check_governance (#5418) landed in the protocol's 3.2 line:
runtime_attestations[]consuming the shared attestation primitive (#4529), scoped to signal activation with GDPR/CCPA deactivation exempt, with response-side evaluations and binding digests (PR #6218, merged August 6, 2026). - Compliance-grader fix — all-pass single-protocol runs rendered “Degraded” by a rollup regression; root-caused to the introducing commit, filed (#5429, triaged P0) and fixed (PR #5444, merged).
- RFC 9421 webhook-signing default — the inventory (#4270) and draft PRs (#4273/#4275, closed in favor of the triage-generated PR the maintainer merged) that drove the 3.0.9 realignment away from the deprecated HMAC framing. The migration tracker (#4205) closed completed on August 13, 2026: reframed as an on-ramp problem rather than an adoption threshold, with the docs and signing guide leading with RFC 9421 and no separate DRI left to assign.
- A dense vector on the rung that means “don’t share the content” —
context_signals.embeddingis the one field on Trusted Match's classified-output tier that can be partially inverted back to a user's own words, and on anai_assistantproperty the “content environment” is one person's conversation turn. Filed against v3.1.21 with the inversion literature and two additive text edits (#7393); shipped in #7396, which took the stricter route — a flat prohibition rather than the demonstrate-on-request carve-out I proposed — and added policy-filtering rules onkeywordsandsummarythat the issue had not asked for. - Context Match over-caching — split out of the embedding issue at the triage's request, since it carried no privacy dependency: the recommended cache key was placement-only while responses vary with artifact, classified signals and geo, so a router could serve one session's decision against another's context (#7414). The collision was fixed in #7397, which replaced the key with
{provider_id, context_hash}, a SHA-256 over the forwarded request minus$schemaandrequest_id. That PR was not a response to #7414. It closes #7395, which was filed under the maintainer’s account from the same #7393 review. Both were opened on September 10, 2026, more than 18 hours before I filed #7414. A bot-triage note marked #7414 blocked on #7397 four minutes after I filed it, and the issue was closed as not planned on September 13 with no comment. Thecache_ttl: 0line did ship, by another route: the #7395 bot triage listed it, my September 10 review of #7397 flagged it as missing, and it was added on September 11. It went in as a hit-rate note (geo or per-session signals produce many distinct hashes), not the correctness SHOULD #7414 proposed, and the 3.1.x backport (#7425) carried the same sentence into v3.1.22. The docs release-notes page goes further. Its Version 3.1.22 section, added by #7451 and first tagged in v3.1.23 (September 14, 2026), says Context Match router operators “affected by unsafe response caching MUST bypass or disable that caching until the router implements both request-aware partitioning and a trusted namespace for every result-affecting security and provider-configuration context.” Main #7446 would add that namespace ascache_namespace, with October 25, 2026 as the earliest date its enforcement can publish. On September 22, 2026 it was held out of the 3.2 RC5 cut for that reason: its own notice rules out enforcement before October 25, and RC5 comes first. The maintainer named two paths from here — a later 3.x minor once the notice period has run, or reshaping it into a notice-only or additive change — and on September 23, 2026 it was routed to 3.3.0 — bringing its minor changeset into the 3.2 candidate would have held GA until the notice floor, against a now-frozen train. We reviewed it independently at the maintainer’s request; with read-only permission here, that review could not clear the required-approval gate, and the hold does not turn on it. - Radio & static-OOH measurement parity — scoped the two channels the delivery contract had no shape for (#6138, with Chris Williams), then filed the asks that came out of it: radio delivery-currency, meaning row-level
measurement_sourceplusnielsen_audionotation (#6139), and spot-level as-run for linear (#5682). Every ratified decision is merged —measurement_sourceon delivery rows (PR #6241),nielsen_audiowith notation-vs-provider language and the radio reconciliation guide (PR #6836, field-name correction PR #6856), reporting cadence extended to measured channels and then promoted to a normative MUST (PR #6240, PR #6860), and spot-level as-run reporting (PR #6574). Taxonomy graduation criteria split out to #6261 and closed when media channels went stable (PR #6542). Implementations were maintainer-authored. - Eligibility you can read off the API —
verifiedwas a bare boolean computed from badge rows, so a seller could not tell “declare specialisms” from “fix storyboards” from “membership tier” (#6374, split from #6363 because the bot triage there flagged it as a separate API-contract issue). Filed August 11, 2026 with our agent as the reproduction: zero failing storyboards,verified: false, and the real first-guard blocker — no declared specialisms, the #6303 taxonomy gap — surfaced by no public field. It sat ready-to-implement for six weeks and shipped on September 21, 2026 as PR #7639. It covers all three legs the filing named, with one design choice stricter than asked: the compliance response now carries aneligibilityblock with agent-level blockers (no_declared_specialisms) and per-role blockers (storyboards_failing,storyboards_partial,storyboards_untested), plus a versionedcriteria_version; the membership-tier leg is split into a separate owner-scopedeligibility_ownerthat returnseligible: nullto anyone else, since tier is commercial information. It is live, and on our own card it now reads exactly what the filing traced by hand through source:blockers: [no_declared_specialisms],roles: {}. What it cannot do is make the blocker removable. The specialism we would need to declare does not exist in any stable release — #6303, the gap for a publisher-direct, human-gated, fixed-price seller, has been on the 3.3.0 milestone since October 6, 2026; the working group approved asales-fixed-ratespecialism for it on October 19, #7963 added it tomainon October 20 as a preview specialism, which earns no badge, the maintainer’s draft #8445 (October 24) would mark it stable for 3.3 GA but is still a draft as of 22:30 UTC on October 29, and our support for it ships behind a switch that is off — so the card now states the reason correctly and the reason still stands.
Conformance suite, hardened
- Two holes in our own seller, found reviewing our own code — these are the most serious defects we have found in this seller, and both were open to anyone: this seller accepts any bearer token that starts with
demo-, with no sign-up. Review of PR #112 in this site’s private repository, the billing-finality fix described below, surfaced the first, open since the seed scenarios were added on August 10: the test controller’sseed_*scenarios wrote without a sandbox check, so a sandbox caller could overwrite the stored record of a product this seller sells, rewrite a live account or move a live buy from pending approval to active, past the human gate, which we reproduced over the wire against a local copy before the fix. #113 and #114 in the same repository fenced every seed to sandbox rows (merged 23:39 and 23:46 UTC, deployed together at 23:51 UTC on October 14, 2026). Review of #114 surfaced the second, outside the controller and open since launch in early August: the tools that change buys, creatives and accounts, and the two that list creatives and accounts, were not scoped to the caller, so anydemo-key could rewrite another buyer’s approved, serving creative image or click URL with no second review, pause or cancel any buy, overwrite any account and list every account and creative. #118 scopes accounts, creatives and buys to the principal that owns them and sends a changed image, click URL, format or alt text back to review (merged 11:09 UTC on October 15, deployed 11:11 UTC with migration 0029). No live buy was running when either fix shipped. Read-only checks of the production database on October 14 and 16 found no sign that anyone else used either hole, though the database cannot show which key paused or cancelled a buy (it records a cancellation only as by a buyer) or whether anyone used the listing. Every live buy ever placed with this seller was our own testing: 149 buys, all for the brands nofluffadvisory.com, test.example, example.com and acmeoutdoor.example, from our smoke harness, self-tests and conformance runs we started, the last created on September 17. None was pushed past the gate: a seed over an existing buy also rewrote its owner to the caller’s key, every live buy is owned by our smoke key or, if placed before buys were bound to a key, by none, and our smoke harness, the only code that uses that key, has never called the test controller. Seven buys from launch evening, August 4, read completed with no decision recorded because that evening’s code stored a buyer’s cancel as completed, until the canceled state shipped that night, and the only way past pending approval was a recorded decision; none of the seven has a delivery record. No product this seller sells was touched by a seed, and production held no live accounts. All 107 live creatives are approved; 23 show an update after their review, all at 10:51:46 UTC on August 5, when migration 0009 rebuilt the creatives table, gave every existing row its first update time and copied its image, click URL and alt text unchanged. The full 3.1.24 suite on@adcp/sdk15.8.1, run from here against production, read 228 passed, 4 failed and 101 skipped after the seed fences, as expected, and the same after #118 (three of the four failures occur only in the local runner; the fourth isprovenance_truth_of_claim). Two narrower gaps in the human gate that #118 listed were closed by #129 in the same repository (merged 21:59 UTC on October 16, 2026, deployed 22:01 UTC with migration 0030). On the 3.1 line the seller serves, a raise or a later end date applied at once to an approved live buy; such a change now sends the buy back to human review, and the buy serves nothing until it is approved again, keeping its confirmation time, so usage it has already delivered stays billable. And a creative was approved as an image URL whose image could change behind it; the seller now fetches the image at sync, the reviewer approves that stored copy and readers are served it from this seller’s own/img/path, so live creatives approved before 0030, which have no stored copy, stop serving until they are synced and approved again. #130 (merged 00:41 UTC on October 17, deployed 00:42 UTC) makes everydemo-key sandbox-only: a call that would create or change a live buy, creative, account or usage record is refused withPERMISSION_DENIEDand writes nothing, and buying live takes a key we issue. - Signals-only agents now skip non-applicable media-buy gating and gained dedicated error-handling / schema-validation storyboards (#2916, #3350, #4009 — fixed in the 3.1.0 suite; earlier #2535, #3999, #4065).
- An assertion no signals-only agent can satisfy —
webhook_emission's syntheticassert_contributionstep grades a branch set whose only contributors requireget_products; a signals-only agent's contributors correctly skip asmissing_tool, and the assertion then fails on the empty set as a[core]failure nothing on the agent side can clear. The storyboard's owngrading.pass_criteriasays those steps grade not-applicable, and a sibling storyboard already guards its assertion phase withskip_if(adcp-client#2862, filed with the runner's machine-readable failure record —expected: [synchronous_completion_handled], actual: []— as the reproduction). Fixed September 14, 2026 in #7504, which gated all three phases of the synchronous webhook-emission branch family onmedia_buy.buying_modescarryingwholesaleand added a regression asserting that a signals-only agent grades the whole branch familynot_applicable— the grading contract the filing argued from. Note where it landed: the runner'sassert_contributionbehaviour is preserved and the storyboard was gated instead, so the fix rides the spec repo rather than the SDK. - Reviewed with wire data — the signal-definition epistemic / provenance layer (#5017, opened under the maintainer account): backed a progressive-disclosure concern that another reviewer had already raised, with three things: wire sizes from our signals agent (88,590 bytes for 5 signals against the 64 KB probe ceiling, pretty-printed — an estimated 60 KB compact — and carrying our own
x_*extension blocks, most of each signal’s bytes by our posted breakdown, but none of #5017’s new fields), an estimate that those fields would add 3–5 KB per signal, and three ways to split discovery from lookup. Before the merge, the maintainer account added compact-by-default guidance and afieldsselector onget_signals. Our field-level schema notes followed about an hour after the merge; the follow-up (#5190, merged May 30, 2026) addressed them, not always as proposed. Neither the pre-merge commits nor #5190 cites our comments, though the maintainer account introduced the selector commit as a follow-up “based on the progressive-disclosure discussion”, which our comment was part of. - A shared reference primitive that stayed a draft — the signal epistemic-model RFC, opened under the maintainer account (#4616), listed three decisions for the working group to make once. The first: should one reference primitive be reused across markets, ID graphs, taxonomies, measurement currencies and panel frames, or should each get its own per-dimension type? I filed a draft PR for the shared option, described in its own text as a discussion artifact rather than a merge bid. It proposed one
{system, value, version?, name?}reference for values defined in external identity, taxonomy, geographic and measurement systems, plus a per-deployment fidelity enum and a conversion descriptor (#4622, opened May 16, 2026). One reviewer approved its second revision round the same day, after I folded in their suggestions; five more rounds followed. Review comments under the maintainer account on May 25, 2026 questioned the premise: calling these “systems” is confusing, “Not clear to me how this generalized system helps us”, and the conversion is not per-signal but happens at activation, across graphs held by several parties, with no one knowing how many times. One comment’s stated main point was to separate “what is this signal” from how it is connected to the delivery surface. The same day I narrowed the descriptions in a seventh round and offered to drop the conversion descriptor, or to close the PR if the rows it targeted turned out to need one dominant system each. The next comment under the maintainer account was the closing one, on May 30, 2026. It called the PR a discussion artifact rather than a merge candidate, said the system-reference idea “remains relevant” for concrete row-level work and should come back as a fresh, narrow PR once one of those rows is ready to adopt it, and said it “was not closed by #5017”, which had handled signal-definition enrichment without adopting these primitives. It was never merged, and none of its three schemas is onmainas of September 16, 2026. Separately, on May 28 and 29, I had written on #5017 that “the per-dimension shape vs. shared-primitive question is resolved” and that its per-dimension direction was “the right one”. #4616 was closed on August 16, 2026 as completed, with a comment calling it superseded framing and saying the actionable signal epistemic decisions are now owned by concrete signal RFCs and implementation issues. Its text has marked the third decision “decided for 3.2” since August 4, 2026, and has never marked the reference-shape question decided. - Wholesale cache-scope isolation — found that conditional-fetch tokens pass the suite even when a global token leaks across
cache_scopelayers; filed (#5739, bot-triaged ready-to-implement and milestoned 3.2.0) and implemented the fix — scope-isolation storyboards for signals and products, the schema MUST-NOTs, and the reference-agent correction (PR #5748, merged August 3, 2026). - Grader reliability — evaluate_agent_quality returning identical scores across four runs including a force_refresh pass; filed with live wire evidence (#6206, bot-triaged same day: the silently-dropped
force_refreshparameter confirmed as a real grader bug; the score-staleness root cause resolved agent-side, follow-up scoping in #6233). - Storyboard-runner account resolution — advertising
list_accountssilently switches the runner into discovery mode, which then hard-fails as “ambiguous” on any multi-tenant roster instead of falling back to the natural-key path that exists a few lines above (#6242, triaged P0 and fixed — schema-default account routing and agency-operated brand matching shipped in@adcp/sdk13.0.0-rc.11 via adcp-client#2488, with runner guidance in #6289; two implementers hit it independently), and the amplification pattern behind it — one schema-invalid capabilities response surfacing as N unrelated track failures (#6254; I first filed the link between the two as a separate issue by mistake, #6255, and closed it 98 seconds later on August 6, 2026 in favour of a comment on #6242). - 3.1.11 harness regression — the dynamic creative-asset directive shipped without its expansion step ordered ahead of the runner's own outbound validation, so conformant sellers were graded down on requests their agents never received (#6267, fixed; the affected track returned to a full pass on the first post-deploy regrade). Filed alongside the contract gap behind it — unbuildable asset slots were charged to the agent, and the storyboard contract contradicted itself on whether to fail the step or skip it — which resolved in the seller's favour: runner-owned coverage gaps now grade not-applicable rather than counting as failures, with the fixture set widened and slot-content guessing prohibited (#6269, fixed). Registry card rendering a stale tool count fixed alongside (#6268).
- Compliance reporting that hides its own verdict — the badge is gated on storyboard-level results, but the API returned the summary count with an empty breakdown (#6291) and a track could display every scenario passing while the verdict that actually gates the badge disagreed (#6292) — both fixed and shipped together. Found while diagnosing why a live agent sat at 132/138 scenarios and no badge. Once the breakdown shipped it showed the card reporting a declared specialism as failing while the underlying record read untested with zero steps attempted and zero failures (#6302) — a storyboard that had never run once being displayed as one that ran and failed.
- A badge panel that names the wrong blocker — the AAO Verified panel tells a seller to “fix the failing storyboards in this card” over a record reading 13 passing, 4 partial, 18 untested, and zero failing: every non-passing state renders as “failing” (#6363, bot-triaged as a classification bug, then fixed by a maintainer PR — the panel now names the actual blocking state).
- Registry-migration QA — five filings in one morning — probing the agenticadvertising.org migration surfaced: verified media-buy agents rendering “Not Verified” badges (#6376 — our hyphen/underscore hypothesis was superseded by the bot triage citing migration 502, which does bulk-revoke every 3.1 badge pending GA — the real cause, a migration that bulk-revoked every 3.1 badge pending GA); an unstructured badge-role error and an OpenAPI description still advertising roles that don’t exist,
salesincluded (#6377, corrected mid-flight — the 400 does carry a body; our first probe read only the status code); the 200-plus-grey embed contract for unverified agents (#6378, resolved documented-intentional, with theonerrorembed pattern to be deprecated in favour of the gate-on-verification pattern this site already ships); the hosted grader still running a superseded spec line hours after release — the operational sibling of #6321 (#6379); and the CLI producing silent mixed-line runs, kit from 3.1.13 while storyboards resolve from bundled 3.1.11 with no consistency check (#6380, reproduced as a two-run diff that cost a full grading pass). A sixth followed the same afternoon when a requeue ran the 3.0.18 cache against this 3.1-only agent and flipped the public card to failing before any scenario executed: sellers migrating 3.0 → 3.1 have no self-serve way to update the compliance target, with the proposed fix of resolving it from advertisedsupported_versionson every health check (#6383). All six closed within a day: the badge trio via PR #6387, the grader deployment after a version-manifest root-cause (#6379), the mixed-line CLI defect with a fail-closed line-inference fix (adcp-client#2518), and the compliance-target trap with owner re-detection plus registry safeguards that refuse verdicts against untrusted fallback targets (#6422/#6426, closing #6383). - One runner deployment, six defects — attributed by controlled experiment — a grader failure that would not reproduce locally was run to ground with a differential wire test: the same
get_productscall against the same database in the same minute returns the correct USD-pruned result withfilters.pricing_currenciespresent and reproduces the grader's failure byte for byte without it, so the deployed runner is dropping the filter (#6439, carrying the runner-half question #6379's closure left open). The same stripping family surfacedbrandsent onlist_accountswhile the published request schema defines no such field — the registry noticing agents for omitting a field the spec never told them exists (#6437) — and the SDK validating capabilities against a 3.1.0-beta.7 enum that rejects scenario names its own 3.1.13 storyboards gate on (adcp-client#2524). A droppedsandboxflag rounded out the family: the same account identity hashed to a second, configless account, root-caused by recomputing the tenancy-triple hash for field-subset variants and closed agent-side by seeding the triple from the resolved principal. Four runner defects, one deliberate decline, zero unattributed failures on the card. - SDK test-fixture bug — the @adcp/sdk scenario suite's sync_creatives fixture fails the SDK's own request validation (adcp-client#2443, bot-triaged with a corrected fix and regression-test ask, both shipped in adcp-client#2463/#2473; blast radius scoped to the local scenario suite in #6233).
- Conformance steps no seller can pass — three findings from taking one live agent from 147 to 240 passing steps. The storyboards mark validations
severity: advisoryand the spec says an advisory failure does not fail the step, but the runner implements none of that contract, so every validation on the stale-cache step is advisory and the step is unpassable by construction (#6320, triaged P0); a second instance of the same skip-graded-as-failure pattern turns up in the idempotency rate-limit step, which is doubly unreachable because its test kit is sandbox-scoped while the graded URL is production (#6344). And a seller who declaressupported_billing: ['operator']is asked for an error code that means the opposite of what is true —BILLING_NOT_PERMITTED_FOR_AGENTimplies the mode exists and this agent may not use it, where the honest answer is that the mode is not offered at all (#6343) — the same shape as the specialism gap below. - Grades sellers cannot reproduce — the hosted grader ran compliance cache
3.1.12while no published SDK carried it:latestshipped 3.1.5, the release candidate 3.1.11, and a step that passed locally failed on the grader with no way to see the storyboard that failed it (#6321, triaged P0; the merged fix gated the version resolver on published bundles). Found alongside a registry state machine that locks honest sellers out of grading entirely: a completed run rewrites the requested compliance target to whatever it just ran, so an agent that narrows its advertisedsupported_versionsto the one version it actually supports then fails pre-flight against the older cache and is graded on nothing at all. Narrowing was the honest move and it took the public card from degraded to failing. - A storyboard that fails the path the spec prescribes — a seller with no task layer is told by the spec to allocate the media buy id at commit and return synchronous success with
confirmed_at: null, then the async storyboard grades that seller on a submitted-task envelope it was never meant to emit (#6345). Triage first assumed omitting the controller scenario made the storyboard grade not-applicable; a live reproduction showed the force step grades not-applicable while the dependent step runs anyway and fails. Fixed: the storyboard now gates on the forcing scenario's advertisement and skips before any phase executes (PR #6368, merged), with the skip-misclassification the review surfaced corrected downstream in the SDK (adcp-client#2511). - One filing withdrawn — an argument that the audit-observation contract forced sellers to relay a verifier's verdict they never obtained (#6322). The premise was wrong: the schema already separates the two, carrying what a submission claimed in one field and what a verifier observed in others, and the only permitted code says
CLAIMED. Retracted with the correct reading (#6323) and the feature built the honest way instead — recording the claim, attributing the observation to this agent, and leaving every verdict field empty. - 53 of 62 failing steps on two public cards were ungradable — the hosted grader admits a storyboard when any one of its
required_toolsis present, while the suite selector then required all of them, socanonical_format_validate_inputwas selected for agents with novalidate_inputand failed all 17 of its steps on each card. Separately, steps the SDK marksnot_applicablewere graded as failures on the hosted runner but skipped locally. Filed with both runner source lines and a local reproduction (#7404, bot-triaged P0); this was the second time the OR gate was fixed. The first was #6773 in August —billing_gate_dispatchlistingget_adcp_capabilitiesbesidesync_accounts, which every conformant agent satisfies, so signals-only agents were admitted and failed the first step instead of skipping. #6774 merged inside fourteen hours and review widened the one-line deletion to 17 storyboards. It landed on main only: the 3.1.x cherry-pick its own PR body called warranted was never made, so the storyboards kept mis-selecting on the 3.1.20 line the hosted grader ran until September 22, 2026. The September fix is that backport finally arriving — the storyboard half merged on September 14, 2026, #7405 on main and the 3.1.x backport #7406 twenty-one seconds later, this one written from here rather than upstream — and shipped the same afternoon in v3.1.23, whose changelog names the filing: agents withoutvalidate_inputnow take a coverage-gap skip. The second half has since moved. #7454, merged September 13, 2026 and linked in the thread that day, gradesfixture_seed_unsupportedand similar skip reasons as not applicable. On the signals card the two storyboards filed under this half,deterministic_testingandidempotency, have readuntestedat 0/0 since September 14. They are no longer failures, though not the passes the local SDK reports either. The September 14–16 runs also reportsdk_version14.0.0-rc.35, while #7454 was built against rc.33, so the shift cannot be credited to #7454 alone. A follow-up on September 15 missed that and argued the public feed could not represent a skipped step at all. The feed already could, and a correction on September 16 says so. The issue was retitled at 10:15 UTC on September 16, 2026, three minutes after that correction, to “Hosted grader: 11 failing steps remain on two cards after #7454/#7405/#7406 (sales 10, signals 1); budget-truncated runs still published as complete grades”. The count matched both cards’ latest runs at the time; how much of the drop those three PRs caused is less clear. The sales card showed the 3.1.20 bundle (published September 2, before #7405 and #7406 merged) until 23:15 UTC on September 29, when an owner test re-graded it on 3.1.24 (see the governance entry below); it had not run since September 17, five days before the grader moved 3.1-line agents to 3.1.23. The signals card was re-graded by a heartbeat run at 20:56 UTC on September 29, on bundle 3.1.24 under @adcp/sdk 14.0.0-rc.50: 10 of its 38 storyboards passing, the other 28 untested, none partial or failing;billing_gate_dispatch, the last signals failure in this thread’s count (one step, in the September 16 retitle), readsuntestedat 0/0 steps. None of the ten sales failures is in a storyboard the filing named, and the 17canonical_format_validate_inputfailures on the sales card cleared because this agent now implementsvalidate_inputand, since September 15, answers the storyboard’s"3.1-beta.5"pin with 3.1, which the exact-match rule for prerelease pins does not allow (#8251; see the entry on the 3.2 bundle below). On truncation, the filing’s third report: the sales card’s two short runs on September 9 and 10, at 130 and 124 scenarios against 193 in a full run, still readcompleteand authoritative in the public history feed. PR #7453, merged September 13, 2026, stops runs the SDK markstimed_outfrom replacing the public grade; PR #7506, which would have done the same for runs that report no completeness at all, was closed unmerged on September 23, 2026, as superseded by PR #7622 (merged September 21). The gap it targeted is still open: on main, a run that reports no completeness is still recorded ascomplete. - Grades that cannot be attributed — two heartbeats of the same agent build, with no deploy between them, disagreed by a scenario. Nothing in the public compliance feed identified the grader that ran them, so a runner that changed under you and a runner that is simply non-deterministic looked identical from outside. Asked for one field on each history entry (#7429). It arrived by another route. #7454, merged September 13, 2026, writes a provenance block on each new history entry, including the exact SDK version, the compliance bundle and a grading-policy version. Runs graded before it was deployed carry none. #7433, a separate PR to persist the runner version, merged September 24, 2026 and closed #7429; history entries have carried
runner_capability_versionsince. - A control that reports success and leaves no trace — an owner requeue on September 7 produced no run and no history row, and the feed’s newest
owner_testentry predated the click by 24 days. Filed as a two-way fork, silent no-op or ran-and-discarded-as-unchanged (#7428, bot-triaged bug and P1 eight minutes after filing). Closed as verified on September 15, 2026 with a third answer. Requeue comply only schedules the next heartbeat, by design; the click fell inside a 19.4-day window, August 19 to September 8, in which the registry graded the agent not at all, and on September 16, 2026 the feed still showed those 465.6 hours with no rows; and a fresh click with devtools open put three runs through admission to completion on September 13, which the maintainer confirmed end-to-end. The original click left no retained HTTP evidence, so its cause is unrecoverable, and the confirmation fixes triage scoped, alast_rechecked_aton the card and a flash that does not auto-dismiss, did not ship with the closure. One thing this filing should not claim: the requeue semantics were not a discovery. They were written down in #4265’s body in May 2026 — the fix our own #4253 was closed against — where the endpoint is described as clearinglast_checked_atso the agent is eligible on the next tick. We filed four months later against behaviour already documented in a thread we opened. - One gate, two meanings —
required_toolsis read by two code paths that disagree: the suite selector excludes a storyboard when any listed tool is missing, the per-storyboard runner admits it when any listed tool is present. A storyboard naming two tools against an agent holding one is therefore skipped by the first and admitted-then-failed by the second. Filed after the same defect recurred on a line an earlier data fix had not reached — adcp-client#2652 was closed on the strength of a storyboard-side change, andbilling_gate_dispatchmis-selected again four weeks later on 3.1.20 (adcp-client#2882, bot-triaged bug / conformance / ready-for-human). The bot triage confirmed the divergence against published 13.0.2 and the convergence already written incomply.ts, and put the choice of semantic to the maintainer, who shipped any-of rather than all-of, which is only safe because the storyboard-side fixes strip the universal capability tool fromrequired_toolsfirst. It shipped in 13.0.4 on September 14, 2026 (comply.js:600now readsrequired.some(…)) and the issue closed the same day. Against the sales agent the change is measurable: 305 storyboards selected on 13.0.2 became 348 on 13.0.4 with byte-identical caches, so the delta is the selector alone, and four of the newly admitted storyboards fail. - A grader that disagrees with itself between two of its own commands — since
@adcp/sdk13.0.3,storyboard rungrades everyresponse_schemacheck on acomply_test_controllerstep as failed withno_schema_registered, whilestoryboard steppasses the identical step seconds later on the same SDK with the schema id resolved. The agent's response is conformant either way — the controller arms and echoes the forced task id (adcp-client#2867). It was the reason both of our agents stayed pinned to 13.0.2 rather than trackinglatest— the newer build could not grade its own controller surface. Root-caused by the bot triage toisExternalResponseSchemaAuthoritativetreating a test-harness tool like a protocol tool, fixed in adcp-client#2894 on the 13.x line, released in 13.0.4 on September 14 and closed the same day. Both agents moved to 13.0.4; on September 29 the sales agent’s local runner moved on to 13.1.2, the first stable build to bundle 3.1.24, and the signals adaptor followed to 13.1.3 on October 6, 2026, re-running the suite on 3.1.24 (55 of 55 scenarios, 0 failed steps). - A gate graded by the wrong principal — the hosted grader’s September 29, 2026 owner test reported
comply_controller_mode_gate: expectedFORBIDDENfor the live-mode account check, receivedNOT_FOUND. The gate was right. Production answers the storyboard’s declared live kit withFORBIDDENand echoes the correlation id, verified by direct call; the hosted suite path substitutes the run’s own sandbox bearer into the run-level test kit, and the SDK’sresolveDeclaredTestKitnever loads a storyboard’s declared kit when a run-level one is present, so the step ran as a sandbox principal and found no creative. #6735 had fixed the four single-storyboard call sites and left the suite path alone. Filed as #7772 on September 30 at 01:35 UTC, with a regression test on our side that pins the transport-level cause (nofluffadvisory#79) rather than a change to a gate that was already correct. Triaged P0 nine minutes later; the bot’s code reviewer corrected the proposed server-side fix — the suite iterates storyboards inside the SDK, so there was no per-storyboard hook to mirror — which we accepted, offering an SDK rule (a declaredsandbox: falsekit wins over the run-level kit) as the smaller change. The maintainer shipped a server-side route instead: #7808 addsliveModeTestKitRoute, which sends a storyboard’sfrom_test_kitsteps with the declared live-mode kit’s key against the agent under test, with a unit test named for this issue, merged October 1 at 11:05 UTC, thirty-three and a half hours from filing to merged fix. The public card lists the storyboard as untested before and after, an optional phase it never counted; the receipt that carried the wrong verdict is owner-only. - A warning the graded line prints and the hosted grader does not — moving the signals adaptor to
@adcp/sdk13.1.3 (the currentadcp-3.1build; 3.1.24 bundled since 13.1.2) opened the run with a schema advisory:compliance_testing.scenarios“must be one of” the six 3.0 controller scenarios, rejectingforce_get_signals_armandforce_task_completion, which the runner’s own signals storyboard requires and the 3.1 schema explicitly allows. The cause is not the schema. The 13.x discovery probe runs with a major-only envelope, its transport-level validator therefore selects the 3.0 line, and it prints the result unconditionally; the profile-level check re-validates the same payload against 3.1.24 and passes, so the structured result is clean, and the hosted grader’s lines — 14.x, then 15.0.0 and 15.2.0 from October 9 and 15.8.1 from October 12 until the move to 15.9.2 on October 16 — discover pinned-first and gate that output, which a 14.2.0 run confirmed by printing nothing. Filed as adcp-client#3124 on October 6, 2026 with a one-hunk ask: backport to 13.x the console gate and version label that adcp-client#2603 put onmainin August. Worth recording how the filing changed under verification: the first draft called it a spec enum gap and blamed the hosted grader; three verification passes showed the enum is the 3.0 line’s own, that adcp-client#3042 kept the selection deliberately, with a code comment saying so, and that only the console output on 13.x was ever wrong. Triaged fourteen minutes after filing: a bug, ready to implement, the one-hunk backport accepted as written and the response-driven selection to be scoped separately, which it now is as adcp-client#3125: change only the local validation line invalidateResponseSchema, take it from the response’s ownadcp_version, and leave the function the triage flagged alone. - A Strict grade that failed on one optional step, and two rollups no agent could pass — the registry’s Strict Spec grading profile, which an owner can select in place of Legacy, failed this site’s signals agent on a single step:
deny_live_callerin the universalcomply_controller_mode_gatestoryboard. Since #7808 (October 1), the fix for our #7772 above, the hosted grader sends that step with the storyboard’s declared live-mode test-kit key, and a single-endpoint seller that exposescomply_test_controllerMUST refuse it withFORBIDDEN; the signals agent answered 401 at its auth gate. The phase is optional and the public card never counted it (the storyboard reads untested, 0 of 0, before and after the fix), but Strict fails on any recorded step failure in a universal storyboard. The live-caller refusal shipped on October 9, 2026 (adcp-signals-adaptor#340): the live-mode key now getsFORBIDDEN, and the owner diagnostics for the 17:53 UTC owner test that followed list no failures. Two bugs we filed on October 9 each blocked every agent on their own. The universal bundles that contain no storyboards (three on 3.1.24, six on 3.2.3) always rolled up untested (adcp-client#3185), and a storyboard that never touches a lifecycle resource (a media buy, creative, account and the like) was capped at partial even when every step passed, which caughtcapability_discoveryfor every agent and the signals baseline for every signals agent (adcp-client#3186). Fixing both would still leave this agent short, as #3186 itself notes: a storyboard whose required tools the agent lacks is graded partial outright, which caps 20 of the 38 universal bundles on its run. Both were triaged as bugs, ready to implement, within five minutes of filing, and both closed as completed at 03:01 UTC on October 25 with adcp-client#3419, which ships in@adcp/sdk16.5.0 and 17.0.0-beta.0; since the hosted grader moved to 17.0.0-beta.0, from a heartbeat at 03:28 UTC on October 26, the public history has shown this agent’s four graded tracks as pass where it had shown them as silent, on every heartbeat until the one at 15:51 UTC on October 29, which readsecurity_transportpartial (see the request-signing entry below). What #3186 noted is what remains, and we filed it as #8527 at 09:54 UTC on October 27: 27 universal storyboards that need media-buy, creative or account tools count as missing tools for a signals-only agent, so their bundles grade partial and Strict reads incomplete; as of 22:30 UTC on October 29 it is open, on the P0 Bugs milestone, with no reply, so Strict still cannot read passing for this agent. A third filing from that afternoon, adcp-client#3181, was triaged the same way fourteen minutes after it went up: the storyboard request builder adds abid_priceto fixed-price CPM creates, which is why thesales-fixed-ratestoryboard, merged tomainon October 20, prices its fixture on CPP. - A heartbeat that had not run in four days, and the question that stayed open — the dashboard advertised a recheck cadence it was not keeping: no scheduled run against the signals agent in four days while a manual evaluation of the same agent passed 30 of 30 (#4253). #4265 shipped three causes the next day —
last_checked_atwas stamped beforecomply()ran, so a crash inside the window silenced the agent for up to 12 hours a cycle; every heartbeat row was writtendry_run: trueand so never reached the dashboard; and manual runs wrote only to a table the dashboard does not read. Two asks did not ship: the last-automated versus last-manual distinction, and an answer to why the tick itself had not fired. The closing comment carved that second one out as a known follow-up — a scheduler concurrency limit of five against an unbounded wait queue, where five hung jobs block the heartbeat indefinitely — deferred until production diagnostics existed. By September 15, 2026 the public feeds showed what looked like a stall of that shape, fleet-wide: across all 35 registered agents, published heartbeat runs went from 25–36 a day over September 8–13 to six on the 14th and one on the 15th, with no published heartbeat in any feed for fourteen hours and 30 of the 31 agents with a heartbeat on record past a day since their last. Filed with the fleet numbers as #7546, pointing at the follow-up rather than asserting it. The filed sweep missed one of the 35 feeds, so the issue shows five and 29 of 30. Bot-triaged seven minutes later as a bug,priority:P0, P0 Bugs milestone, ready-to-implement. Its code-reviewer readscheduler.tsas confirming the mechanism, withreleaseSlot()in afinallythat does not run until the job’s promise settles: “Five hung jobs = five permanently occupied slots = everything else waits forever.” The fix it named is aPromise.racetimeout inrunJob, with a queue-depth cap as secondary hardening; the triage’s other expert recommended pulling/internal/jobsfirst to identify the stuck slots. The fix merged at 17:09 UTC on September 16, 2026 as #7556, opened under the maintainer account, and #7546 closed as completed one second later. It is thePromise.racetimeout the triage named, made opt-in: a scheduled job can setexecutionTimeoutMs, and when that expires the scheduler signals the run to stop through anAbortSignaland stops waiting on it, so thefinallyholdingreleaseSlot()runs even if the job never settles. Only the compliance heartbeat opts in, at two hours against a documented worst case of about 115 minutes; it passes the signal into the SDK run, checks it before each agent, and, when an aborted run throws, stops before recording a failure against the agent it was testing. The job status behind/internal/jobsnow reportsactiveJobs,queuedJobsandmaxConcurrency; neither this PR nor its 3.1.x backport adds the queue-depth cap. The repository’s Deploy workflow reports a successful Fly.io deploy of the merge commit, completed at 17:38 UTC, and two more ofmainthat include it, completed at 17:55 and 21:05 UTC. The 3.1.x backport, #7557, merged at 19:05 UTC the same day and carries over the guardmainalready had against a job overlapping its own previous run. The 35 public compliance feeds list only complete, authoritative runs, and since #7453 a timed-out run is kept as audit evidence without changing the public grade; the heartbeat also tests crawler-discovered agents outside the public listing. So the feeds bound the heartbeats that finished on these 35 agents, not the ones attempted or the ones run on other agents. In them, after one heartbeat on September 15, two appear on the 16th, at 18:19 and 18:23 UTC, on two agents, neither of them mine, and none after those through 12:46 UTC on September 17; the feeds do not show whether those two runs owe anything to the fix. Of the 31 agents with a heartbeat on record, 29 are more than two days past their last one. In those feeds, as of 12:46 UTC on September 17, my two agents had no heartbeat since the merge: their last were at 20:03 UTC on September 14 (signals) and 02:43 UTC on September 13 (sales), and their newest runs were owner-triggered, at 09:52 UTC on September 16 and 11:41 UTC on September 17. A follow-up on #7546 at 11:01 UTC on September 17, 2026 set out these public numbers with the same caveat and asked what the jobs status endpoint shows. The fleet-wide question was answered on #7355 on October 16, from the registry’s database rather than the public feeds: by the maintainer’s counts, September 14 to 28 “was not a scheduler stop”. Heartbeats kept running, 9 to 29 a day against 57 to 74 over September 1 to 13, but 0 to 14 a day were published; most of the rest timed out against the 600-second budget (201 runs on 13 agents, 654 seconds on average; #8268 dates them September 13 to 30) and were recorded as non-authoritative, and 33 runs on one agent did not complete because its declared specialism was missing from the grader’s compliance cache. So the public numbers above showed only the heartbeats published on those 35 agents, and by his counts the drop in them was not the scheduler stall this filing pointed at. - A heartbeat schedule our own owner tests re-armed, and a premise we got wrong — every authoritative compliance run re-armed the heartbeat schedule, owner tests included: the
schedule_nextCTE inrecordComplianceRunsetnext_compliance_check_atto the time of the run pluscheck_interval_hours, 12 by default, and clearedrequeued_atwithout looking attriggered_by. A full-suite owner test therefore cancelled a pending requeue and put the next heartbeat a full interval after the test (#7680, filed September 24, 2026, bot-triaged P1). The filing said that both agents at the centre of #7355 are this site’s, among the most owner-tested in the registry, and that we generated that traffic, which skewed the sample there; it kept the mechanism separate from #7355’s throughput question. The first triage took the second of our two suggested fixes, scoped as a gate onrequeued_at IS NULL. Eight minutes later we showed that our suggestion broke either way it was read. As scoped, it would make a requeue permanent: the CTE was the only placerequeued_atwas cleared, so the agent would stay due and sort first in every batch. Copied literally, the guard we had pointed to would stop every heartbeat from advancing the cadence. The triage was rewritten at 17:06 UTC with the shape we had argued for, atriggered_bygate, but a different predicate: we proposed= 'heartbeat', it chose!= 'owner_test'so thatmanualandwebhookruns still advance the cadence. On the fix, PR #7691 from another contributor, we argued for= 'heartbeat'again as the fail-closed choice, on the premise thattriggered_bywas an unvalidated string. The premise was wrong. We had cited three places. Two,compliance-db.ts:1294andregistry.ts:1511, are read shapes; the third,registry-api.ts:8730, passes through a request field typed'owner_test' | 'manual'atcompliance-refresh-requests-db.ts:16. The write input,compliance-db.ts:301, is typed by theTriggeredByunion at:22, which also declareswebhook, a value we had said appeared nowhere. The maintainer account pointed to the union, and we retracted six minutes later, naming only the first two citations. The PR author also handled the first-runINSERT, which our filing had left out and the triage had deferred as a follow-up, with aCASEon the same literal as theWHERE. What held from our side was that the two have to change together; the same reply called that catch important, and the shipped code comment now says so. #7691 never merged. The same SQL and test matrix landed inside the maintainer’s PR #7969 (“fix(runtime): persist replay claims and preserve heartbeat cadence”), whose description says it includes the scheduling fix from #7691. It merged at 10:40 UTC on October 9, 2026, and #7691 was closed as superseded the same minute. #7680 stayed open until 09:45 UTC on October 13, 2026, when the maintainer closed it with before-and-after evidence from this site’s sales agent: a heartbeat at 09:44 UTC on October 10, an owner test at 17:42 and the next heartbeat at 22:10, on its 12-hour cadence, where before the fix the owner test would have moved it to about 05:42 the next morning. The public history shows the same three runs. The same comment credits the fleet’s recovery to PR #7844 on October 1, not to this fix. The companion filing, #7693 (September 25, 2026), argues that no floor on heartbeat staleness can be keyed safely whilenext_compliance_check_atholds three states at once: the cadence, an in-flight batch lock and a skip deferral. It was bot-triaged P1 and ready to implement, with every skip site writing a newlast_heartbeat_attempt_atand the floor keyed on that same column, so the floor could never fire for the skip-starved agents it exists for. We said so, and two minutes later the triage was amended to key the floor on a newlast_heartbeat_success_at. In that comment we also withdrew our own sequencing, which had the floor waiting on a platform-side request to expose queue state. As of 22:30 UTC on October 29, 2026 none of #7693 is built: there is no PR, andmainwrites the batch lock intonext_compliance_check_at; #8188 (merged 01:55 UTC on October 14) added a record of which worker holds each claim, so a dead worker’s claims are released, with a backoff for an agent whose suite had started, and left the lock in that column. At 09:45 UTC on October 13 #7355 was re-scoped to heartbeat throughput falling since October 3, and its list of multi-day gaps includes this site’s signals agent: 102 hours without a completed heartbeat in the public history, from 16:52 UTC on October 8 to 23:08 UTC on October 12. The only completed run the public history shows inside that gap is an owner test at 17:53 UTC on October 9, 25 hours after the last heartbeat and already 13 hours past the 12-hour cadence. #7969 was in production by then: the Deploy workflow’s Fly deploy of a latermainthat contains it finished at 15:43 UTC that day (run 37947767784, marked failed by an artifact-upload step that ran after the deploy), so that owner test left the schedule alone, and #7680 does not account for the gap. At 23:35 UTC on October 16 the maintainer corrected that re-scope: its per-day figures came from the public compliance-history endpoint and undercount, and by his account the October decline was “mostly deliberate” — agents outside the heartbeat’s eligibility set (no member registration, badge or fresh publisher authorization) or in backoff after inconclusive runs. The one real stall he names is the worker crash loop that two fixes of his, merged early on October 14, address: per their descriptions, one seller’s 17.8 MB reply was OOM-killing the heartbeat worker, and because a batch is claimed whole, each crash dropped the rest of a fixed six-agent batch, all six of which had gone 7 to 9 days without a run by October 13; #8187 caps agent responses at 4 MiB, and #8188 is the claim record above. The comment does not name the six. This site’s signals agent, graded on October 9 and 12, was not one of those six, and as of 22:30 UTC on October 29 the thread does not say whether heartbeats ran against it in the 102-hour gap and went unpublished, or did not run, though #8268 counts only two to four non-authoritative heartbeats a day fleet-wide in early October and none on October 12 and 13. Two narrower defects he filed at 23:34 UTC on October 16, both closed as completed on October 19 (at 08:53 and 10:28 UTC), were #8268 (a heartbeat that ran past the 600-second budget was recorded as non-authoritative and never published) and #8269 (an agent graded on a 3.0 bundle that declaredsponsored-intelligence, a specialism defined only from 3.1, had its whole run voided; the filing says the same applies to any later specialism), and he said he would close #7355 after one more per-day check showed several clean days; as of 22:30 UTC on October 29 it is open, with no comment since his of October 16. - Request-signature verification shipped (test keys only), output schemas of ours that degraded the card for 14 hours, and a suite that contradicts itself for a signing seller — the signals agent has verified RFC 9421 request signatures since October 10, 2026 (adcp-signals-adaptor#342): it advertises
request_signing.supported: truewithcovers_content_digest: "either"("required"since it switched to 3.2 on October 16) and emptyrequired_for,warn_forandsupported_forlists, trusts only the public conformance test keys, and treats a verified signature as a sandbox caller, never a live one. A run of the 15.2.0 runner from here against production at 21:27 UTC on October 10, right after the merge, passedsigned_requests28 of 28. The first hosted grade after it was the heartbeat at 23:08 UTC on October 12 (runner 15.8.1, bundle 3.1.24): the public history shows 3 of 3 scenarios passed on itssecurity_transporttrack, which the October 9 owner test had skipped, and per the owner diagnosticssigned_requestspassed 28 of 28. The same heartbeat took the card from passing to degraded, and the fault was ours. The runner’s MCP client (@modelcontextprotocol/sdk1.32.1) checks a result’sstructuredContentagainst the tool’s declaredoutputSchema, error results included, once it has cached the tool list, and the 15.8.1 runner caches it. Our schemas had not kept up with our payloads:get_signalsstill required the v2countfield that our own payload dropped when it moved to the 3.1 shape, so every call failed with MCP error -32602 and thesignal_ownedbadge storyboard went partial; four more tools declared schemas no error result could match, andactivate_signal’s rejected its own success result as well. The run read core 37 of 38 scenarios, signals 5 of 9 and error handling 4 of 8. None of our tool schemas had changed since September 8 (#341 to #343, merged between the passing October 9 owner test and this heartbeat, did not touch them), and our own runs on 15.2.0, the post-merge production run included, passed without exercising that check. The heartbeat at 11:18 UTC on October 13 still ran the old code and read partial again. The fix (adcp-signals-adaptor#344, merged 11:28 UTC) makes every output schema either the tool’s real success shapes or the error envelope, and an owner test through Addie’sevaluate_agent_qualityat 13:13 UTC on October 13, under 15.8.1, read passing about 14 hours after the card degraded: 11 of 38 storyboards, up from 10 before signing, all 58 scenarios on the four tracks it ran (the media-buy and creative tracks were skipped),signed_requestsandsignal_ownedamong the passes, the Legacy signals badges intact and the streak reset to zero. As of 22:30 UTC on October 29, of the 33 agents in the public listing two cards record asigned_requestspass, Publink’s (34 of 34) and BidMachine’s seller agent’s (33 of 33), while this one’s reads 32 of 33 after a heartbeat at 15:51 UTC on October 29, having read 33 of 33 on the heartbeat before (see the next entry); the one other card with a result, Triton Digital’s (partial, 3 of 29 steps), has dropped out of the listing. Signing also produced four filings on October 13. Three are against the SDK; as of 22:30 UTC on October 29 the first two are untriaged, with no labels or replies, and the third is fixed in the 16.x SDK: the 13.x runner gradessigned_requestsvectors 001, 007, 018 and 028 against a signing posture the agent never declared and fails in-library vector 025 for every agent, because three fixes frommainwere not backported (adcp-client#3241);verifyRequestSignatureaccepts any nonce and runs the step-14 duplicate-key check before the step-13 replay insert (adcp-client#3242), and our own verifier, built on 13.1.3, has the first of those gaps, which we shipped knowingly on October 10, while its card read 33 of 33 on 3.2.4, as it read 28 of 28 on 3.1.24, because no vector bundled with SDK 15.9.0 to 15.9.2 (3.0.25, 3.1.24 and 3.2.4) tests the nonce rule; and every valid ES256 signature fails verification in workerd, the Cloudflare Workers runtime the adaptor runs on, which is why it carries a crypto shim (adcp-client#3243; fixed on October 15 by adcp-client#3272, merged 17:34 UTC, which ships in16.0.0-beta.3and the later 16.0.0 betas and in every stable build from16.0.0(October 20) tolatest, 16.6.1 as of 22:30 UTC on October 29, and in the 17.0.0 betas, though not in 15.9.5 to 15.9.8; the adaptor’s 13.1.3 predates it, so the adaptor keeps the shim). The fourth is evidence on adcp#7563: the 3.1.24 suite contradicts itself for a seller that supports request signing, because vector 027 requires a 401 for an unsigned request carryingpush_notification_config.authenticationandmedia_buy_seller’s create step sends exactly that request, unsigned; by our reading, not a run, #8064 as first drafted would have moved the conflict onto sellers that setlegacy_hmac_fallback: falserather than remove it. On October 19 the maintainer revised it to carry both halves of our suggestion: the fivecreate_media_buyfixtures that sentpush_notification_config.authentication,media_buy_seller’s create among them, no longer do, and vector 027 now applies only whenlegacy_hmac_fallbackis true. It merged tomainat 13:01 UTC on October 24, leaving the 3.1.x bundles as they are, and #8475 (merged 10:52 UTC on October 28) then gave the choice of webhook-signing mode back to the buyer’s registration while keeping the deprecation of condition (b); both first shipped in the v3.3.0-beta.1 prerelease, and as of 22:30 UTC on October 29 the 3.1.x line, v3.1.30 included, still carries vector 027 and that create step as before. That is why the sales agent’s signing build, which passessigned_requests28 of 28 locally and then fails that create, is on hold. As of 22:30 UTC on October 29 the sales card grades on 3.1.30, since a heartbeat at 09:01 UTC on October 29, its first on 3.1.30, after #8561 moved hosted 3.1 grading there (it graded on 3.1.27 from 00:58 UTC on October 17, after #8242 moved it off 3.1.24, and on 3.1.29 from 01:44 UTC on October 18, after #8278); the v3.2.4 signing vectors (026’s new expected code, negatives 029 to 031, the profile-3.2 mirrors) apply only to an agent that claims 3.2, which the signals agent has done since October 16, and its card, on 3.2.8 since October 29, readsigned_requests33 of 33 until a heartbeat at 15:51 UTC that day read 32 of 33 (next entry). - AdCP 3.2 held behind a switch until the hosted grader could grade it, a private sandbox bearer for that grader, and two partial grades on signing — on October 15, 2026 the signals agent gained AdCP 3.2 support (adcp-signals-adaptor#345, merged 14:39 UTC and deployed) behind a
SERVE_ADCP_3_2switch that shipped off, with a golden test holding the switched-off output byte for byte to the previous build’s, apart from the compliance-run fields it masks. Two things kept it off. The hosted grader’s pin until October 16,@adcp/sdk15.8.1, has no builder forsigned_requestsnegatives 029 to 031, so it throws before sending those three requests and fails them for any agent graded on 3.2.4 that supports request signing (the builders first shipped in 15.9.0); switched on, a local run on the 3.2.4 bundle read degraded on 15.8.1 and passing on 15.9.1, by our emulation of the hosted status rules, so we filed the pin as #8221 at 15:10 UTC on October 15, asking for 15.9.x. And our review of #345 found that under 3.2 the test controller gainsseed_account, a seed is stored per sandbox principal, and everyone holding the public demo key is the same principal, so anyone could pre-seed a diverging account and make the grader’s own seed step fail withINVALID_PARAMS. Since October 15 AAO grades this agent with its own private sandbox bearer token instead, which has the public demo key’s privileges over MCP in a separate namespace (adcp-signals-adaptor#346, merged 21:58 UTC, and adcp-signals-adaptor#347, merged 23:31 UTC, a last-seen marker that showed which key the 23:41 UTC owner test used). #8231, merged at 09:00 UTC on October 16 without referencing #8221, moved the grader to 15.9.2, and the first public run on 15.9.2 came at 10:07 UTC that day; #8221 drew no reply, and we closed it at 19:08 UTC that day as resolved by #8231. Local runs on 15.9.2, scored the same way, then read passing with the switch on (bundle 3.2.4: the same 11 storyboards as the 3.1 card,signed_requests33 of 33 and all 14 account seeds accepted) and unchanged with it off, and the card’s first hosted grade on 15.9.2, a heartbeat at 13:08 UTC, read passing on 3.1.24. The switch went on with adcp-signals-adaptor#348, merged and deployed at 13:18 UTC: the agent advertises "3.0", "3.1" and "3.2", serves 3.2 to a caller that pins no version, advertisescovers_content_digest"required" and offersseed_account. An owner test at 13:58 UTC graded it on the 3.2.4 bundle under 15.9.2: passing, 11 of 52 storyboards with 41 untested,signal_owned3 of 3 andsigned_requests33 of 33, and the registry issued the agent an AdCP 3.2 signals badge (signal-owned) the same minute, beside the 3.1 and 3.0 badges it keeps. It was graded on 3.2.5 from a heartbeat at 01:57 UTC on October 18, on 3.2.6 from one at 15:01 UTC on October 23, the first public run on 3.2.6, on 3.2.7 from one at 03:17 UTC on October 25, the first public run on 3.2.7, and on 3.2.8 from one at 03:48 UTC on October 29, and every grade from 13:58 UTC on October 16 to that one passed, the last at 11 of 52 storyboards. As of 22:30 UTC on October 29 the card reads degraded on 3.2.8, 10 of 52, after the heartbeat at 15:51 UTC that day, on the same runner as that pass (17.0.0-beta.3), readsigned_requests32 of 33. Per the owner diagnostics the failed step is the 3.2-profile rate-abuse vector, 020: the runner sends its 100 cap-fill requests one after another with a 10-second timeout on each, request 94 was aborted, so the grader reports the cap as never established and the vector as ungraded, which still counts as a failed step, though the request past the cap got the 401request_signature_rate_abuseit should; a local run of the same runner against production at 16:03 UTC, on the 3.2.7 bundle it carries, passed 33 of 33. The 3.2, 3.1 and 3.0 signals badges stand. Of the 33 agents in the public listing it is one of 12 graded on 3.2, all on 3.2.8, and one of three with a 3.2 badge: Publink’s agent has held a 3.2 media-buy badge (sales-non-guaranteed) since 05:50 UTC on October 23, and BidMachine’s seller agent has held 3.2 media-buy (sales-non-guaranteed) and signals (signal-owned) badges since 23:22 UTC on October 28. Labels that describe what the switch does not change stay on 3.1.x on purpose:/health’sadcp_spec_version, the/openapi.jsonversion, theX-AdCP-Spec-Versionheader and the demo page’s "3.1.20-conformant" note on itsadagents.jsonname the vendored 3.1.20 schemas the agent validates payloads against, as no 3.2 schema set is vendored, andext.compliancedescribes a recorded 3.1.24 run of October 6. Three static strings that called the release 3.1 GA, the demo page’s sidebar and endpoints panel and/privacy, have read 3.2 GA since adcp-signals-adaptor#349 (merged 18:53 UTC on October 16 and deployed a minute later), which also gave the vendored schemas (3.1.20) a sidebar row of their own and put the recorded run’s line on the Conformance row (3.1.24). A lesson for any agent that verifies signed requests:signed_requestsvector 020 fills the per-keyid replay cap for the test key (100 entries, the test kit’s grading target), and our verifier keeps those entries for six minutes, so signed grades against our endpoint must start at least six minutes apart; an owner test that finished at 13:13 UTC, under five minutes after the 13:08 UTC heartbeat, had some of its signed vectors refused withrequest_signature_rate_abuse, per the owner diagnostics, and read partial (security_transport1 of 3), which took the card off passing until the 13:58 UTC run: a collision of two signed grades rather than a defect. Earlier, on October 15, the heartbeat at 11:52 UTC readsecurity_transport1 of 3 and took the card to degraded. Per the owner diagnosticssigned_requestsread 26 of 28, the two failures being probes that aborted after about 10.3 seconds inside the MCP initialize handshake. Neither reached a signed request, so the failure is not in our verifier; the diagnostics do not show whether the delay was in our endpoint, the network or the grader. An owner test at 13:57 UTC read passing again, 11 of 38, and every grade after it passed until the 13:13 UTC collision on October 16. - Four ways the 3.2 bundle grades a seller on what the spec lets it leave out — four filings from here at 20:27 UTC on October 16, 2026 came out of one sweep: a local run of the 3.2.3 bundle on October 14 against the sales agent’s 3.2 build, which sits behind a switch that is off in production, re-graded with a reimplementation of the registry’s step-disposition rules and checked against the 3.2.4 files hosted grading uses. #8251: three storyboards pin the prerelease
"3.2-beta.6"on every step (14 of 14 inctv_experience_validate_input, 21 of 21 inpremium_display_canonical_validation, 2 of 2 inget_products_rejected), so a 3.2 seller that does not advertise that prerelease answersVERSION_UNSUPPORTED, as the exact-match rule for prerelease pins tells it to, and fails all three. The same filing names a gap of ours: the sales agent passescanonical_format_validate_inputon its 3.1 card, a storyboard that pins"3.1-beta.5"on all 16 steps, only because since September 15 it answers any 3.1 prerelease pin with 3.1, which that rule does not allow. #8252:product_filter_behaviorsends a refine call with nobuying_modesgate, and SDK 15.9.2 refuses that call before it reaches a seller that does not declare refine, so a seller that declares wholesale but not refine reads partial, 2 of 3. #8253: 20 steps in 9 storyboards call tools a seller may leave out, among themlist_products,control_media_buyandsync_event_sources, with no gate that implies the tool, and the hosted grader counts the resulting skip as a failure; three of those storyboards fail or read partial on our seller, two of them partly through gaps of ours (we do not apply coverage filters onget_products, and we do not implement accepted-proposal change rights). #8254:collection_selection,owner_sold_channel_carriageandadvanced_delivery_reportinggrade product features the spec makes optional, and nothing a seller can declare marks them not applicable; one of the failed checks in our run of the third, clicks not narrowed out of the totals, is a bug of ours. The maintainer fixed the first three onmainthat evening — #8260 (23:31 UTC) removed the 37 pins and the 16"3.1-beta.5"ones, #8261 (23:38 UTC) gates the wholesale and refine phases onbuying_modesand #8263 (23:54 UTC) gates the optional-tool steps — and opened 3.2.x backports: #8267 (merged 00:03 UTC on October 17), #8270 (merged 00:08 UTC) and #8271 (merged 00:52 UTC on October 18). #8254 was a policy question: the maintainer’s decision PR, #8259, proposed catalog evidence, applying these storyboards only where discovery finds a product with the feature in the seller’s own catalog, and was held for the working group; our comment on it at 00:11 UTC on October 17 backed catalog evidence for 3.2.x and seller-wide declarations for 3.3, citing our live catalog, where none of the five products setscollectionsandreporting_capabilitiesdeclares neither format nor time breakdowns; as the comment says, under catalog evidence all three would grade not applicable for our seller. The maintainer closed #8259 unmerged at 05:47 UTC on October 23 as superseded, and #8254 closed at 00:31 UTC on October 24 with #8428, which, with #8336 before it (merged 04:34 UTC on October 23, for the reporting storyboard), finds the products these storyboards grade in the seller’s own catalog instead of seeding them: a complete, valid discovery that finds none now gradesfixture_unsatisfied, shown as not applicable, while malformed discovery and broken support still fail. Neither is in a 3.2.x release as of 22:30 UTC on October 29 (v3.2.7 and v3.2.8 carry neither); both first shipped in the v3.3.0-beta.1 prerelease of October 29. v3.2.6 (October 23) is the first release to carry the fixes for the first three filings, and hosted 3.2 grading has run 3.2.6 or later since that day. The sales card is graded on 3.1, which none of these fixes reaches: the 3.1 bundles from 3.1.24 to 3.1.30 keep the 16"3.1-beta.5"pins, so itscanonical_format_validate_inputpass rests on the answer to prerelease pins described above. #8251 itself says that removing the pins makes those storyboards gradeable for our 3.2 build, not passing, because the build does not implement what they check. - A billing storyboard passed by building the feature, and a provenance storyboard a detecting seller cannot pass honestly — from September 29 to October 14 the sales card’s two partials were the intentional ones described in the governance entry below.
billing_finality_deliverybuys with apost_sivtmeasurement window, which no product this seller sells guarantees, then expects final delivery data and areport_usagecall. The fix, PR #112 in this site’s private repository (merged 21:33 UTC on October 14, 2026, deployed 22:16 UTC), builds that behaviour without recognizing the fixture: measurement terms are checked against each package’s own product, so the storyboard’s seeded sandbox product is held to the windows it declares while every product this seller sells still rejectspost_sivt; delivery rows carryis_final,finalized_atonce final and a per-packagemeasurement_window; and a newreport_usagetool is idempotent, scoped to the caller’s own buys and takes final numbers on a real buy only for a period that has ended. An owner test through Addie at 22:52 UTC (runner 15.8.1, bundle 3.1.24) read that storyboard 7 of 7 and the card 33 of 82, with 48 untested and one partial. As of 22:30 UTC on October 29 the card reads 34 of 82 on 3.1.30, with no partial or failing storyboard, degraded as it has been since August 11, and the agent is not Verified, for the reason in the eligibility entry above. Its first 3.1.29 grade, a heartbeat at 01:44 UTC on October 18, read 31 of 82, and the fault was ours:provenance_enforcementfell from 6 of 6 to 1 of 6 andprovenance_audit_observationfrom 4 of 4 to 1 of 4, because from 3.1.28 those scenarios seed a fixture-local creative format withseed_creative_format(#8234) and oursync_creativesaccepted only our own formats, so it answeredFORMAT_UNKNOWNbefore any provenance check ran. PR #131 (merged 23:13 UTC on October 18, deployed 23:14 UTC) lets a sandbox sync name a seeded format, with live syncs unchanged, and the heartbeat at 02:58 UTC on October 19 read 33 of 82 again. The remaining partial,provenance_truth_of_claim(1 of 3 through 3.1.29), we would not pass as written, and we filed why as #8199 at 10:49 UTC on October 14: the verifier the runner seeds is a.examplehost that never resolves and both fixture images are 404 or NXDOMAIN, so a seller that runs its own AI detection has nothing to detect and nothing to call, and 3 of 3 needs the seller to tell the two creatives apart by recognizing the fixture, which is what the protocol’s training agent does. It was put on the 3.3.0 milestone at 09:29 UTC on October 15, and #8218, opened by the maintainer at 10:47 UTC that day, closed it when it merged at 08:24 UTC on October 19: it serves real fixture images and a public conformance verifier that answers onlyget_creative_features. PR #132 (merged 15:08 UTC on October 19, deployed 15:10 UTC) has our seller call that verifier, and only it, for a sandbox creative whose ownverify_agentnames it and whose inventory accepts it, and our run ofmain’s storyboards against the production seller readprovenance_truth_of_claim6 of 6 andprovenance_audit_observation7 of 7. On the card the call stayed inert while the grader ran 3.1.29, whose storyboard still names the unreachable sentinel: by #8218’s own description hosted grading picks the fix up only once 3.1.x and 3.2.x patch releases carry it and the grader moves to them. v3.2.6 carries it, but our sales agent serves 3.1; the 3.1.x backport, #8405, merged to the 3.1.x branch at 12:17 UTC on October 22 and shipped in v3.1.30 at 16:34 UTC on October 28; #8561 (merged 20:43 UTC that day) moved hosted 3.1 grading to 3.1.30, and the sales card’s first 3.1.30 grade, a heartbeat at 09:01 UTC on October 29, readprovenance_truth_of_claim3 of 3 and the card 34 of 82, up from 33; the heartbeat at 21:07 UTC that day read the same. - A field the 3.1 schema requires and two governance storyboards never send — on the 3.1 line,
governance_approvedandgovernance_conditionsregister a governance agent on the account, then callcreate_media_buywithoutplan_id, which the request schema from 3.1.20 through v3.1.23 says is “Required when the account hasgovernance_agents”. The seller has no other key for the approval call: the 3.1check_governancerequiresplan_idand tells governance agents to reject a siblingaccountfield (#7547, filed September 15, 2026). The sales agent shipped delegated governance that day —sync_governance, acheck_governancecall before commit,governance_aware: true— and a registration on the shared sandbox test account is permanent, so everycreate_media_buyon that account started to requireplan_id. Six storyboards went red: the two named here; the basemedia_buy_sellerstoryboard, which registers an agent in its own setup phase and then buys withoutplan_id; and three that never ask for governance and only reuse the account. The full local suite (@adcp/sdk13.0.4, bundle 3.1.20) read 246 steps passed and 8 failed, with Reporting & Delivery down to 7 of 17 scenarios. The filing argues that an agent declaringgovernance_awarecannot accept such a buy and skip the call. About an hour and a half after shipping, this one stopped declaring it. One constant,GOVERNANCE_DELEGATION_ENABLED = false, turned off the call and the tool:sync_governancewas offtools/listand refused withUNSUPPORTED_FEATUREuntil October 24, when PR #138 in this site’s private repository (merged 15:43 UTC, deployed 15:44 UTC) brought the tool back as registration only, one governance agent per account and a payload naming more than one rejected, while the constant keeps the call off andgovernance_awarefalse. A test fails ifgovernance_aware, nowfalse, disagrees with the constant. A buy on the registered account now goes through withoutplan_idand without calling the agent, then waits for human approval like every buy on this agent. The suite went to 259 passed and 2 failed (post_sivtandsync_creatives_contradicted, both intentional). Bot-triaged P0 on September 15, at first with two options put to the maintainer: A, addplan_id: "$context.plan_id"to both storyboards; B, movemain’s deprecation ofplan_id, in favour ofgovernance_context, into the next 3.1.x patch. Our follow-up, under an hour after the switch-off, checked A against the shipped 3.1.20 bundle. Each file there has onecontext_outputsblock, owned byget_products_brief, so$context.plan_idis undefined. Each also has async_plansstep, a governance-agent task, without therequires: [multi_agent],default_agent: salesand per-stepagent:routing thatgovernance_deniedandgovernance_denied_recoverycarry. Sosync_plansis routed to the seller, and a seller-only agent skips the plan-setup phase as a missing tool. Both findings are about the 3.1 line. That follow-up, at 01:07 UTC on September 16, did not say so, and a second one at 10:21 UTC the same day did. Onmain, both storyboards have routedsync_plansto a governance agent and exported itsplan_idsince August 8, 2026 (PR #6277, PR #6280), and they sendgovernance_contextwhere the released 3.1 files send neither field — there$context.plan_idis defined, as the triage said. When that second follow-up went up, none of that had reached the 3.1.x branch or v3.1.23, the hosted card graded on bundle 3.1.20, and a 3.1 seller needed the fixes from #6277 and #6280 on 3.1.x under either option. At 10:26 UTC on September 16, 2026 the bot rewrote that triage. It now says the A-or-B framing rested on the 3.1.20 bundle alone, names a backport of #6277 and #6280 as the fix, and leaves the deprecation as a separate policy call. As of 11:25 UTC on September 17, 2026, nobody else has commented on #7547. At 17:09 UTC on September 16, 2026 PR #7555 merged into the 3.1.x branch. It marks the two 3.1 governance storyboardsrequires: [multi_agent]withdefault_agent: sales, routessync_plansto the governance agent and every other step to the seller, givessync_plansacontext_outputsblock that capturesplans[0].plan_id, sends that asplan_id: "$context.plan_id"oncreate_media_buy, and adds a test for the routing and the hand-off. It does not take #6277 and #6280 whole: its description says doing so would also import 3.2-only response fields such ascheck_typeandconsultation_context, which the 3.1 schema-path lints reject, so it carries only the routing andplan_idfixes. The 3.1.xcreate_media_buyrequest schema defines nogovernance_contextfield, and neither patched request sends one. The description says “Closes #7547”, but GitHub acts on that keyword only when a PR merges into the default branch,main, and as of 10:48 UTC on September 17, 2026, #7547 is still open. Nor was the fix released then: there was no v3.1.24 tag, the 3.1.x branch was three commits past v3.1.23, and the open release PR (PR #7559) listed the change under[email protected]. The newest public run on the hosted card at that point, an owner refresh at 09:55 UTC on September 16, 2026, before the merge, graded bundle 3.1.20. Of the storyboards, #7555 touches onlygovernance_approvedandgovernance_conditions. The other four that failed are unchanged on 3.1.x since v3.1.20, and each still callscreate_media_buyfor the brand and operator the governance storyboards register, withoutplan_id. Three of them,delivery_reporting,measurement_accountabilityandvendor_metric_accountability, register no agent and only reuse the account. The fourth, the basemedia_buy_sellerstoryboard, registers one in its owngovernance_setupphase, with no multi-agent requirement, and then buys withoutplan_id: the shape this filing reported, in a storyboard it did not name. Onmainits copy has the same shape and sends nogovernance_context, whichmainrequires once governance applies to the account. #7555 changes none of the four: turning the constant back on would again have the agent refuse those buys for lack ofplan_id, as it did in the local suite before the switch-off — the three while the registration on the shared account stands, andmedia_buy_selleron every run, since its own setup phase registers the agent again. The local suite could not run the patched storyboards at the time: it ran bundle 3.1.20 from@adcp/sdk13.0.4, which carries no later 3.1 bundle; the first stable build to bundle 3.1.24 is 13.1.2 (September 28, 13:54 UTC; npm tagadcp-3.1that day,latestby 10:27 UTC on September 29). The constant stays off; the local suite, re-run on 13.1.2 against the live agent on September 29, read 250 steps passed, 2 failed and 81 skipped, the same two intentional failures, and the hosted grader’s run that night reported the same totals. A follow-up on #7547 at 12:18 UTC on September 17, 2026 raised the base storyboard there, marked as outside that issue’s scope, with the evidence for both branches, and offered to file it separately. The maintainer account answered on September 18, 2026 to file it separately — the base storyboard is a different file, carries none of theplan_idinfrastructure the backport relies on, and has three plausible fix shapes of differing blast radius, so it should not absorb into a P0 thread otherwise ready to close. It is #7585, filed the same day with the per-branch line references: on 3.1.x the file has zero occurrences ofplan_id,sync_plans,multi_agentordefault_agent, and onmainzero ofgovernance_context. The filing concedes what the defect is not: on both branches the obligation is prose in the field description rather than a machine-enforced conditional — top-levelrequiredis the same five fields on each line, and neither schema carries anif/thenordependentRequiredtouching either field — so a schema-validation pass over the storyboard is clean, and it fails only against a seller that enforces the documented requirement. That timing held. The maintainer chose the subtractive option — dropgovernance_setupoutright rather than gate it — and the backport landed as PR #7627 on September 23, 2026, inside the window. Checked in the published v3.1.24 bytes rather than on the branch:governance_setupandsync_governancenow appear zero times in the base storyboard, and the isolated account keymedia-buy-lifecycle.pinnacle-agency.exampleappears six times. #7547 closed completed the same day on its own scope, which was the point of asking to separate them: both named scenarios now sendplan_id, and a separate gate (PR #7625) makes them skip entirely for a seller that declaresmedia_buy.governance_aware: false, as this one does. The v3.1.24 fixes reached the sales card only on September 29, 2026. Until then it read bundle 3.1.20, four releases behind, from owner tests on September 16 and 17 that ran through Recheck & retest (owner-refresh-test sessions) shortly before that control was fenced platform-wide (adcp#7457). A requeue only sets queue position, published heartbeats stayed rare through that window (adcp#7355; by the maintainer’s database counts there, 9 to 29 heartbeats a day ran between September 14 and 28 and 0 to 14 a day were published, most of the rest timeouts), and the interactive path below had not been used on either card before September 29. The grader itself moved on: from September 22 it graded 3.1-line agents on bundle 3.1.23 under @adcp/sdk 14.0.0-rc.40 (heartbeats on other agents on September 22 and 25, owner tests on September 23 and 24), and its runner moved to 14.0.0-rc.49 on September 28, then rc.50 on September 29, rc.51 at 09:07 UTC on September 30, rc.53 from 13:43 UTC that day, the stable 14.0.0 from 17:00 UTC on October 1, 14.2.0 from 16:20 UTC on October 6, 15.0.0 from 10:39 UTC on October 9 (#7968), 15.2.0 from 16:32 UTC that day (#8012), 15.8.1 from 10:36 UTC on October 12 (#8148), 15.9.2 from 10:07 UTC on October 16 (#8231), 15.9.5 from 23:03 UTC on October 17 (#8284), 16.1.0 from 12:06 UTC on October 21 (#8355), 16.2.0 from 03:12 UTC on October 22 (#8393), 16.3.0 from 19:38 UTC that day (#7925), 17.0.0-beta.0 from 03:28 UTC on October 26 (#8489), 17.0.0-beta.2 from 16:22 UTC on October 27 (#8528) and 17.0.0-beta.3 from 17:36 UTC on October 28 (#8550), grading 3.1-line agents on 3.1.24 until #8242 moved them to 3.1.27, whose first public run came at 23:53 UTC on October 16, #8278 to 3.1.29, whose first came at 17:47 UTC on October 17, and #8561 to 3.1.30, whose first came at 23:05 UTC on October 28 (since #7841, merged 14:58 UTC on October 1, an agent that advertises 3.2 is graded on the 3.2 bundle instead: 3.2.1 from the first such run at 21:48 UTC on October 1, 3.2.2 from October 5, 3.2.3 from October 8, 3.2.4 from October 13, 3.2.5 from October 18, 3.2.6 from October 23, 3.2.7 from October 25 and 3.2.8 from October 29). Published heartbeats came back too — in the public history, six on the evening of September 29, nine on September 30 and twenty-one on October 1 (sixteen of them by 20:31 UTC), both of this site’s cards among them (13:49 and 14:54 UTC, both on 3.1.24 under rc.53), against none to two a day over September 22 to 28 — and the public history returns 1,318 checks between October 2 and 22:30 UTC on October 29, a floor, as it returns at most 100 per agent (counting 52 on Triton Digital and 82 on TeqBlaze’s adcp.teqblaze.io, which the public listing has since dropped), 592 of them on 3.1.24, 33 on 3.1.27, 502 on 3.1.29, 24 on 3.1.30 and 157 on the 3.2 bundles, under 14.0.0, then 14.2.0, then 15.0.0 and 15.2.0 from October 9, 15.8.1 from October 12, 15.9.2 from October 16, 15.9.5 from October 17, 16.1.0 to 16.3.0 from October 21 and 22, and the 17.0.0 betas from October 26, with the daily public count falling from 58 on October 3 to 27 on October 11, a decline the maintainer has since called mostly deliberate (eligibility rules and backoff), then rising to 35 on October 12, 44 on October 13 and 55 on October 14 (between 45 and 55 a day from October 15 to 28), this site’s signals card re-graded twice a day through October 8, by an owner test at 17:53 UTC on October 9 under 15.2.0 (passing on a nine-day streak), by heartbeats at 23:08 UTC on October 12 and 11:18 UTC on October 13 under 15.8.1 (both partial, on an output-schema fault of ours), by an owner test at 13:13 UTC on October 13 and heartbeats through 23:41 UTC on October 14 (all passing), by a heartbeat at 11:52 UTC on October 15 (partial, on two signing probes that aborted before signing; see above), by owner tests from 13:57 UTC that day and a heartbeat at 00:00 UTC on October 16 (passing again), by a heartbeat at 13:08 UTC on October 16, its first under 15.9.2 (passing), by an owner test at 13:13 UTC (partial, a collision of two signed grades; see above) and, once the agent served 3.2, by an owner test at 13:58 UTC on the 3.2.4 bundle (passing) and by heartbeats twice a day since, on 3.2.4, then on 3.2.5 from 01:57 UTC on October 18, on 3.2.6 from 15:01 UTC on October 23, on 3.2.7 from 03:17 UTC on October 25 and on 3.2.8 from 03:48 UTC on October 29 (all passing until the heartbeat at 15:51 UTC on October 29, partial on one signing probe; see above), and the sales card at 32 of 82 until an owner test at 22:52 UTC on October 14 read 33 of 82, then at 31 of 82 from its first 3.1.29 grade at 01:44 UTC on October 18 until the heartbeat at 02:58 UTC on October 19 (see the provenance entry above), at 33 of 82 after that but for one heartbeat at 08:53 UTC on October 28 that read 31 of 82 on a grader-side transport failure, per the owner diagnostics (the next, at 20:59 UTC, read 33 again), and at 34 of 82 from its first 3.1.30 grade at 09:01 UTC on October 29; as of 22:30 UTC on October 29 its newest grade is a heartbeat at 21:07 UTC on October 29, on runner 17.0.0-beta.3 and bundle 3.1.30. The sales card went about 53 hours without a completed grade: after its grade at 15:14 UTC on October 6 (under 14.0.0), its heartbeat at 03:47 UTC on October 7 ran out of the grader’s extended 30-minute budget after 58 of 75 storyboards, and the public history shows no completed run until a heartbeat at 20:42 UTC on October 8, under 14.2.0, that took eight and a half minutes; the next, at 09:10 UTC on October 9, took four. The same suite run from here against the live agent finishes in 70 seconds. The first 3.1-line runs under rc.49 followed on the afternoon of September 28: four owner tests on one other agent between 14:10 and 15:29 UTC, every one graded on bundle 3.1.23; the runner moved and the 3.1 bundle did not, and as of 10:30 UTC on September 29 no public run on any of the 34 registered agents had been graded on 3.1.24. Those four runs also show the one owner path the fence leaves open: in the registry source it guards the queued Recheck & retest refresh alone, while Addie’sevaluate_agent_qualitytool, the one #6206 was filed against and also exposed on the AAO MCP endpoint, runs the suite interactively under a 20-minute budget and, for a full run on the default hosted target while the calling organization owns the agent, records it as anowner_testthat the live public card shows once the run completes, with no provenance fence in that path. The first public 3.1.24 grade in the registry, and its first public rc.50 run, came at 20:56 UTC on September 29, from a heartbeat on this site’s signals card. The sales card followed at 23:15 UTC through Addie’sevaluate_agent_quality(aquality-eval-test session on its 20-minute budget): an owner test on bundle 3.1.24 under @adcp/sdk 14.0.0-rc.50, complete and authoritative, with 32 of 82 storyboards passing, 48 untested and two partial, where the card graded on September 17 read 29 passing and seven partial. In that rungovernance_approvedandgovernance_conditionsreaduntested: on 3.1.24 both require a multi-agent run andmedia_buy.governance_aware: true, and this seller declaresfalse. The two partials were the intentional failures from the local suite above,billing_finality_delivery(apost_sivtmeasurement window this seller does not guarantee) andprovenance_truth_of_claim(itssync_creatives_contradictedstep); the first has passed since October 14 withpost_sivtrefused on every product this seller sells (see the billing-finality entry above). The run also reported the live-account phase ofcomply_controller_mode_gatefailing,NOT_FOUNDwhere the storyboard expectsFORBIDDEN, which the card does not count. - A conformance failure you cannot act on —
capability_discoveryfailures reported the scenario name and nothing else: no request, no response, no pointer, no expected against actual (#2352, with the Signals track reported separately as #2356 when a declaredsignalsprotocol was told the track was not applicable). The maintainer wrote the protocol-side runner-output contract in response — #2364 mandates the exact request and response, an RFC 6901json_pointer, machine-readable expected and actual, a fetchableschema_urland six canonical skip reasons — and #2365 populated the signals baseline, which had shipped as a placeholder with no phases, so there had been nothing to run. Both merged the same day. Worth recording what the same thread established about us: the filing argued our agent was correctly implemented, and two of the failures were real non-conformances on our side, a missingadcp.idempotencyblock and nocontext.correlation_idecho. We fixed those. The contribution is the report and the reproduction; the implementation was the maintainer’s. - A second filing withdrawn — an argument that npm-published compliance caches omit the sibling
schemas/directory the resolver needs, leaving an earlier fix inert for anyone on a plainnpm install(#6650, re-filed correctly as adcp-client#2587). The maintainer unpacked the exact tarball and showed the resolver falling through to the package’s own bundled schemas, no sibling required. Retracted the same afternoon. The advisory that prompted it had a different cause entirely — a second, 3.0-shaped validation from the new mixed-version compatibility check — filed separately and fixed within the hour. Two things this one is worth remembering for: the issue still reads completed in the API, because an author self-close with no reason renders that way, and the automated triage posted a confirmed-and-ready-to-implement verdict at P1 four minutes after the issue was closed and under an hour before the human maintainer disproved it. Triage labels on this repository are a bot’s opinion, not a maintainer’s endorsement. - A storyboard that asks for a value the schema rejects, still shipping on 3.1 — the version-negotiation steps in
universal/error-compliance.yamlanderror-compliance-signals.yamltell a seller to answerVERSION_UNSUPPORTEDwithrecovery: fatal, and list the vocabulary as “correctable, transient, or fatal”.core/error.jsontypesrecoveryas["transient", "correctable", "terminal"], sofatalis not a legal value, andenums/error-code.jsonclasses this codecorrectable: re-pin to a supported release and retry. Someone else had already found it and filed it (#7375, September 8, 2026); PR #7376 corrected both lines onmainthe same day and the issue closed. What had not happened was the backport. On September 17, 2026 the 3.1.x source still carried the old prose at lines 302 and 359, and v3.1.23 — published on September 14, six days after the fix landed onmain— shipped it indist/compliance/3.1.23/. That is the line the hosted grader ran, on bundle 3.1.20, until September 22, 2026, when it moved 3.1-line agents to 3.1.23. A comment on the closed issue that day set out the two branch states, the release timeline and the enum, and noted the grade is unaffected either way, because nothing in the SDK parses theexpected:prose — only thevalidations:entries decide pass or fail. Both of this site’s agents had the defect the prose would have caused, in opposite directions: the sales agent answered an unsupported pin withrecovery: "terminal", which on 3.1 means the buyer needs a human, and the signals adaptor sent norecoveryat all, where 3.1 says senders SHOULD populate it on every error. Both now answercorrectable, verified live. Seven minutes after that comment, PR #7572 was opened against3.1.xcarrying the same two-line change plus a changeset, described as ready for the 3.1.24 window. Like the other triages on this page it was bot-drafted under the maintainer account, and it sat as a draft — unmergeable by definition, with no reviewer requested — for over a day while the release PR queued two other changesets and not this one. A comment on September 18, 2026 naming the draft flag as the mechanical blocker drew a reply three minutes later undrafting it, a merge at 23:38 UTC, and the release PR regenerating fifteen minutes after that to consume the changeset. On the 3.1.x branchrecovery: fatalnow appears zero times in either file; the general error-shape line reads “transient, correctable, or terminal”, which is the enum the schema actually defines. v3.1.24 was tagged on September 23, 2026, so the fix is released rather than queued. The audit that followed on this side found the defect was a class rather than an instance: the sales agent’sToolErrorconstructor defaultsrecoverytoterminal, and its own doc comment named only two of the three legal values, so 16 sites across seven codes inherited the most severe classification for errors a buyer could have fixed — a wrong media buy id, a malformed request, a governance denial explained as “over budget”. Each was re-judged against what its call site actually does, and three were keptterminalon the evidence: one internal invariant on an id the buyer never supplies, and two admin-console paths where the field never reaches the wire. Those three are now pinned explicitly rather than inherited. The denial case also now forwards the governance agent’sfindings, whichcheck-governance-responserequires when the verdict is denied: calling an error correctable while dropping the reason leaves the buyer guessing what to change. - A filing that was right about the symptom and wrong about the cause — the signals agent’s hosted card showed
error_handlingat 10 of 11 scenarios. The failure was not an error-handling defect at all:billing_gate_dispatchassertsfield_present: account.supported_billingin an ungated first phase, and that field isrequiredwithminItems: 1, so there is no honest value for an agent with no account management. Its own description says buyers pass one of those values insync_accounts— a tool this agent does not expose. Declaring it to pass would have been thegovernance_awaremistake again. Filed as #7573 on September 18, 2026, bot-triaged P1 within eight minutes with a primary fix in the SDK and a defensive one in the storyboard. Two corrections followed, both ours. One line of the filing said the divergence only appeared when a track was forced; that was true of SDK 13.0.2 and false of the pinned 13.0.4, carried forward without re-checking. The larger one: the triage’s primary fix was a no-op. The runner already enforcedrequired_toolsin every mode — atcomply.js:600for capability-driven selection and again atrunner.js:1420at load time — as an any-of gate, which the bundled storyboard schema documents as deliberate and pairs with a strictrequired_any_of_toolsform that almost nothing uses. Both storyboards were admitted on one matching tool:get_adcp_capabilities, which every agent has, andcomply_test_controller. Searching only this protocol’s repository before filing had also missed two earlier filings of ours in the client repository, making this the third occurrence — and the any-of convergence that admits these storyboards shipped in 13.0.4 because we asked for it. The fix that landed, PR #7581, took the defensive route on September 18, 2026:optional: trueplusrequires_capability: { path: account, present: true }, with a runner test. It is onmainonly. The finding that mattered came afterwards. Therequired_toolscorrections had already shipped upstream — across storyboards on August 23 and for the other file on September 14 — and are in the released v3.1.23 bundle. Running the SDK’s ownresolveStoryboardsForCapabilitiesandpartitionStoryboardsByRequiredToolsagainst each bundle in turn: on 3.1.20 both storyboards are runnable, on 3.1.23 both are excluded for missingsync_accountsandvalidate_inputrespectively — 19 runnable storyboards and 171 steps down to 17 and 149, with nothing newly selected. So the storyboard defect had largely been fixed weeks earlier, and what remained — until September 22, 2026 — was that the hosted grader was pinned to bundle 3.1.20 (this site’s two cards read it until September 29, when a heartbeat re-graded the signals card at 20:56 UTC and an owner test re-graded the sales card at 23:15 UTC, both on 3.1.24; the grader graded 3.1-line agents on 3.1.23 from September 22 and on 3.1.24 by September 29). That is a registry-side pin, not a protocol matter, and it was raised there rather than on the issue. Excluded is not passed: those storyboards leave the denominator as not-applicable rather than becoming passing steps. - An idempotency key that dies with the row it protects — the recovery audit left one item out of scope:
buyResponseanswers a missed read-back withMEDIA_BUY_NOT_FOUND, on an id every caller derives server-side, so there is nothing in the request for the buyer to verify. The trigger is a console delete landing between the caller’s own read and that one. D1 has no interactive transactions and the purge’sbatch()is atomic only with itself, so nothing serialises the two. Three interleavings reproduce. The narrowest is the create path, since a fresh buy is writtenpending_approval, which sits in neither delete guard and so needs a decide-then-purge pair inside the window; the widest isget_media_buys, where one purged row fails the entire list and the sandbox sweep in the promotion cron carries no status guard at all. The first fix answeredCONFLICTwithrecovery: transientand was wrong.core/error.jsondefinestransientas “retry after delay”, the purge takes the row’sUNIQUEidempotency_keywith it, and a buyer following that instruction writes a second buy instead of replaying the first. Checked against the running implementation rather than argued: the same key after a purge returns a newmedia_buy_id. Create now answersterminalon both of its call sites, and the read paths staytransient, where a retry does converge. The half the spec owns went upstream (#7575, filed September 17, 2026):replay_ttl_secondsscopes the replay window purely in time, minimum 3600, and enumerates three outcomes, being the cached response,IDEMPOTENCY_CONFLICTandIDEMPOTENCY_EXPIRED. None of them is the fourth that any seller keying idempotency to the resource row will produce, where the key dies with the resource and a replay inside the advertised window creates a second one and returns success.CONFLICTis no better a fit: the schema scopes it to revision mismatch, anderror-details/conflict.jsoncarriesexpected_versionandcurrent_version, neither of which a deleted row has. - A test-scenario report that was out of date when filed — reported on April 27, 2026 that the SDK’s signals test scenario (
scenarios/signals.js) calledactivate_signalwithsignal_id, carrying aSignalIDobject, where the schema requires asignal_agent_segment_idstring (#3349, filed in the protocol repository under a header that already namedadcp-clientas its subproject, and re-filed there three minutes later as adcp-client#1033). The field name had been corrected on March 21, 2026 and released that day in@adcp/client4.13.0, five weeks before the filing. The object half was never true: even before that fix, the value passed was a string. The two bot triages disagreed: the one on #3349 accepted the premise, and the one on adcp-client#1033, fifteen minutes later, found the report false against the then-current 5.21.1. The client-repo copy was closed on April 30, 2026 as already fixed. This one was closed as not planned on May 19, 2026, with a comment that the spec storyboards already use the correct fields and the report belongs toadcp-client. Two merged PRs came out of the bot triages, and neither is the fix the filing asked for: the #3349 triage, taking the report at its word, drafted the removal of training-agent aliases that still accepted the old field names, with sharper schema descriptions (PR #3356, merged May 14, 2026), and the adcp-client#1033 triage drafted a fix for a separate bug it found in the same scenario, a governance advisory check that never fired (adcp-client#1036, merged April 28, 2026).
Open proposals
- Geo cell interoperability — conversion (#6013) and cell-grain delivery reporting (#6014) companions to the geo_cells RFC (#5969), with Nathan Woodman.
- Static OOH: the channel with no play event — static out-of-home has nothing to emit an impression against, so proof-of-posting has no shape in the delivery contract; filed with a measurement shape and posting-record proposal (#6140). The channel contract it asked for merged —
ooh_metrics, posting records and a channel guide (PR #6241) — and the measurement-shape half is still open. - A seller shape the taxonomy has no slot for — the specialisms treat guaranteed-vs-non-guaranteed as a proxy for human-approval-vs-auction, so a publisher-direct seller running fixed-price, non-guaranteed inventory behind a human approval gate matches neither:
sales-non-guaranteedassumes bidding and states that buys need no human approval, whilesales-guaranteeddescribes the human gate but assumes IO-signed reserved inventory and proposal flows (#6303, filed with our own live agent as the reproduction after withdrawing a claim we couldn't honestly certify). On October 6, 2026 the maintainer took it to the working group with a decision brief and a draft, #7963: a new specialism proposed for 3.3,sales-fixed-rate, for fixed-price, non-guaranteed sellers, with optional review-hold phases graded only when a seller advertises a newforce_media_buy_confirmationcontroller scenario. We accepted it, name included, over thesales-publisher-directwe had asked for: a specialism should name a mechanic a runner can test, not who the seller is. Run against our live agent on October 7, the draft’s product discovery and create phases pass, the bid-free create comes back held withconfirmed_at: null, the three hold phases skip because we do not advertise the scenario yet, and the only failure is the capabilities check, which expectssales-fixed-rateamong our declared specialisms: a specialism proposed for 3.3 that we cannot declare while we serve 3.1. Our reply also records the gap on our side: ourpending_creativesstill means awaiting our review, so status has to follow creative readiness before we advertise the hold scenario. On October 9 the draft marked the specialism preview, likesales-exchange, until the working group ratifies the name and the hold gating, and recorded one narrowing of existing text: a held buy whose flight start passes stayspending_startuntil it is confirmed or rejected, so the rule that a seller movespending_starttoactiveat flight start would apply only to committed buys. On October 19 the working group approved the specialism as drafted, and #7963 merged tomainat 19:51 UTC on October 20 withsales-fixed-ratestill preview, so it earns no badge until it is marked stable. Our sales agent’s support for it ships behind a switch,SALES_FIXED_RATE, that is off (PR #137 in this site’s private repository, merged 00:56 UTC on October 21 and deployed 00:57 UTC); with the switch on, a local run ofmain’ssales_fixed_ratestoryboard passes 40 of its 43 steps, skips two and fails one, the capabilities step, because the 3.1 list of specialisms has nosales-fixed-rate. On October 24 the maintainer opened #8445, a draft that promotes the specialism to stable for 3.3 GA, with two gates: the runner fix for adcp-client#3181, which has shipped in@adcp/sdk16.5.0 and 17.0.0-beta.0, and an independent run on the 3.3 bundle. We posted ours on #6303 at 09:34 UTC on October 27: withSALES_FIXED_RATEand a second switch,SERVE_ADCP_3_3, on (PR #139, merged 09:32 UTC that day and deployed 09:33 UTC; the switch advertises"3.3-beta.0"on the 3.2 behaviour line), a local run of the storyboard and the four storyboards graded with it, on the 3.3 bundle under 17.0.0-beta.0, passed 44 of 45 steps, skipped one (pending_creatives_to_start, not applicable to a seller that reviews every creative by hand) and failed none, the capabilities step now validating against the 3.3 schema. PR #144 (merged 11:47 UTC on October 27, deployed 11:49 UTC) adds two 3.3-only behaviours behind the same switch:ACCOUNT_NOT_FOUNDfor an account reference that was never provisioned, and anexpected_confirmation_at24 hours after creation on a held buy. Both switches are off in production, which advertises only "3.1", and #8445 is still a draft as of 22:30 UTC on October 29. The certification contradiction found alongside it is already fixed: the docs stated that grading neither requires nor usescomply_test_controller, while specialism certification requiresseed_*scenarios only that controller can execute — confirmed and scoped to the storyboards it actually covers, since the exemption holds for universal and protocol runs but not for specialisms that seed state (#6301, fixed). - revoke_activation revocation primitive (#4203) · Measurement Feedback Signal (#4296).
- JWKS adoption baseline — the chain-of-custody gate on signal-payment work (#4206).
Not protocol work — bug reports and a feature request against the AAO platform
- Shipped as filed, one of them only in part — the member profile edit page threw
parsed is not definedon load, a one-line scoping error (#4257), and photo uploads on the same page reverted silently on save, from two compounding causes: the upload handler wrote a base64 data URL the brand-identity endpoint rejects, and the save never sent the photo field (#4260). Both were fixed in PR #4352, merged May 10, 2026, which references neither issue; its description credits my in-app escalations of the same two defects, and both issues were closed on May 12 with a comment pointing to it. Badge links of the form/registry/agents/{encoded-url}returnedCannot GET(#5485) until PR #5492, a five-issue batch merged June 12, 2026, served that path as a public agent profile page. A “0 / 0 Community seats” row on a Professional plan, which the filing itself called display-only (#5494), was hidden on plans without Community seats by a small conditional (PR #5586, merged June 18, 2026). A copy button for replies in AAO’s Addie assistant shipped (#4266 → PR #4269) without the plain-text/markdown toggle the request also asked for, which the bot triage declined and the PR left out of scope. - Wrong or partial diagnoses, and one wrong report — the dashboard’s Test view listed media-buy storyboards for a signals-only agent; filed as missing capability filtering, but the filter already existed, and a fallback to the full catalog, taken when the platform’s capability probe of my agent failed for a reason the record does not give, bypassed it. The fix drops the fallback and shows the probe error, with a pointer to what
get_adcp_capabilitiesmust return, rather than the filtered list the filing expected (#4254 → PR #4369). The member directory showed “Tools: 0” for an agent with eight tools; filed as a stale comply cache, a theory the fixing PR calls incorrect: the public discovery endpoint probed the agent live and left the tool list out of its response (#4256 → PR #4261). The organization page offered “Register an agent” to an org that had one; the filing guessed the page was not querying the registry or was filtering on a stale comply cache, and the health service in fact returned a hard-coded agent count of zero (#4255 → PR #4258). Listing the org’s agents on that page, the filing’s first expected behaviour and its whole suggested fix, was left out of scope by the bot triage and the PR. Addie kept reporting GitHub as unconnected for a turn after OAuth completed; the bot triage and the fixing PR found the token lookup already ran live on every tool call, which rules out the filing’s caching guesses, and put the lag down to propagation delay in the upstream WorkOS API, which none of the filing’s three guesses named. The fix is a single 1.5-second retry that absorbs that window, a note in the not-connected reply that the connection may still be propagating, and a prompt rule that stops Addie offering the Connect link again right after the user says it is connected; none of it is a change to connection state (#4348 → PR #4349). And one report was wrong. A community profile photo picker, on a different page from #4260, reported as broken, was the reference-photo input for AI portrait generation: the filing’s own steps name it (“Upload a reference photo”), and the bot triage found its hint reads “Used as reference only, not stored” and that the avatar changes only after Generate and Approve. That triage put a choice between a direct upload path and clearer labels to the maintainer, and the merged fix built the upload path (#4658 → PR #5157, merged May 29, 2026). - Registry & docs reliability — registry UI rendering empty product/format sections for anonymous viewers despite working public endpoints (#6229, fixed and closed; my own duplicate of it, #6230, filed 57 seconds later, drew a bot triage the same day that surfaced a third break in publisher discovery; I carried that finding across when I closed #6230 as a duplicate on August 6, 2026, which GitHub records as not planned, and it was fixed in the same change) and the dashboard's aao-verified doc links returning 404 (#6231, fixed and closed). The registry card's "Publishers & properties" panel surfacing a raw JSON-parse exception instead of the actual authorization-list cause (#7333) is fixed too — PR #7362 now indexes publisher authorization data, guards invalid JSON responses, and renders clean 404/error states.
Contribution statuses validated September 16, 2026 against the public AdCP repository; open proposals are point-in-time and may merge, change, or close. Maintainer-authored work is credited as review influence, not authorship.
Protocol domains.
AdCP organizes work into domain-specific tasks and schemas. Task names below are drawn from current official documentation.
-
Media Buy
Inventory discovery, campaign creation, delivery reporting.
get_products · create_media_buy · get_media_buy_delivery -
Creative
Creative format discovery, building, preview, distribution.
build_creative · preview_creative · list_creative_formats -
Signals
Audience and contextual data discovery and activation.
get_signals · activate_signal -
Accounts
Commercial identity, billing, and usage reporting.
list_accounts · sync_accounts · report_usage -
Governance
Brand suitability, approval, policy, audit, content standards.
create_content_standards · calibrate_content -
Brand
Machine-readable brand identity and authorized agents via brand.json.
brand.json discovery -
Sponsored Intelligence
Conversational brand experiences on AI platforms.
Experimental surface in AdCP 3.0 -
Curation
Media inventory curation.
Listed "coming soon" — not a current domain
From brief to governed execution.
- Discover authorized agents — adagents.json, brand.json, registry
- Set up account context — commercial identity, billing, advertiser / operator relationship
- Discover inventory and products — natural-language brief into structured product responses
- Build or sync creative — formats, assets, brand identity, approvals
- Add signals — audiences, suppression, contextual signals, signal activation
- Check governance — budget, brand safety, targeting rules, approvals
- Execute, report, and audit — media buy, delivery, events, audit logs
Discovery starts from two machine-readable files — a publisher's adagents.json (who may sell its inventory) and a brand's brand.json (who may act for the brand, and how):
{
"publisher": "example-publisher.com",
"authorized_agents": [
{
"url": "https://sales-agent.example-ssp.com",
"role": "seller",
"properties": ["news", "sports"]
}
],
"signing_keys": [
{ "kid": "2026-key-1", "alg": "Ed25519" }
]
} {
"brand": "example-brand.com",
"display_name": "Example Brand",
"authorized_agents": [
{ "url": "https://agent.example-buyer.com", "role": "buyer" }
],
"identity": {
"logos": ["/brand/logo.svg"],
"colors": ["#1a1a1a", "var(--accent)"],
"tone": "confident, plain-spoken"
},
"policy": { "restricted_categories": ["gambling", "politics"] }
} Signals: where AdCP meets signal containerization.
The signals layer is where AdCP becomes especially important for agentic advertising. Agents need to discover signals, understand what they mean, check provenance and policy, activate them, and monitor status. That is the same operating problem behind signal containerization — turning audience logic into a governed, executable object.
Discover
What signals exist and what do they mean?
Inspect
Where did the signal come from, how fresh is it, and what policy applies?
Activate
Where can the signal run: seller, platform, identity path, contextual path, or measurement layer?
Monitor
What is the activation status and what outcome did it improve?
An agent discovers signals with a natural-language brief, then activates the chosen signal to a destination — a single get_signals call rather than a taxonomy lookup:
{
"tool": "get_signals",
"arguments": {
"brief": "in-market for premium CTV, US households, privacy-safe",
"deliver_to": { "platform": "dsp", "account": "acct_123" },
"max_results": 10
}
} One open question now sits under this layer. The vectors agents exchange today are text-family embeddings — they encode the words that describe an audience or a brief, and text models famously lose numbers, geography, and time. A second family, spatiotemporal embeddings, encodes the place and hour themselves. A text space and a place-time space do not share a geometry: similarity across the two is undefined until someone defines it — a signals-standards question, exactly the kind of definition a signals working group exists to produce, not an integration ticket.
Governance is the trust layer.
Agentic advertising only works if actions are accountable. A protocol needs to know who called a tool, what authority they had, what was approved, what conditions applied, and what actually ran.
- Human-in-the-loop approvals (a three-party separation of duties)
- Idempotency keys on every mutating request
- Request signing (RFC 9421 HTTP Message Signatures)
- Correlated audit logs
- Governance checks and signed governance tokens
- Brand-safety and content-standard enforcement
- Budget thresholds and authority / role boundaries
The principle
Human judgment is embedded in the system design, not bolted on afterward — the human stays the locus of accountability.
Where AdCP fits with IAB Tech Lab standards.
AdCP is best understood as an agentic management and workflow layer. IAB Tech Lab's AAMP work extends existing standards with agentic foundations, protocols, and trust infrastructure — and its buyer and seller reference agents do workflow-layer work of their own (briefs, planning, negotiation, OpenDirect booking); ARTF focuses on containerized real-time execution; Agentic Audiences on embedding-based signal exchange. These layers can be complementary with clear boundaries — the full repository-level comparison, verified against both codebases, is in AdCP vs AAMP: The Retrofit and the Greenfield. See also the IAB Agentic Standards guide. The protocol's sell side already has a shipping open-source implementation — Prebid.org took stewardship of AdCP's sell-side reference code in January 2026, dissected in The Prebid Sales Agent — and landing inside the publisher execution layer documented in Prebid & Header Bidding Infrastructure.
| Initiative | Role | Best question |
|---|---|---|
| AdCP | Agent task / workflow language | What should the agent ask or do? |
| AAMP | IAB Tech Lab umbrella — buyer/seller reference agents plus foundations and trust | How do agents run briefs, negotiation and booking on the standards already in production? |
| ARTF | Containerized real-time execution framework | How can service agents run inside host infrastructure with low latency and controlled mutation? |
| Agentic Audiences | Embedding-based signal exchange | How do agents exchange identity, contextual, and reinforcement signals? |
| OpenRTB / AdCOM / Deals API / OpenDirect | Existing transaction and object standards | What underlying objects and transaction models should agentic systems reference? |
Implementation lens by company type.
Pick your company type to see what to expose or consume — and the AdCP domains it touches first.
Authorized agents, inventory products, packages, account setup, governance, reporting.
Partner discovery, product discovery, creative formats, media buy, governance, reporting.
Signal catalog, provenance, activation paths, pricing, policy, status.
Seller agents, deals, signals, governance, a real-time execution bridge, reporting.
Buyer agents, media buys, creative, signal activation, governance, measurement.
Formats, asset generation, preview, brand identity, approval workflow.
Delivery, event logging, attribution, audit, lift, governed outputs.
A protocol defines the task. It does not replace judgment.
The industry should avoid treating AdCP as a magic layer that makes agents safe by default. The protocol can define the task. It cannot replace commercial judgment, governance design, data rights, measurement discipline, or operating ownership.
- Start with one workflow, not every workflow.
- Expose the safest useful task first.
- Design governance before spend movement.
- Treat signals as governed objects, not just segment IDs.
- Build audit and status from day one.
- Connect protocol work to business outcomes, not demos.
Where this fits in the full standards stack.
The layer map
AdCP defines the agent workflow layer. It still needs transaction rails, privacy constraints, measurement trust, and evidence discipline around it.
Building around AdCP or agentic advertising standards?
Use the playbooks to map the workflow, governance, signal model, implementation path, and commercial productization before building against the spec.