feat(testing): bridge participation marker (#1775)#1786
Conversation
When the TestControllerBridge merges seeded fixtures into a handler response,
stamp `_bridge: { callback, tool, merged_count }` on structuredContent so
storyboard runners and compliance leaderboards can distinguish wire conformance
against fixtures from adapter-against-upstream health.
- 19 dispatch sites across 16 bridge callbacks; append-merge stamps on ≥1
valid entry; singleton-replace stamps only when a fixture actually matched.
- Schema-safe (AdCP 3.0 envelopes all allow additionalProperties at top level).
- Drops three unreachable duplicate dispatch blocks (get_brand_identity /
get_rights / si_get_offering) appended below the canonical else-if chain.
- BridgeMarker type exported from @adcp/sdk/server.
Consumer side (storyboard runner / leaderboard policy) lives in
adcontextprotocol/adcp and is coordinated via #1782.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three convergent doc nudges from parallel expert review — no logic changes: 1. VALIDATE-YOUR-AGENT.md bullet now addresses the procurement reader directly: explains how `merged_count` distinguishes "fully fixture- augmented" from "bridge-supplemented real adapter call" (PM review). 2. JSDoc on `AdcpServerConfig.testController` flags the trust-boundary risk when registered without `resolveAccount` — caller-supplied `account.sandbox = true` is the only gate, so production deployments must either configure `resolveAccount` or omit `testController` outside test/staging (security review M1; pre-existing risk made discoverable by the marker). 3. JSDoc on `BridgeMarker` explains why the marker lives on the response body rather than MCP `_meta`: cross-transport parity with A2A, L2 text-body parity, and precedent with `replayed` (protocol review). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Expert review pass — four reviewers, no blockersRan four parallel expert reviews (ad-tech-protocol, code-reviewer, adtech-product, security). Synthesis: Convergent feedback addressed in commit 1d603ee:
Non-blocking, deferred:
Verdict from each reviewer: ship it. CI re-running on commit 1d603ee; will admin-merge once green. |
…roxy sellers only (#1787) Names the audience explicitly so state-local sellers don't wire the bridge unnecessarily, and upstream-proxy sellers know to wire it. Cross-links the upstream taxonomy proposal at adcontextprotocol/adcp#4593 and the leaderboard policy at #1782. Also collapses a duplicate trust-boundary blurb (added in #1779 alongside the security-review note in #1786) into a single coherent section. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n model (#1795) * docs: align bridge framing with revised single-dimension certification model Maintainer walked back the two-badge proxy-vs-state-local split from #1782 and proposed a single-dimension framing (Wire Conformance / Live Integration Verified) where every seller faces the same verifiability gap, and the bridge is one of two mechanisms for closing the seed→read loop, not a special path for one seller class. JSDoc on AdcpServerConfig.testController: - Drops "only upstream-proxy sellers" as primary framing - "Pick by where your read handlers fetch from, not by seller class" - "Either path earns wire-conformance credit; it is *not* a separate certification category" skills/build-seller-agent/SKILL.md: - New "Test surfaces" section frames the verifiability gap as universal - Names the two implementations (state-local store vs TestControllerBridge) - Hedges on certification names while #1782 settles No SDK behavior change. Marker contract, trust-boundary docs, and dual-emit warn from #1786/#1787/#1788 all stay as-is — they describe mechanism without committing to certification taxonomy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(skill): fold docs-expert feedback — tighten Test surfaces section + in-repo JSDoc link --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Closes #1775 (SDK half — consumer half tracked in #1782 and
adcontextprotocol/adcp).When the
TestControllerBridgemerges seeded fixtures into a handler response, stamp a non-normative_bridge: { callback, tool, merged_count }marker onstructuredContent(mirrored tocontent[0].textwhen JSON). This is the runner-visible signal that distinguishes "this pass exercised the adopter's adapter against upstream" from "this pass exercised wire conformance against fixture data merged by the SDK". Storyboard runners and compliance leaderboards read it to attribute bridge participation in run records.Why now: the bridge surface has grown from 5 tools (PR #1754) to 13 through phases 2–4 (#1759, #1761, #1772, #1773). With 13 fixture-augmented tools, every storyboard pass through fixture-merge reads identically to one that ran through a real adapter — exactly where the gap matters most (walled-garden proxies). JSDoc disclaimers don't survive contact with a leaderboard score.
What ships
stampBridgehelper (parallel tostampReplayed) sets_bridgeonstructuredContent+ opportunistic JSON-text mirror.create-adcp-server.tsstamps per-tool with the originating callback name, so runners can attribute participation at thegetSeededCreatives/getSeededMediaBuys/ etc. granularity.get_account_financials,get_brand_identity,si_get_offering,get_property_list,get_collection_list,get_content_standards) stamp only when a seeded fixture actually matched the request id and replaced the handler payload.additionalProperties: trueat the top level (success arm foroneOfenvelopes). Underscore prefix advertises "internal / out-of-spec" to validators that round-trip unknowns.BridgeMarkertype exported from@adcp/sdk/serverfor adopters who want to type-check marker reads.get_brand_identity,get_rights,si_get_offering) that were appended below the canonicalelse ifchain. No behavior change — only the first block ever fired in a continuouselse if.Docs
docs/guides/VALIDATE-YOUR-AGENT.md§ Platform-proxy sellers gains a new bullet explaining the marker, its payload, and how to interpret a bridge-augmented pass (wire conformance against fixture data, not adapter-against-upstream health). Cross-links #1775 (cross-repo coordination) and #1782 (leaderboard policy).Half-shipped warning
The SDK emits the signal; the runner has to surface it for the marker to do its job. Consumer side lives in
adcontextprotocol/adcp(storyboard runner) andadcp-client#1782(leaderboard policy). Without those, this PR is the inert half of the contract — landing it unblocks the cross-repo work.Test plan
test/lib/bridge-marker.test.js— one happy path per bridge callback + four absence cases (omitted callback, non-sandbox, singleton no-match, empty-fixture)seed-per-tool-wiring,seed-get-products-wiring,test-controller-bridge,seed-merge-helpers) pass unchangedtsc --noEmitcleanprettier --checkclean🤖 Generated with Claude Code