Skip to content

docs(development): draft 8.0 design proposal for AdCP 3.1 support#1809

Open
bokelley wants to merge 3 commits into
mainfrom
bokelley/v3.1-sdk-design
Open

docs(development): draft 8.0 design proposal for AdCP 3.1 support#1809
bokelley wants to merge 3 commits into
mainfrom
bokelley/v3.1-sdk-design

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Summary

Standalone design proposal at docs/development/v3.1-sdk-design.md for how the SDK should expose AdCP 3.1 (canonical formats, version envelope, expanded capabilities discovery) without forcing adopters to think about v1 vs v2 shapes.

Draft for review only — no code changes in this PR.

The question the doc answers

How does the SDK present 3.1 to adopters? Two namespaces (client.v1.* / client.v2.*)? A new type for Product? A single API that auto-routes? The doc commits to the third: single API + dual-shape Product + per-agent auto-negotiation + structured projection diagnostics.

Design choices the doc commits to

  • Auto-negotiation per agent driven by get_adcp_capabilities.supported_versions, cached per-agent.
  • Single dual-shape Product that always carries format_ids and populates format_options when projectable. Symmetric on write — v1 manifests upshift to 3.1 sellers, v2 manifests downshift to 3.0 sellers honoring canonical_formats_only: true.
  • Bidirectional projection layer at src/lib/v2/projection/ using the spec's v1-canonical-mapping.json registry, with the 5-step resolution order (v1_format_ref → explicit canonical → registry glob → structural match → fail-closed).
  • Structured Diagnostic[] surface attached to both response errors[] AND the SDK's TaskResult. Codes: FORMAT_PROJECTION_FAILED, FORMAT_DECLARATION_DIVERGENT, CUSTOM_FORMAT_FETCH_FAILED. Never logger-only per the spec's resolution-order amendment.
  • Centralized fetchFormatSchema utility implementing the normative custom-format fetch contract (HTTPS-only, SSRF guards, no redirects, 1 MiB cap, sha256 hard-fail, \$ref sandbox, compile-time budget). If every SDK reimplements this, the protocol's security posture is the weakest implementation.
  • Versioned codegen under src/lib/types/v3.0/ and src/lib/types/v3.1/, with public re-exports always pointing at the latest stable version.

Explicit non-goals

  • No two-namespace split.
  • No silent projection across the major boundary — major-version mismatch throws VersionMismatchError.
  • No logger-only projection failures.
  • No piecemeal custom-format validation — either ship the complete fetchFormatSchema or hard-fail with CUSTOM_FORMAT_FETCH_FAILED { reason: 'unimplemented' }.

Migration path

