Skip to content

feat(training-agent): serve the AdCP 3.3 beta as an opt-in preview (#8181) - #8689

Merged
bokelley merged 1 commit into
mainfrom
bokelley/ta-33-preview
Nov 2, 2026
Merged

bokelley merged 1 commit into
mainfrom
bokelley/ta-33-preview

Conversation

@bokelley

@bokelley bokelley commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

First slice of #8181. The hosted reference training agent now serves the AdCP 3.3 beta as an opt-in preview to callers that pin it exactly, so the experimental 3.3 surfaces it already implements can be exercised against test-agent.adcontextprotocol.org. Hold policy, extend_hold and response collections are still open in #8181.

Which preview, and why

It serves 3.3-beta.3, the wire value of the published 3.3.0-beta.3 bundle.

  • main's static/schemas/source is structurally identical to v3.3.0-beta.3. Only two description strings changed since the tag, so what the agent emits matches what was published.
  • Responses are validated against the committed, immutable dist/schemas/3.3.0-beta.3. That bundle is registered with the SDK on first preview use, because the pinned @adcp/sdk 17.0.0-beta.8 packages 3.3.0-beta.1, which differs from beta.3 in 616 schema files.
  • @adcp/sdk 17.0.0-beta.11 and later bundle beta.3 and negotiate exactly 3.3-beta.3. Checked end to end: a beta.12 buyer selects the preview, calls request_account and reads authorization.authorized_at. A 3.2.7 buyer stays on 3.2.

Negotiation by caller type

Caller Served
adcp_version: "3.3-beta.3" The preview. Echoed, and valid against the beta.3 bundle.
"3.2", "3.1", "3.0", prerelease pins the agent already listed Unchanged
"3.3", "3.4" (release pins above 3.2) 3.2, unchanged. A release pin never resolves onto a prerelease.
Other 3.3 prereleases ("3.3-beta.1", "3.3-beta.4") Unchanged: never the preview
adcp_major_version only Unchanged (3.2 echo). The v5 resolver now skips the preview when picking the highest release.
Unpinned Unchanged: the route default (3.0 on /mcp, 3.2 on tenant routes)

Capability discovery lists "3.3-beta.3" in adcp.supported_versions on every tenant route and on /mcp. Per versioning.mdx, a buyer reads the list and then pins.

Design: a separate registry for the preview

The SDK shapes every response down from its configured ceiling (downgradeResponse(fromVersion: adcpVersion, ...)). A first version raised the shared ceiling to the preview (as candidate mode does). That projected every stable response through the 3.3 schema: a 3.0 check_governance status: "approved" came back as "completed".

Preview requests now go to their own registry, whose ceiling is the preview. Exact pins on tools/call arguments, or on tools/list params or _meta (as @adcp/sdk sends it), are routed there. Everything else stays on the stable registry, configured as on main. The preview registry is built on first use; production prewarms it after the stable registry is up.

  • A differential capture of about 400 stable requests (capabilities at ten pins on all eight routes, tools/list, and a media-buy flow at five pins on /sales/mcp and /mcp) matches main byte for byte, except the added supported_versions entry. With the kill switch, it matches main exactly.
  • The proposal-profile routes and the identity33 route don't get the preview and don't advertise it.

Experimental 3.3 surfaces at the preview pin (sales)

  • Account requests (account.requests): request_account, and account_requests on list_accounts.
  • authorization.authorized_at / regranted_at and revision on accounts.
  • Reporting subscriptions (media_buy.reporting_subscriptions).
  • The reporting_subscription_probe sandbox clock, and sales-fixed-rate.
  • None of these appear for any other pin.
  • request_account is registered in the preview registry only, so a stable caller still gets "Tool request_account not found", as today.
  • Custom sales tools (list_accounts, request_account, get_principal, ...) now echo adcp_version when served at the preview. Stable responses are unchanged.

Production safety

  • Kill switch: TRAINING_AGENT_DISABLE_ADCP_PREVIEW=1 (or true). It's read at startup, so it takes effect on the next restart (for example fly secrets set, no deploy). The process then negotiates exactly as main.
  • Account resolution: fail-closed account resolution on served 3.3 routes behaves like 3.2 for the public sandbox tokens. The agent's resolveAccount is reference-aware: provisioned natural keys resolve, an unprovisioned natural key on a discovery task gets ACCOUNT_NOT_FOUND, and account_id references keep the sandbox's synthetic-account posture. Tested at both pins.
  • Hosted compliance targeting is unchanged: the preferred lines are 3.2 then 3.1, and the selected bundle is 3.2.8 with or without the preview.
  • Runtime-asset verifier: verify-training-agent-runtime-assets now also checks that the image carries dist/schemas/3.3.0-beta.3.

Candidate mode

ADCP_STORYBOARD_CANDIDATE_VERSION_MODE=1 keeps its single-registry behavior. The candidate replaces the hosted preview, and the kill switch doesn't apply to it.

Tests

The new tests fail on main.

  • server/tests/unit/training-agent-preview-version.test.ts:
    • preview selection;
    • per-route negotiation for every caller type;
    • the preview advertised once, and never 3.3-beta.1;
    • experimental surfaces present at the preview and absent otherwise (capabilities, tools/list, request_account, list_accounts, controller scenarios);
    • public-token account resolution at both pins.
  • server/tests/unit/training-agent-preview-kill-switch.test.ts: with the switch set, nothing advertises, serves or registers the preview.
  • served-release-shape.test.ts (fix(training-agent): withhold 3.3 fields from callers pinned below 3.3 #8496) now drives every route's flow at the preview too. Every response must echo 3.3-beta.3 and validate against the published beta.3 bundle.
  • Mutation checks: ignoring the kill switch, routing preview pins to the stable registry, and dropping the major-only exclusion each fail the new tests.

Verification

  • npm run typecheck, npm run test:unit, npm run test:schemas: pass.
  • npm run test:server-unit -- --shard=N/3:
    • 1/3: 274/274 files pass.
    • 2/3: 273/273 files pass.
    • 3/3: 272/273 files pass. The failure is training-agent-creative33-buyer.test.ts (fixture video 404), which fails identically on main's sources.
  • Training-agent integration tests:
    • public-token, legacy-mcp, tool-dispatch-smoke, tool-catalog-drift, 3-0-compat-tools, demo-key, webhooks, reporting-reliability, si-lifecycle, brand-json, verification-walkthrough and schema-versioning pass.
    • idempotency-cold-boot needs Postgres, which wasn't available locally.
  • Storyboard matrix (CI on this PR: current surface in candidate mode, plus 3.0-compat):
    • every floor passes;
    • per-job totals are identical to main (run 41355916057 at 13c5ba4) across all 16 jobs and the 8 sales orchestrators;
    • the property_list_reference_resolution and other step failures are the same ones main has.
  • npm run test:external-smoke against the deployed agent passes. This is read-only; the deployed agent changes only after deploy.
  • Differential capture of stable requests: identical to main except the advertised 3.3-beta.3. Identical with the kill switch set.
  • SDK end to end: an @adcp/sdk 17.0.0-beta.12 buyer pinned to 3.3.0-beta.3 selects the preview against the local agent. A 3.2.7 buyer stays on 3.2.

Upstream

adcontextprotocol/adcp-client#3597 covers three SDK points:

  • release pins downshift onto an advertised prerelease;
  • every older-pinned response is projected through the ceiling's schema (the reason for the separate registry);
  • the SDK derives supported_versions that include its own beta.

Docs: the 3.3 beta program page gains "Try the preview on the public test agent". It also corrects which SDK versions bundle beta.3, and the quickstart points to it. A patch changeset covers the protocol-scoped docs page.

🤖 Generated with Claude Code

…8181)

The hosted training agent serves the published 3.3.0-beta.3 bundle to
callers that pin "3.3-beta.3" exactly, so the experimental 3.3 surfaces it
implements (account requests, grants and the sandbox clock, principal-level
reporting subscriptions) can be exercised against it.

- The preview is listed in adcp.supported_versions on every tenant route
  and the v5 /mcp route. Only an exact pin is served it: unpinned,
  major-only and release-pinned callers (including "3.3" and later) and
  other 3.3 prereleases negotiate exactly as before.
- Preview requests go to their own SDK registry, configured with the
  preview as its ceiling. Stable requests stay on a registry configured as
  if the preview did not exist, because the SDK shapes every response down
  from its ceiling. A shared ceiling turned a 3.0 check_governance
  status: "approved" into "completed".
- Preview responses validate against the committed, immutable
  dist/schemas/3.3.0-beta.3 bundle, registered on first use. main's
  schemas match beta.3 structurally, and @adcp/sdk 17.0.0-beta.11 and
  later negotiate exactly "3.3-beta.3". Custom sales tools echo the
  preview.
- TRAINING_AGENT_DISABLE_ADCP_PREVIEW=1 turns the preview off at the next
  restart; the agent then negotiates exactly as before.
- Candidate mode (ADCP_STORYBOARD_CANDIDATE_VERSION_MODE) replaces the
  preview and keeps its single-registry behavior.
- Docs: the 3.3 beta program page explains how to pin the preview, and the
  quickstart points to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — opt-in 3.3-beta.3 hosted preview added to the public test agent, served only to an exact adcp_version/_meta.adcp_version pin via a separate preview registry; stable callers untouched.

Checked:

  • No static/schemas/source/** change → no spec drift, no oneOf regression.
  • No existing dist/** artifact mutated — committed dist/schemas/3.3.0-beta.3/index.json is read-only; immutability preserved.
  • Changeset present (patch), additive/exact-pin-only, not Breaking-class — highestSupportedRelease filters the preview (task-handlers.ts:371) and stable routes exclude the pin (types.ts:865), so unpinned/major-only callers never upgrade onto it.
  • Routing switches to previewHolder only on requestPinsPreview (router.ts:548); kill switch collapses to pre-preview behavior, test-covered.
  • Operability: ensurePreviewSchemaBundle (schema-compat.ts:331) lazy/memoized/no external I/O; rejection-caching matches existing idiom.

high_risk is true only because docs/reference/3-3-beta.mdx was modified; no medium-or-higher finding on that file, so the modification is presumed safe (no escalation on the flag alone). No critical/high/medium findings. gated_paths=false. No no-auto-approve team match. Prior decision: none. Falls through to row 9 → approve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant