Skip to content

feat(examples): dual-emit v1+v2 formats on reference seller (#741 polish)#847

Merged
bokelley merged 1 commit into
mainfrom
bokelley/issue-741-storyboards-3-1
May 24, 2026
Merged

feat(examples): dual-emit v1+v2 formats on reference seller (#741 polish)#847
bokelley merged 1 commit into
mainfrom
bokelley/issue-741-storyboards-3-1

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Summary

Closes the conformance gap between the canonical-formats projection layer (#741) and the AdCP storyboard runner's reference seller. Half 1 + half 2 of #741 shipped the projection layer; this PR makes examples/seller_agent.py actually exercise it.

What changed

Every product in examples/seller_agent.py's PRODUCTS catalog now carries BOTH wire shapes:

  • v1format_ids: [{agent_url, id}] (unchanged).
  • v2format_options: [ProductFormatDeclaration] with format_kind="image", sized params (width/height + asset_source + image_formats), and v1_format_ref pointing back at the v1 format_id.

A new _image_format_options(...) helper builds the v2 declaration; the docstring documents the dual-emit pattern as the template adopters should follow during the 3.0 → 3.1 migration window.

Why this matters

  • 3.0 buyers continue reading format_ids[] from the wire, unchanged.
  • 3.1 buyers now see real format_options[] declarations — the storyboard runner exercises that path end-to-end against the reference seller for the first time.
  • SDK projection round-trips. project_product_to_v1 on each product emits refs that round-trip back to the source declaration via find_declaration_by_v1_format_id.

Tests

  • New tests/test_seller_agent_dual_emit.py pins 3 invariants over the 6 products:
    1. Every product dual-emits.
    2. Every v2 declaration's v1_format_ref matches the product's v1 format_ids.
    3. SDK projection rebuilds the v1 emit from the v2 surface with zero advisories.
  • 5258 tests pass locally.
  • Both existing storyboard runs still pass — the v2 surface is purely additive on the wire.

Polish PR backlog (remaining)

Tracked as separate PRs after this one:

Refs: #741

…nce seller

Closes the conformance gap between the canonical-formats projection
layer (#741) and the reference seller the AdCP storyboard runner
targets. Previously, ``examples/seller_agent.py`` published
``Product.format_ids[]`` only (v1) — the SDK's v2 surface shipped
but was never exercised end-to-end by the storyboard suite.

## What changed

Every product in the reference seller's ``PRODUCTS`` catalog now
carries both wire shapes:

* **v1**: ``format_ids: [{agent_url, id}]`` (unchanged).
* **v2**: ``format_options: [ProductFormatDeclaration]`` with
  ``format_kind="image"``, sized ``params`` (width/height +
  ``asset_source`` + ``image_formats``), and ``v1_format_ref``
  pointing back at the v1 ``format_id``.

A small ``_image_format_options(...)`` helper builds the v2
declaration so each product entry stays a one-keyword addition.
The helper docstring documents the dual-emit pattern as the
template adopters should follow during the 3.0 → 3.1 migration.

## Why

* 3.0 buyers continue reading ``format_ids[]`` from the wire.
* 3.1 buyers now see real ``format_options[]`` declarations and
  can route on ``format_kind`` plus typed ``params``.
* The storyboard runner exercises this path end-to-end against
  the reference seller for the first time.

## Tests

5258 pass locally. ``tests/test_seller_agent_dual_emit.py`` pins
3 invariants over the 6 products:

1. Every product dual-emits.
2. Every v2 ``v1_format_ref`` matches the product's v1
   ``format_ids`` (round-trip anchor).
3. SDK projection rebuilds v1 emit from v2 with zero advisories.

Refs: #741
Copy link
Copy Markdown

@aao-ipr-bot aao-ipr-bot Bot left a comment

Choose a reason for hiding this comment

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

LGTM. The right shape for the 3.0 → 3.1 migration window: v1 format_ids[] stays load-bearing on the wire, v2 format_options[] is purely additive, and v1_format_ref is what makes the SDK's projection round-trip.

Things I checked

  • Dict literal produced by _image_format_options (examples/seller_agent.py:172-208) validates against ProductFormatDeclarationformat_kind="image" is in CanonicalFormatKind (canonical_format_kind.py:11), params keys (sizes, asset_source, ssl_required, image_formats) miss every credential-shaped suffix the _reject_credential_shaped_extras gate (canonical_decl.py:238) screens for. No fail-closed surprise at validation time.
  • All 6 products dual-emit with a v1↔v2 anchor: each format_options[0].v1_format_ref[0] matches the product's format_ids[0]. The three test invariants (tests/test_seller_agent_dual_emit.py:55, 64, 81) pin exactly that, plus zero advisories from the projection.
  • products_response(PRODUCTS, cache_scope="public") at examples/seller_agent.py:502 and :512 is the wire sink — the new field flows through the standard serializer, no bespoke path.
  • Test imports the seller via importlib.util.spec_from_file_location and cleans up sys.modules in finally (test file:46-48). Hermetic; doesn't leak the seller_agent name into other tests.
  • Happy path unchanged for 3.0 buyers — format_options is additive, no existing field touched.

Minor nits (non-blocking)

  1. Import reshuffle hitched to the feature commit. INSECURE_ALLOW_ALL moved into the main adcp.server import block (examples/seller_agent.py:23) — pure ordering, unrelated to dual-emit. Could have lived in its own commit, but trivial enough not to matter.
  2. _image_format_options is single-size by construction. Signature takes width, height and emits one sizes[] entry. Fine for the reference catalog, but the name suggests broader applicability — adopters copying it as a template will need to widen it the first time they declare a multi-size declaration. Worth either renaming (_single_size_image_format_options) or accepting a sizes iterable when this gets lifted out of examples/ into adcp.canonical_formats.fixtures (item D on the deferred backlog).

Approving.

@bokelley bokelley merged commit 7731f53 into main May 24, 2026
23 checks passed
@bokelley bokelley deleted the bokelley/issue-741-storyboards-3-1 branch May 24, 2026 12:45
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