Notes from the Implementer Seat
Standards takes written from the spectator seat are weightless — nothing happens when you're wrong. Running a live AdCP signals agent is different: the freshness gap you file becomes a merged schema field, the compliance grader that wrongly grades your perfect run becomes a P0 fix, and your wire-size data backs a design concern in review. The diligence question for any agentic vendor: show me your issues, not your deck.
In English, please
Most standards commentary comes from people who only read the rulebook — nothing happens to them if they're wrong. This account comes from someone who builds and runs software obeying one such rulebook, AdCP, which lets AI buying agents and ad sellers trade targeting data. When the rulebook has a gap, the software fails at 2am.
Running the software surfaced a gap invisible from reading alone: no standard way for a buying agent to know if targeting data was fresh or months old — a problem when a machine acts on it unchecked. That gap was reported, and a fix — a simple 'last updated' stamp on every record — is now built into the rulebook.
A separate bug lived in the public system that grades whether software correctly follows the rules: it made a fully correct run display publicly as failing. Since that public grade functions as the actual product for anyone listed, a wrong grade is not cosmetic — it was traced to its exact cause and fixed.
Not every contribution meant writing the fix personally. When outdated guidance on digitally signing data needed replacing, the better move was to make the case and let the rulebook's own maintainers write the change — because trust in a group that maintains shared rules comes from crediting work honestly, not from racing to land your own patch.
The public tests used to fail single-purpose software on checks meant for far more capable systems; six bug reports later, the tests were narrowed to fit what each type of software actually does. Separately, real measurements from the live software — five records totaling 88 kilobytes against a 64-kilobyte limit, though about 60 kilobytes once formatting whitespace was stripped, and before any of the proposed new fields — backed another reviewer’s case for sending data in smaller pieces.
A related effort worked out, with another contributor, how a targeting grid should translate into what ad sellers can actually deliver — including owning up to a placeholder reference to a not-yet-existing document, which had already cost the collaborator time before it was fixed. The takeaway for judging anyone's claims about AI-run advertising: ask for actual bug reports and fixes, not sales slides.
On this page
Most standards commentary is written from the spectator seat. You read the spec, you form a take, you publish the take. The take is usually fine. It is also usually weightless, because nothing happens to you when you are wrong.
The implementer seat is different. I build and operate a live AdCP signals agent — the No Fluff Advisory adaptor, listed in the AAO registry, with all-pass conformance runs across the 3.0 and 3.1.0 GA suites. When the protocol has a gap, my agent fails a storyboard at 2am. When the conformance grader has a bug, my dashboard says “Degraded” in front of anyone who looks. The protocol bills me for my opinions.
This essay is what that seat has taught me — told through the actual issues and pull requests, because in standards work the receipts are the argument.
The freshness field nobody had
Run a signals agent for a while and you notice something the spec review never surfaced: a buyer agent receiving a signal record has no standard way to know how fresh it is. Is this audience definition from yesterday or from March? In a market where agents act on signals without a human sanity-check, that is not a metadata nicety. It is a trust gap.
So I filed the gap analysis (#5248) and authored the fix: a last_updated field on signal-definition (PR #5249), merged and shipping in the 3.1.0 line since rc.7.
The lesson: protocol gaps appear at runtime, not in document review. Nobody reads a schema and thinks “this lacks a freshness contract.” You only feel it when your agent has to decide whether to act on a record of unknown age.
When the grader lies
In June, a rollup regression in the compliance grader started rendering all-pass single-protocol runs as “Degraded.” My agent had a perfect run and a failing grade. For a registry-listed agent, that is not cosmetic — conformance status is the product.
I root-caused it to the exact commit that introduced it, filed it (#5429, auto-triaged P0), and authored the fix a maintainer merged (PR #5444).
The lesson: the conformance tooling is part of the standard. A spec with a buggy grader is a buggy spec, operationally — because what implementers and buyers experience is the grade, not the prose. If you are evaluating an agentic vendor, ask who fixes the test harness when it is the harness that is wrong.
Deprecating politely
Earlier in the cycle, the webhook-signing guidance still carried a deprecated HMAC framing. I inventoried every place it leaked into the storyboards and SDK on-ramps (#4270) and drafted the PRs pointing the defaults at RFC 9421 (#4273, #4275). Those drafts were closed in favor of the maintainer implementation — which is exactly how it should work — and the 3.0.9 line realigned away from HMAC.
The lesson: sometimes your contribution is the case, not the commit. Standards bodies run on maintainer trust; an implementer who insists on landing their own patch over making the argument well is optimizing for credit instead of the protocol.
A test suite is only as real as its edge cases
Signals-only agents used to fail storyboards that had nothing to do with them — media-buy gating applied to agents that never buy media, error-handling tracks gated behind tasks a signals agent does not implement, envelope assertions against transport responses where the field does not exist.
Six root-caused reports later (#2916, #3350, #4009, and earlier #2535, #3999, #4065), the 3.1.0 suite skips non-applicable gating and gives signals-only agents dedicated error-handling and schema-validation storyboards.
The lesson: a standard claims to support a class of participant; the test suite decides whether that claim is true. Single-protocol agents are the canary for whether “modular” is real.
Review is where implementers earn their seat
The most consequential signals work in the current cycle — the signal-definition epistemic and provenance layer (#5017) — was opened under the maintainer account, not by me. My contribution was review, and one piece of data the thread did not yet have — actual wire sizes from my live agent. Five signals came to 88,590 bytes against a 64 KB probe ceiling before any of #5017’s new fields, though that was pretty-printed, and our own x_* extension blocks made up most of each signal’s bytes by the breakdown I posted; compacted, it was an estimated 60 KB. The point was what the new fields would add on top: 3–5 KB a signal, by my estimate. That backed a progressive-disclosure concern another reviewer had already raised, and before the merge the maintainer account added compact-by-default guidance and a fields selector on get_signals. My field-level schema notes came an hour after the merge; the follow-up, #5190, addressed them without citing them, not always my way.
The lesson: implementers bring evidence to review that no amount of careful reading can produce. And crediting the author accurately is not modesty — in a working group, overclaiming is how you lose the seat.
(Revised September 17, 2026 — this section said five “enriched” signals weighed in at 88 KB and that the number “drove” the progressive-disclosure design. The signals carried none of #5017’s new fields, the 88,590 bytes were pretty-printed (my comment on #5017 did not say so), another reviewer raised progressive disclosure first, and my schema notes came after the merge and were taken up, uncited, in the follow-up #5190.)
Interoperability is a crosswalk problem, not a primitive problem
A newer thread shows the same discipline from a different angle: geographic cell targeting. RFC #5969 defines geo_cells / geo_cells_exclude as the addressable unit for geo targeting — deliberately not defining what a seller does when it cannot execute on that unit natively, which is every seller today. The WG named that gap decision point X-A vs. X-B; the resolution (X-B) keeps the wire contract minimal (“MUST NOT deliver outside the declared cells”) and pushes the conversion machinery into a companion document instead of the normative surface.
Working with Nathan Woodman (@nwoodman-spec), I filed both halves of that companion: cell-to-execution conversion (#6013) and cell-grain delivery reporting (#6014). The #6014 design — cell as a fifth geo_level, inheriting the existing per-level reporting apparatus rather than a parallel array — is one I proposed on #5969; Nathan wrote the full specification.
A small confession is part of the record: an earlier comment on #5969 referenced the reporting companion by a placeholder number that never got replaced. Nathan went looking for it while writing #6013 and correctly couldn’t find it. I fixed the record once both issues existed, accepted the one sentence #5969 needed in its own normative text (binding the delivery restriction to the seller’s output footprint, not its internal representation), and added a privacy-considerations note a third contributor pushed on: a resolution floor alone doesn’t protect a sparse cell, and an audience floor alone doesn’t stop someone from drawing geometry around one household — you need both, stated together.
The lesson: a placeholder reference is a debt against your collaborator’s time, not a stylistic shortcut — fix it before someone goes looking for it. And the same interoperability argument that justifies a spatial base unit generalizes cleanly to time: my draft framing for a temporal companion proposes UTC hour-of-week as the base unit, with broadcast-day conventions as declared, versioned crosswalks onto it — never the reverse — for the same reason geo_cells doesn’t defer to administrative boundaries. It’s still a draft, posted for a gut check before it goes anywhere formal. That’s the nature of the seat: some proposals are load-bearing before you’re sure they’re right.
The embedding on the rung that says don’t share the content
AdCP 3.1.21 shipped Trusted Match with a context_signals.embedding field on the classified-output tier — the tier whose entire purpose is that the raw content does not travel. On an ai_assistant property the “content environment” is one person’s conversation turn, and a dense vector of that turn is partially invertible back to their words. A field that exists to avoid sharing content was carrying it.
I filed that against v3.1.21 with the inversion literature and two additive text edits (#7393), then opened a PR of my own (#7415). The fix that shipped was not mine. A maintainer wrote #7396 four days after the issue, it was backported to the 3.1 line as #7424, and it went out in v3.1.22 on September 13, 2026. My PR is still open.
That is the second time in this essay the same thing has happened, and it is worth naming rather than smoothing over: the argument landed, the patch did not. Nor did the maintainers take my proposal as written — I argued for a demonstrate-on-request carve-out, they chose a flat prohibition, and they added policy-filtering rules on keywords and summary my issue never asked for. The stricter route was the better one. Being overruled upward is still being overruled, and “I filed the issue that became v3.1.22” is a sentence that needs the rest of this paragraph attached to it.
The companion thread did not land at all. Triage asked me to split out the cache problem, since it carried no privacy dependency: the recommended Context Match 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 correctness half shipped in #7397 as a {provider_id, context_hash} key over the full forwarded request; my issue was closed not planned — superseded, not implemented — and the steer toward cache_ttl: 0 for viewer-variable placements was not taken up.
That release’s changelog is itself incomplete, by the maintainers’ own account: #7451, filed by a maintainer and merged September 13, corrected the v3.1.22 record in v3.1.23 the next day, because #7397 shipped without changelog coverage. Every 3.1.x release body on GitHub is version-only — the v3.1.22 body is literally the string “AdCP 3.1.22”. If you are auditing anyone’s contribution claims, mine included, the release notes will not help you. The repository will.
(Added September 13, 2026. This essay published in June, and this is the clearest instance it has of the thing it argues for, so it belongs in the body rather than in a footnote.)
What I am proposing next
Two open threads, stated as proposals because that is what they are:
- A revocation primitive —
revoke_activation(#4203), because governed activation without governed un-activation is half a contract. Alongside it, a Measurement Feedback Signal (#4296). - Migration coordination — the JWKS adoption baseline that gates chain-of-custody work (#4206). Deprecations without adoption data are just wishes.
All of these may merge, change, or close. That is the nature of the seat.
(Revised September 13, 2026 — two threads listed here have since closed as completed and have moved out of the proposal list: runtime signal-quality attestations on check_governance (#5418) and the RFC 9421 webhook-signing migration coordination (#4205). Leaving shipped work filed under “proposing next” understates the record — the direction of error nobody thinks to check for.)
The operator takeaway
If you are a buyer, a board, or an operator evaluating anyone’s agentic advertising story — including mine — the diligence question is simple: show me your issues, not your deck. A vendor who runs a live agent against the public conformance suite has receipts: merged PRs, root-caused bugs, review contributions with data, proposals that survived contact with maintainers. A vendor who does not will show you architecture slides.
The implementer seat is slower than the spectator seat. It is also the only seat where you find the freshness gaps, catch the grader lying, and learn what five signals actually weigh on the wire.
That discomfort is the point.
The full contribution record, with statuses validated against the public AdCP repository, lives on the AdCP protocol guide. The agent itself is documented at Building: AdCP Signals Adaptor.