Skip to content

feat(compat): AdapterPair pattern + v2.5 sync_creatives (stage 4)#661

Closed
bokelley wants to merge 1 commit into
claude/versioned-schemas-stage-3from
claude/versioned-schemas-stage-4
Closed

feat(compat): AdapterPair pattern + v2.5 sync_creatives (stage 4)#661
bokelley wants to merge 1 commit into
claude/versioned-schemas-stage-3from
claude/versioned-schemas-stage-4

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Stacked on #660 (which is stacked on #659).

Summary

Stage 4 of the versioned-schema-validation port. Replaces the heuristic spec_compat_hooks() model with a typed per-tool adapter registry.

  • New package adcp.compat.legacy with AdapterPair dataclass, registry, and LEGACY_ADAPTER_VERSIONS = (\"2.5\",) — mirrors the TS SDK's src/lib/adapters/legacy/v2-5/.
  • First concrete adapter: adcp.compat.legacy.v2_5.sync_creatives. Three wire-shape coercions: bare format_id → structured, asset_type inference, image-without-dims → url demotion.
  • Envelope helper accepts both native + legacy versions in its supported set.
  • create_tool_caller routes legacy-versioned requests through the adapter before current-schema validation. Missing adapter for a tool at a legacy version → INVALID_REQUEST before handler dispatch.

Why this works

The adapter sits between wire decoding and current-schema validation. A v2.5 buyer sends their wire shape, the adapter rewrites it to v3 shape, and the existing v3 schema + Pydantic model validation runs. A buggy translator surfaces as INVALID_REQUEST with a field-level pointer — the same error mode adopters already know — instead of leaking through as opaque data.

Response side runs normalize_response (if the adapter declares one) after validation, so the buyer sees their expected shape. sync_creatives doesn't need this; later adapters (preview_creative) will.

Deferred to Stage 4b

Real v2.5 schema bundle. Upstream CDN (adcontextprotocol.org/protocol/) doesn't ship v2.5 tarballs; the JS SDK pins to a GitHub commit SHA. Wire that fetch in a follow-up. Today the adapter validates against v3 output only.

Test plan

  • pytest tests/ — 4453 passed (one unrelated flaky timing test deselected)
  • 25 new tests across two new files
  • ruff check + mypy
  • No regressions on existing dispatcher behaviour: v3 buyers bypass the adapter path

Stage 5 will

Port the remaining v2.5 adapters (get_products, create_media_buy, update_media_buy, list_creative_formats, preview_creative) and add a deprecation warning on spec_compat_hooks() pointing at the new layer.

🤖 Generated with Claude Code

@bokelley bokelley force-pushed the claude/versioned-schemas-stage-3 branch from fc7dd4c to d0c1944 Compare May 11, 2026 10:05
@bokelley bokelley force-pushed the claude/versioned-schemas-stage-4 branch from df85e26 to afa8455 Compare May 11, 2026 10:05
Stage 4 of the versioned-schema-validation port. Replaces the heuristic
``spec_compat_hooks()`` model with a typed per-tool adapter registry.

What changed:

* New package ``adcp.compat.legacy`` with the ``AdapterPair`` dataclass,
  registry (``get_legacy_adapter`` / ``list_legacy_adapter_tools``), and
  the ``LEGACY_ADAPTER_VERSIONS`` constant. Mirrors the TS SDK's
  ``src/lib/adapters/legacy/v2-5/``.
* First concrete adapter: ``adcp.compat.legacy.v2_5.sync_creatives``.
  Three wire-shape coercions (bare-string ``format_id`` → structured,
  ``asset_type`` inference, ``image``-without-dims → ``url`` demotion).
* ``validation.envelope.SUPPORTED_WIRE_VERSIONS`` now includes legacy
  versions, so ``detect_wire_version`` accepts both native and legacy
  claims.
* ``create_tool_caller`` routes legacy-versioned requests through the
  adapter: ``adapt_request`` runs *after* version detection and *before*
  current-schema validation, so a buggy translator surfaces as
  ``INVALID_REQUEST`` with a field-level pointer. A legacy version
  with no adapter for the requested tool raises ``INVALID_REQUEST``.
* Response side: optional ``normalize_response`` callable runs after
  current-schema validation. ``sync_creatives`` has the same response
  shape across v2.5/v3 so leaves it ``None``.

Deferred (Stage 4b): real v2.5 schema bundle. Upstream CDN doesn't
ship v2.5 tarballs; the JS SDK pins a GitHub commit SHA. Wire that
fetch in a follow-up. Today the adapter validates against the v3
output only.

Tests (25 new): registry contract, every v2.5 sync_creatives coercion
via the public adapter surface, end-to-end dispatcher routing including
missing-adapter and adapter-raises cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley force-pushed the claude/versioned-schemas-stage-4 branch from afa8455 to 0eb2b06 Compare May 11, 2026 10:13
@bokelley bokelley deleted the branch claude/versioned-schemas-stage-3 May 11, 2026 10:14
@bokelley bokelley closed this May 11, 2026
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