Standards & Protocols Reference

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 Protocol Stack — agents on top, a governed task layer at the centre, transport, discovery and execution surfaces below. PROTOCOL STACK AdCP Protocol Stack Buyer, seller and operator agents — the actors that initiate and fulfil tasks across the stack. ACTORS Buyer · seller · operator agents The AdCP task layer — the one standard surface where work is named, run and governed. This is the heart of the stack. TASK LAYER AdCP — the task layer Media Buy — an AdCP task family exposed through the same discoverable, governed interface. Media Buy Creative — an AdCP task family exposed through the same discoverable, governed interface. Creative Signals — an AdCP task family exposed through the same discoverable, governed interface. Signals Accounts — an AdCP task family exposed through the same discoverable, governed interface. Accounts Brand — an AdCP task family exposed through the same discoverable, governed interface. Brand Governance — an AdCP task family exposed through the same discoverable, governed interface. Governance Sponsored Intelligence — an AdCP task family exposed through the same discoverable, governed interface. Sponsored Intelligence Transport — AdCP rides existing agent protocols (MCP and A2A) rather than inventing a new wire format. TRANSPORT MCP · A2A Discovery and identity — adagents.json, brand.json and a registry let agents find and trust each other before any task runs. DISCOVERY & IDENTITY adagents.json · brand.json · registry Execution surfaces — the systems AdCP tasks actually drive: publisher, SSP, DSP, creative system, data provider and measurement. EXECUTION SURFACES publisher · SSP · DSP · creative system · data provider · measurement Governance rail — the controls that hold across every layer: approval, idempotency, signatures, audit and human judgment. GOVERNANCE approvalidempotencysignaturesaudithuman judgment AdCP is the task layer — discoverable, executable, governable, auditable.
AdCP sits over MCP / A2A transport: a task layer above execution surfaces, with a governance rail.

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.

Standard readiness (validate before build)
  • AdCP 3.1 GA · v3.1.23 · Sep 2026
  • AdCP 3.2 rc.3 · dev & staging only · Sep 14, 2026
  • AdCP 3.0 prior line · wire-compatible
  • v2 deprecated Aug 1, 2026
  • Sponsored Intelligence experimental

Fast read

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.1 remains the general-availability line and the production recommendation; the latest stable tag is v3.1.23 (GitHub, September 14, 2026), whose #7406 backports the <code>required_tools</code> OR-gate fixes to 3.1.x: <code>canonical_format_validate_input</code> drops <code>comply_test_controller</code> (filed as #7404; #7405 on main), and four storyboards drop <code>get_adcp_capabilities</code> (the applicable subset of #6774). #7451 corrects the v3.1.22 release record to name the request-aware cache partitioning that shipped without changelog coverage. The 3.2 line is at v3.2.0-rc.3 (September 14, 2026), which the official versions page marks development and staging only — pin "3.2-rc.3" solely against peers that advertise it, and keep production on wire pin "3.1". The versions page still says no SDK embeds rc.3; that lapsed on September 15, 2026, when @adcp/sdk 14.0.0-rc.37 and Python adcp 8.0.0b15 both shipped declaring ADCP_VERSION 3.2.0-rc.3. Only Go adcp/v3.2.1 remains on rc.1. The docs are explicit that patch components are never sent. 3.1 is additive over 3.0 and supported patches do not change stable wire surfaces, so a 3.0 integration keeps working with no code change; v3.1.22 did change the explicitly experimental TMP contract, under the emergency exception to the normal notice process. v3.1.3 was withdrawn days after shipping (see the version-watch section below). Validate implementation details against current official docs.
Best next step
Understand the domains, then decide which workflow your company should expose or consume first.
Definition

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.

LayerJobExample
TransportCarries messagesMCP / A2A
AdCPDefines advertising tasks and schemasget_products, create_media_buy, get_signals, activate_signal
Execution systemPerforms the actionpublisher, SSP, DSP, creative system, data provider
GovernanceControls authority and auditapprovals, signatures, idempotency, audit logs
Existing ad standardsPower underlying objects and transactionsOpenRTB, AdCOM, OpenDirect, Deals API
Rationale

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.