Line What changes
7.x Defensive fixes (wire-envelope normalization in #1807 — already shipped).
8.0 Full 3.1 enablement.
8.x Hardening, additional registry entries.
9.0 When AdCP 4.0 lands, removes v1 paths per spec deprecation calendar.

The 8.0 PR sequence is decomposed into four reviewable units in the doc (compat-version + versioned codegen → projection + diagnostics → fetchFormatSchema → auto-negotiation).

Open questions called out

  1. Custom-format fetch default policy ('allowed' vs 'disabled').
  2. adcpVersion constructor option semantics under auto-negotiation (pin vs maximum).
  3. Read-time projection vs lazy accessor.
  4. get_adcp_capabilities cache TTL + invalidation.
  5. canonical_catalog_version skew handling (forward-compat with future canonicals).

Reviewers welcome

  • Adopters writing buyer agents: does this disappear far enough into the background that you don't have to think about it?
  • SDK maintainers: is the projection layer factored correctly to survive the 4.0 transition?
  • Spec working group: should the diagnostic codes be upstream (in the spec's error vocabulary) rather than SDK-local?

Companion to PR #1807 (wire-envelope normalization fix — the defensive 7.x patch this doc references).

Test plan

  • npm run format:check clean.
  • Pre-push validation (typecheck + build) passed.
  • CI green.

🤖 Generated with Claude Code

bokelley and others added 3 commits May 17, 2026 17:26
Standalone design proposal for how the SDK should expose AdCP 3.1
(canonical formats, version envelope, expanded capabilities) without
forcing adopters to think about v1 vs v2 shapes.

The proposal lays out: auto-negotiation per agent via supported_versions;
a single dual-shape Product type that always carries format_ids and
populates format_options when projectable; bidirectional v1<->v2
projection with a structured Diagnostic surface (FORMAT_PROJECTION_FAILED,
FORMAT_DECLARATION_DIVERGENT, CUSTOM_FORMAT_FETCH_FAILED) that's never
logger-only per the spec's resolution-order amendment; a centralized
fetchFormatSchema utility implementing the normative custom-format fetch
contract; versioned codegen under src/lib/types/v3.0/ and v3.1/.

Migration path: 7.x defensive fixes (PR #1807 already shipped); 8.0
ships canonical-formats enablement; 8.x hardens; 9.0 removes v1 paths
when AdCP 4.0 lands.

Explicit non-goals and open questions documented for reviewer feedback.
Draft for review only — no code changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brian's review feedback on the draft: canonical formats aren't "new
functionality" — they're the V2 way of thinking about creatives, and
better. The "dual-shape Product exposed on the public type" framing was
the engineer's compromise; the right product position is to flip every
SDK consumer to V2 in 8.0 and make the SDK responsible for translating
v1 sellers to look V2-shaped.

Changes:

- Reframe "The question" as "The position" — 8.0 commits to V2 as the
  mental model, not as an additive surface.
- Add a "What V2 actually changes for the buyer's mental model" section
  capturing Brian's insight: V1 conflated "what does this publisher's
  inventory accept?" with "what can this creative agent build?" V2
  separates them — format_options lives on Product (tied to inventory),
  creative.supported_formats lives on creative-agent capabilities.
- Goal #1 changes from "zero forced migration" to "V2 is the mental
  model." Goal #2 changes from "one Product type carrying both
  representations" to "the SDK teaches V2 by hiding v1." Goal #3 drops
  the "opt-in for new functionality" framing entirely.
- "Dual-shape Product" architecture section becomes "V2-shaped Product,
  transparent v1 normalization." Product no longer carries top-level
  format_ids; legitimate ops needs (tracing the v1 named format) read
  format_options[i].v1_format_ref.
- Migration table updated: 8.0 is "the major that flips you to V2";
  adopter action is a search-and-replace from format_ids[] to
  format_options[]. 9.0 becomes mostly an internal retirement, not a
  second mental-model migration.
- Open question #3 (read-time vs lazy projection) resolved to read-time
  — lazy projection is incompatible with V2-only public types.
- New non-goal: "We don't expose v1 vocabulary on the public type."
  Explicit about why dual-shape is the wrong compromise.

The wire-level facts didn't change; the SDK's product position did.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lessons from the v1↔v2 projection prototype at PR #1815 land as a
new top-of-doc section, plus three subsections updated inline:

1. New "Lessons from the prototype" section captures the 8
   architectural refinements:
   - Catalog source-of-truth is 4-tier (AAO base + community mirror
     + adagents.json#/formats + per-placement format_options[]).
     The doc had treated it as 1-tier ("fetch from
     list_creative_formats").
   - Multi-size lossy advisory is a new diagnostic class emitted
     alongside the v1 emit, not instead of. Structural invariant
     relaxed.
   - Symmetric "not yet" buckets: v1_translatable: false (v2 side)
     and catalog_lacks_canonical_annotation (v1 side).
   - canonical_formats_only semantically overloaded.
   - Quantified coverage: 41/57 (72%) clean v1→v2; 5/5 single-size
     +7-emit multi-size v2→v1.
   - Registry reverse-lookup dormant at 3.1 GA (registry shrank to
     7 pure-structural fallbacks).
   - AAO convergence pattern resolved per-publisher format-id sprawl.
   - format_kind orthogonal to dimensional identity; affects
     codegen and adopter mental model.

2. Projection-layer section rewritten:
   - Documents the 4-tier catalog model with merge order (most-
     specific-wins).
   - Full resolution order for both v1→v2 and v2→v1 directions.
   - Diagnostic union expanded to include all 7 codes (3 spec, 4
     SDK-local) with prose for each.

3. V2-shaped Product table updated:
   - Single-size vs multi-size vs responsive vs canonical_formats_only
     vs v1_translatable: false outbound behavior all spelled out.
   - Multi-size fan-out documented as the productized path.

4. Migration path gains a "Coverage adopters can expect at 8.0 GA"
   subsection citing the prototype's coverage report.

Doc shape preserved (option C of the three options I offered):
Lessons section + sharpen affected subsections inline; no wholesale
rewrite of unaffected sections.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley force-pushed the bokelley/v3.1-sdk-design branch from 0cfc82d to 7b490bb Compare May 17, 2026 21:33
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