Version watch

What’s new in 3.1 — and what 3.2 changes.

AdCP 3.1 is now the current general-availability line (latest stable tag v3.1.23, with the 3.2 line now at rc.3 for development and staging only), additive over 3.0. The 3.2 line has been in release candidate since September 3, 2026 and moved three times in eleven days, most recently to v3.2.0-rc.3 on September 14 — which the official versions page restricts to development and staging. Separate what shipped from the obligations adoption brings from what 3.2 is about to change.

GA · v3.1.23 — additive over 3.0 · next line at v3.2.0-rc.3

Confirmed — AdCP 3.1 (GA · v3.1.23)

  • AdCP 3.1 is the current general-availability line — GA at GitHub tag v3.1.0 (June 18, 2026), patched through v3.1.2 (June 30 – July 8, 2026; protocol-only fixes and spec clarifications). v3.1.3 (July 13) was withdrawn days later — official notice: it added a stable protocol schema field in a patch release, violating the 3.1.x versioning contract, plus a publisher-domain filter bug accepting a plural selector shape the schema rejects; the docs still route implementers around it to v3.1.2. Patches then ran from v3.1.4 (July 19) to v3.1.23 (September 14, 2026, the latest stable tag). v3.1.21 (September 9) was a single change, #7384 (the 3.1.x backport of #7383): guaranteed idempotency replay scoped to state-mutating requests, an optional idempotency_key on the get_products request schema, read wrappers tolerating the key without claiming read replay, and the read-tool-idempotency storyboard relaxed to match — withdrawing the 3.1-era plan to make reads reject a missing key at 3.2. The v3.1.22 changelog lists two: legacy format asset declarations aligned with the canonical asset union, guarding individual and repeatable-group variants against drift (#7423), and a Trusted Match privacy correction restricting Context Match embeddings derived from non-public single-user content while requiring privacy reduction for free-form context signals (#7424 — the 3.1.x backport of #7396, which this site’s author filed as #7393). Treat that changelog as accurate but not complete: the tag also carries #7425 (commit 251df51), which backports the request-aware cache partitioning from #7397 to 3.1.x and carried no changeset, so the changelog has no line for it. #7451 merged on September 13, 2026 to correct the v3.1.22 record, and it landed the next day in v3.1.23 as commit 8a933df. Every 3.1.x release body on GitHub except the withdrawn v3.1.3’s is version-only — literally the string "AdCP 3.1.20" — so per-patch detail lives in the repo changelog, not the release notes. The 3.2 line is now at v3.2.0-rc.3 (September 14, 2026), the current signed protocol checkpoint, restricted to development and staging. The SDKs caught up within a day: @adcp/sdk 14.0.0-rc.37 (September 15, 02:39Z) and its successor rc.38 declare ADCP_VERSION 3.2.0-rc.3 and bundle the rc.3 compliance cache and schemas, Python adcp 8.0.0b15 (02:33Z the same morning) ships ADCP_VERSION 3.2.0-rc.3, and only Go adcp/v3.2.1 still embeds rc.1. The versions page on main still names TypeScript RC.35, Python beta.14 and Go 3.2.1 as all embedding rc.1 and says no SDK embeds rc.3 — held there on purpose rather than merely stale: with Go on rc.1 there is no pin common to all three SDKs, so the maintainers are waiting to align the doc once against rc.3 instead of twice, and the Go catch-up is tracked in adcp-go#536. The rendered docs site has published no rc.3 snapshot at all. The wire-negotiation pin is the string "3.1"; SDKs normalize "3.1.x" → "3.1", and the docs forbid sending patch components. v2 is fully deprecated on August 1, 2026 (UTC).
  • Additive over 3.0 at the wire level: no required field was removed and no shape broke, so a 3.0-conformant integration keeps working with no code change.
  • A production-hardening release: release-precision version negotiation (supported_versions[]), distributed and cryptographically signed brand trust (self-published brand.json + signed verify_brand_claim), canonical creative formats plus a transformers catalog, and vendor-attested measurement goals.
  • Operational guarantees: idempotency_key required on every state-mutating request (v3.1.21 walked back the every-request rule — reads keep the key optional and wrappers must tolerate it); dependency-impact / media-buy health signals, buyer-side webhook observability, and reporting finality + reach-window semantics.
  • Feature detail is sourced from the official "What’s New in 3.1" and 3.0→3.1 migration docs plus the SDK notes — the GitHub release records only the version and date.

Obligations when you claim 3.1

  • Advertise supported_versions in get_adcp_capabilities and echo the served version at the envelope root; transports must tolerate unknown envelope-root fields.
  • Brand agents implementing verify_brand_claim(s) must return a signed (JWS) response.
  • Move creative metadata to canonical format_kind + list_transformers, and pass the 3.1 compliance storyboard bundle to earn the 3.1 badge.
  • Schema tightening can surface as errors: trademarks[].status is now a typed enum and trademarks[].countries must be ISO 3166-1 alpha-2 (former free-text values fail); cache_scope is schema-required on get_products / get_signals.
  • Authentication hardens: RFC 9421 request signing — recommended in 3.0 — becomes REQUIRED for mutating and financial operations in 3.1+, with static bearer/Basic credentials prohibited there (still permitted for read/discovery). Until you land on 3.1, bearer-over-TLS remains the effective floor for spend operations — the spec says to ship 9421 ahead of the cutover.
  • The Go SDK (adcp-go) has breaking type changes — plan the upgrade; it is not a drop-in.

In release candidate — AdCP 3.2 (v3.2.0-rc.3)

  • The 3.2 line has been in release candidate since September 3, 2026 and moved three times in eleven days — rc.1 on September 5, rc.2 on September 13, rc.3 on September 14. The docs call rc.3 the current signed protocol checkpoint and restrict it to development and staging — pin "3.2-rc.3" only against peers that advertise it and keep production on wire pin "3.1". The SDKs caught up on September 15: @adcp/sdk 14.0.0-rc.37 and rc.38 declare ADCP_VERSION 3.2.0-rc.3, Python adcp 8.0.0b15 embeds it too, and only Go adcp/v3.2.1 still embeds rc.1. The versions page has not, and says so on purpose: with Go still on rc.1 there is no pin common to all three SDKs, so it aligns once when Go lands (adcp-go#536).
  • Reliable Reporting becomes a discoverable capability rather than an assumption: a seller advertises it, so a buyer can tell before spending whether the reporting contract is one it can hold the seller to.
  • Vendor measurement gets a closed vendor_relationship enum — first_party / affiliated / third_party — so a seller has to declare when it is its own measurement vendor. Absence means undeclared, not independent.
  • Delivery and reach semantics tighten: cross-buy delivery aggregates are deprecated in favour of media-buy-level currency, and deduplicated cross-buy reach is disambiguated from summed reach so the two stop being reported as the same number.
  • The scheduled removal lands here — legacy top-level media-buy status moves to media_buy_status — and typed validation and precedence for pixel-ratio registry metadata begins in 3.2, where 3.1 SDKs only preserve it.

Watch next (4.0 and beyond)

  • A 4.0 deprecation set: Format.pricing_options, legacy SignalId / flat signal targeting, inline brands[], data_provider_signals.
  • Flagged experimental in 3.1, not yet stable: the measurement-vendor catalog (measurement.core) and the responsive_creative / sponsored_placement / agent_placement format kinds.
  • A proposed pre-GA rename of body-level status discriminators (e.g. check_governance) was discussed (#4897) but is unconfirmed in the final tag — validate before relying on it.
  • AdCP Verified independent auditing, signals-protocol maturity, registry adoption, SDKs, and reference implementations.

Migrating 3.0 → 3.1 — the checklist

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.
From the implementer seat

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 and 3.1 GA suites (57/57 scenarios on the current 3.1.20 line), 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 (#5248PR #5249, in the 3.1.0 line since rc.7).
  • Root-cause diagnostics for the conformance runner — a schema-invalid get_adcp_capabilities response used to surface as N unrelated track failures with nothing linking them; reported it (#6254) and wrote the protocol-side contract, which now defines the capabilities_response_schema_invalid notice and an RFC 6901 capability_pointer field 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.embedding is the one field on Trusted Match's classified-output tier that can be partially inverted back to a user's own words, and on an ai_assistant property 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 on keywords and summary that 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 $schema and request_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. The cache_ttl: 0 line 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 as cache_namespace, 3.2-only, with October 25, 2026 as the earliest date its enforcement can publish, and is still open as of September 16, 2026.
  • 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_source plus nielsen_audio notation (#6139), and spot-level as-run for linear (#5682). Every ratified decision is merged — measurement_source on delivery rows (PR #6241), nielsen_audio with 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.

Conformance suite, hardened

  • 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 satisfywebhook_emission's synthetic assert_contribution step grades a branch set whose only contributors require get_products; a signals-only agent's contributors correctly skip as missing_tool, and the assertion then fails on the empty set as a [core] failure nothing on the agent side can clear. The storyboard's own grading.pass_criteria says those steps grade not-applicable, and a sibling storyboard already guards its assertion phase with skip_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 on media_buy.buying_modes carrying wholesale and added a regression asserting that a signals-only agent grades the whole branch family not_applicable — the grading contract the filing argued from. Note where it landed: the runner's assert_contribution behaviour 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 a fields selector on get_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 on main as 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_scope layers; 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_refresh parameter 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_accounts silently 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/sdk 13.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, sales included (#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 the onerror embed 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 advertised supported_versions on 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_products call against the same database in the same minute returns the correct USD-pruned result with filters.pricing_currencies present 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 surfaced brand sent on list_accounts while 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 dropped sandbox flag 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: advisory and 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 declares supported_billing: ['operator'] is asked for an error code that means the opposite of what is true — BILLING_NOT_PERMITTED_FOR_AGENT implies 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.12 while no published SDK carried it: latest shipped 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 advertised supported_versions to 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_tools is present, while the suite selector then required all of them, so canonical_format_validate_input was selected for agents with no validate_input and failed all 17 of its steps on each card. Separately, steps the SDK marks not_applicable were 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_dispatch listing get_adcp_capabilities beside sync_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 actually runs. 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 without validate_input now take a coverage-gap skip. The second half has since moved. #7454, merged September 13, 2026 and linked in the thread that day, grades fixture_seed_unsupported and similar skip reasons as not applicable. On the signals card the two storyboards filed under this half, deterministic_testing and idempotency, have read untested at 0/0 since September 14. They are no longer failures, though not the passes the local SDK reports either. Those runs also report sdk_version 14.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 matches both cards’ latest runs; how much of the drop those three PRs caused is less clear. Both cards still grade on the 3.1.20 bundle, published September 2, before #7405 and #7406 merged, and the one signals failure, in billing_gate_dispatch, is still there, as it was when this thread counted it on September 13. None of the ten sales failures is in a storyboard the filing named, and the 17 canonical_format_validate_input failures on the sales card cleared because this agent now implements validate_input. 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 read complete and authoritative in the public history feed. PR #7453, merged September 13, 2026, stops runs the SDK marks timed_out from replacing the public grade; PR #7506, which would do the same for runs that report no completeness at all, is still an unmerged draft.
  • 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. #7429 is still open, and #7433, a separate PR to persist the runner version, is still an unmerged draft.
  • 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_test entry 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, a last_rechecked_at on 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 clearing last_checked_at so 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 meaningsrequired_tools is 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, and billing_gate_dispatch mis-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 in comply.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 from required_tools first. It shipped in 13.0.4 on September 14, 2026 (comply.js:600 now reads required.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/sdk 13.0.3, storyboard run grades every response_schema check on a comply_test_controller step as failed with no_schema_registered, while storyboard step passes 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 tracking latest — the newer build could not grade its own controller surface. Root-caused by the bot triage to isExternalResponseSchemaAuthoritative treating 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 now track 13.0.4.
  • 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_at was stamped before comply() ran, so a crash inside the window silenced the agent for up to 12 hours a cycle; every heartbeat row was written dry_run: true and 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 a stall of that shape showed fleet-wide: across all 35 registered agents, heartbeat runs went from 25–36 a day over September 8–13 to six on the 14th and one on the 15th, with no heartbeat anywhere in the registry 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 read scheduler.ts as confirming the mechanism, with releaseSlot() in a finally that 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 a Promise.race timeout in runJob, with a queue-depth cap as secondary hardening; the triage’s other expert recommended pulling /internal/jobs first 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 the Promise.race timeout the triage named, made opt-in: a scheduled job can set executionTimeoutMs, and when that expires the scheduler signals the run to stop through an AbortSignal and stops waiting on it, so the finally holding releaseSlot() 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/jobs now reports activeJobs, queuedJobs and maxConcurrency; 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 of main that 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 guard main already 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 my two agents have 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 are 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.
  • A field the 3.1 schema requires and two governance storyboards never send — on the 3.1 line, governance_approved and governance_conditions register a governance agent on the account, then call create_media_buy without plan_id, which the request schema from 3.1.20 through v3.1.23 says is “Required when the account has governance_agents”. The seller has no other key for the approval call: the 3.1 check_governance requires plan_id and tells governance agents to reject a sibling account field (#7547, filed September 15, 2026). The sales agent shipped delegated governance that day — sync_governance, a check_governance call before commit, governance_aware: true — and a registration on the shared sandbox test account is permanent, so every create_media_buy on that account started to require plan_id. Six storyboards went red: the two named here; the base media_buy_seller storyboard, which registers an agent in its own setup phase and then buys without plan_id; and three that never ask for governance and only reuse the account. The full local suite (@adcp/sdk 13.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 declaring governance_aware cannot 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, turns off the call and the tool: sync_governance is off tools/list and refused with UNSUPPORTED_FEATURE. A test fails if governance_aware, now false, disagrees with the constant. A buy on the registered account now goes through without plan_id and 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_sivt and sync_creatives_contradicted, both intentional). Bot-triaged P0 on September 15, at first with two options put to the maintainer: A, add plan_id: "$context.plan_id" to both storyboards; B, move main’s deprecation of plan_id, in favour of governance_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 one context_outputs block, owned by get_products_brief, so $context.plan_id is undefined. Each also has a sync_plans step, a governance-agent task, without the requires: [multi_agent], default_agent: sales and per-step agent: routing that governance_denied and governance_denied_recovery carry. So sync_plans is 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. On main, both storyboards have routed sync_plans to a governance agent and exported its plan_id since August 8, 2026 (PR #6277, PR #6280), and they send governance_context where the released 3.1 files send neither field — there $context.plan_id is 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 storyboards requires: [multi_agent] with default_agent: sales, routes sync_plans to the governance agent and every other step to the seller, gives sync_plans a context_outputs block that captures plans[0].plan_id, sends that as plan_id: "$context.plan_id" on create_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 as check_type and consultation_context, which the 3.1 schema-path lints reject, so it carries only the routing and plan_id fixes. The 3.1.x create_media_buy request schema defines no governance_context field, 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 is the fix released: there is no v3.1.24 tag, the 3.1.x branch is three commits past v3.1.23, and the open release PR (PR #7559) lists the change under [email protected]. The newest public run on the hosted card, an owner refresh at 09:55 UTC on September 16, 2026, before the merge, graded bundle 3.1.20. Of the storyboards, #7555 touches only governance_approved and governance_conditions. The other four that failed are unchanged on 3.1.x since v3.1.20, and each still calls create_media_buy for the brand and operator the governance storyboards register, without plan_id. Three of them, delivery_reporting, measurement_accountability and vendor_metric_accountability, register no agent and only reuse the account. The fourth, the base media_buy_seller storyboard, registers one in its own governance_setup phase, with no multi-agent requirement, and then buys without plan_id: the shape this filing reported, in a storyboard it did not name. On main its copy has the same shape and sends no governance_context, which main requires 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 of plan_id, as it did in the local suite before the switch-off — the three while the registration on the shared account stands, and media_buy_seller on every run, since its own setup phase registers the agent again. The local suite cannot run the patched storyboards yet: it runs bundle 3.1.20 from @adcp/sdk 13.0.4, which carries no later 3.1 bundle. The constant stays off. 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 the plan_id infrastructure 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 of plan_id, sync_plans, multi_agent or default_agent, and on main zero of governance_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-level required is the same five fields on each line, and neither schema carries an if/then or dependentRequired touching either field — so a schema-validation pass over the storyboard is clean, and it fails only against a seller that enforces the documented requirement. The timing matters because #7559’s dist/compliance/3.1.24 copy is the identical blob, so 3.1.24 ships the storyboard unchanged unless the decision lands first.
  • A conformance failure you cannot act oncapability_discovery failures 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 declared signals protocol 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 6901 json_pointer, machine-readable expected and actual, a fetchable schema_url and 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 missing adcp.idempotency block and no context.correlation_id echo. 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 plain npm 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.yaml and error-compliance-signals.yaml tell a seller to answer VERSION_UNSUPPORTED with recovery: fatal, and list the vocabulary as “correctable, transient, or fatal”. core/error.json types recovery as ["transient", "correctable", "terminal"], so fatal is not a legal value, and enums/error-code.json classes this code correctable: 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 on main the 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 on main — shipped it in dist/compliance/3.1.23/. That is the line the hosted grader runs, on bundle 3.1.20. 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 the expected: prose — only the validations: 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 with recovery: "terminal", which on 3.1 means the buyer needs a human, and the signals adaptor sent no recovery at all, where 3.1 says senders SHOULD populate it on every error. Both now answer correctable, verified live. Seven minutes after that comment, PR #7572 was opened against 3.1.x carrying 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 is bot-drafted under the maintainer account, so it still has to pass review and catch the release. The audit that followed on this side found the defect was a class rather than an instance: the sales agent’s ToolError constructor defaults recovery to terminal, 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 kept terminal on 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’s findings, which check-governance-response requires 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_handling at 10 of 11 scenarios. The failure was not an error-handling defect at all: billing_gate_dispatch asserts field_present: account.supported_billing in an ungated first phase, and that field is required with minItems: 1, so there is no honest value for an agent with no account management. Its own description says buyers pass one of those values in sync_accounts — a tool this agent does not expose. Declaring it to pass would have been the governance_aware mistake 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 enforced required_tools in every mode — at comply.js:600 for capability-driven selection and again at runner.js:1420 at load time — as an any-of gate, which the bundled storyboard schema documents as deliberate and pairs with a strict required_any_of_tools form that almost nothing uses. Both storyboards were admitted on one matching tool: get_adcp_capabilities, which every agent has, and comply_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: true plus requires_capability: { path: account, present: true }, with a runner test. It is on main only. The finding that mattered came afterwards. The required_tools corrections 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 own resolveStoryboardsForCapabilities and partitionStoryboardsByRequiredTools against each bundle in turn: on 3.1.20 both storyboards are runnable, on 3.1.23 both are excluded for missing sync_accounts and validate_input respectively — 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 remains is that the hosted grader is pinned to bundle 3.1.20. 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: buyResponse answers a missed read-back with MEDIA_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’s batch() is atomic only with itself, so nothing serialises the two. Three interleavings reproduce. The narrowest is the create path, since a fresh buy is written pending_approval, which sits in neither delete guard and so needs a decide-then-purge pair inside the window; the widest is get_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 answered CONFLICT with recovery: transient and was wrong. core/error.json defines transient as “retry after delay”, the purge takes the row’s UNIQUE idempotency_key with 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 new media_buy_id. Create now answers terminal on both of its call sites, and the read paths stay transient, where a retry does converge. The half the spec owns went upstream (#7575, filed September 17, 2026): replay_ttl_seconds scopes the replay window purely in time, minimum 3600, and enumerates three outcomes, being the cached response, IDEMPOTENCY_CONFLICT and IDEMPOTENCY_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. CONFLICT is no better a fit: the schema scopes it to revision mismatch, and error-details/conflict.json carries expected_version and current_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) called activate_signal with signal_id, carrying a SignalID object, where the schema requires a signal_agent_segment_id string (#3349, filed in the protocol repository under a header that already named adcp-client as 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/client 4.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 to adcp-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-guaranteed assumes bidding and states that buys need no human approval, while sales-guaranteed describes 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). The certification contradiction found alongside it is already fixed: the docs stated that grading neither requires nor uses comply_test_controller, while specialism certification requires seed_* 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).
  • Eligibility you can read off the APIverified is a bare boolean computed from badge rows, so a seller cannot 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, auto-triaged ready-to-implement within the hour, and untouched by a maintainer since — closes the API half of #3525): proposes a per-role eligibility block with machine-readable blocker codes, filed with our agent as the reproduction — zero failing storyboards, verified: false, and the actual first-guard blocker (no declared specialisms, the #6303 taxonomy gap) surfaced by no public field.
  • 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 defined on 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} returned Cannot 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 (#4266PR #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_capabilities must return, rather than the filtered list the filing expected (#4254PR #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 (#4256PR #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 (#4255PR #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 (#4348PR #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 (#4658PR #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.

Domains

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

AdCP Protocol Domains — one hub, seven domains, one shared schema. AdCP PROTOCOL DOMAINS Media Buy — discover inventory, transact, and report on delivery through one schema. Media Buy discover · buy · report Creative — assemble and preview ad creative against the same shared schema. Creative build · preview Signals — discover audience and context signals and activate them on a buy. Signals discover · activate Accounts — billing, usage, and entitlements for every party on the protocol. Accounts billing · usage Governance — approval workflows and an auditable trace across every domain. Governance approve · audit Brand — a brand.json declares brand identity and guardrails the protocol can read. Brand brand.json Sponsored Intelligence — an experimental domain, not yet a stable part of the schema. Sponsored Intelligence experimental exp. AdCP — the single protocol every domain shares; one schema runs across all of them. PROTOCOL AdCP One protocol, many domains — the same schema across every step.
AdCP at the center; each domain connects to a workflow surface.
Workflow

From brief to governed execution.

  1. Discover authorized agents — adagents.json, brand.json, registry
  2. Set up account context — commercial identity, billing, advertiser / operator relationship
  3. Discover inventory and products — natural-language brief into structured product responses
  4. Build or sync creative — formats, assets, brand identity, approvals
  5. Add signals — audiences, suppression, contextual signals, signal activation
  6. Check governance — budget, brand safety, targeting rules, approvals
  7. Execute, report, and audit — media buy, delivery, events, audit logs
From brief to governed execution — seven steps, brief in, audited execution out. BRIEF → GOVERNED EXECUTION Discover agents — read adagents.json and the registry to find which agents can act for this brand. 01 Discover agents adagents.json · registry Account context — resolve identity and billing so the brief runs against the right account. 02 Account context identity · billing Discover products — turn the natural-language brief into a structured product schema. 03 Discover products brief → schema Build / sync creative — assemble formats and route them through approvals. 04 Build / sync creative formats · approvals Add signals — attach audiences and activation signals to the campaign. 05 Add signals audiences · activation Check governance — verify budget, brand safety, and approvals before anything spends. 06 Check governance budget · brand safety Execute, report, audit — run delivery, emit events, and keep an auditable log of every action. 07 Execute, report, audit delivery · events · logs Natural-language brief in, governed + audited execution out.
Brief → discovery → accounts → products → creative → signals → governance → buy / report / audit.

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):

adagents.json illustrative
{
  "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.json illustrative
{
  "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

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?

Signals: a governed object — discover, inspect, activate, monitor. SIGNAL LIFECYCLE Discover — find which signals exist and what each one actually means before you reach for it. 01 Discover what signals exist what they mean Inspect — interrogate provenance, freshness, and policy so you trust the object before activating it. 02 Inspect provenance · freshness policy Activate — put the signal to work across an identity path, a contextual path, and measurement. 03 Activate identity path · contextual path · measurement Monitor — watch live status and the realised outcome, then feed it back into discovery. 04 Monitor status outcome the same operating problem as signal containerization Discover, inspect, activate, monitor — signals as executable objects.
Discover, inspect, activate, monitor — signals as executable objects.

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:

get_signals (MCP tool call) illustrative
{
  "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.

Trust

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.

Complementarity

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.

InitiativeRoleBest question
AdCPAgent task / workflow languageWhat should the agent ask or do?
AAMPIAB Tech Lab umbrella — buyer/seller reference agents plus foundations and trustHow do agents run briefs, negotiation and booking on the standards already in production?
ARTFContainerized real-time execution frameworkHow can service agents run inside host infrastructure with low latency and controlled mutation?
Agentic AudiencesEmbedding-based signal exchangeHow do agents exchange identity, contextual, and reinforcement signals?
OpenRTB / AdCOM / Deals API / OpenDirectExisting transaction and object standardsWhat underlying objects and transaction models should agentic systems reference?
Two ancestries, overlapping functions — AdCP's new rails and IAB Tech Lab's existing rails both reach the deal plane. COMPLEMENTARITY MAP AdCP — the new-rails stack: workflow surfaces (discover, buy, creative, signals, govern, report) defined as wire primitives any counterparty can call. AdCP Agent task & workflow discover buy creative signals govern report IAB Tech Lab — the existing-rails stack: AAMP's buyer and seller agents run briefs, negotiation and OpenDirect booking; ARTF is the runtime; Agentic Audiences the signal exchange — all on foundations already in production. IAB TECH LAB Standards & runtime AAMP agents + umbrella ARTF runtime Agentic Audiences signal exchange FOUNDATIONS OpenRTB · AdCOM Deals API · OpenDirect Agentic advertising interoperability — the shared outcome both layers serve: tasks expressed once run across open standards and runtimes. CONVERGES AT The deal plane (contested) Different ancestries, overlapping functions — the head-to-head is at the deal plane.
Two ancestries — new rails and existing rails — overlapping in function, meeting head-to-head at the deal plane.
Implementation

Implementation lens by company type.

Pick your company type to see what to expose or consume — and the AdCP domains it touches first.

Select your company type
What to expose

Authorized agents, inventory products, packages, account setup, governance, reporting.

  • adagents.json
  • Media Buy
  • Accounts
  • Governance
What to consume

Partner discovery, product discovery, creative formats, media buy, governance, reporting.

  • brand.json
  • Media Buy
  • Creative
  • Signals
What to expose

Signal catalog, provenance, activation paths, pricing, policy, status.

  • Signals
  • Accounts
  • Governance
What to support

Seller agents, deals, signals, governance, a real-time execution bridge, reporting.

  • Media Buy
  • Signals
  • Governance
  • registry
What to support

Buyer agents, media buys, creative, signal activation, governance, measurement.

  • Media Buy
  • Creative
  • Signals
  • Governance
What to expose

Formats, asset generation, preview, brand identity, approval workflow.

  • Creative
  • Brand
  • Governance
What to expose

Delivery, event logging, attribution, audit, lift, governed outputs.

  • Media Buy
  • Accounts
  • Governance
No Fluff POV

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.
Stack fit

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.

Next step

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.