Skip to content

feat(decisioning): CreativeBuilderPlatform + CreativeAdServerPlatform (breadth sprint Batch 2)#333

Merged
bokelley merged 2 commits intomainfrom
bokelley/decisioning-creative-protocols
May 1, 2026
Merged

feat(decisioning): CreativeBuilderPlatform + CreativeAdServerPlatform (breadth sprint Batch 2)#333
bokelley merged 2 commits intomainfrom
bokelley/decisioning-creative-protocols

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented May 1, 2026

Summary

Breadth sprint Batch 2 — ports two creative-archetype Protocols from JS reference (commits `841616d7` F13 + `bca20dfb` F16-F17).

New:

  • `CreativeBuilderPlatform` — covers `creative-template` (stateless transform: Bannerflow, Celtra) AND `creative-generative` (brief-to-creative AI: Pencil, Omneky). Unified per JS F13 — wire shape doesn't distinguish "transform a template" from "generate from a brief." Required: `build_creative`. Optional: `preview_creative`, `refine_creative`, `sync_creatives`.
  • `CreativeAdServerPlatform` — covers `creative-ad-server` (Innovid, Flashtalking, GAM-creative). Stateful library + per-creative pricing + tag generation. Required: `build_creative`, `preview_creative`, `list_creatives`, `get_creative_delivery`. Optional: `sync_creatives`.
  • `RefinementMessage` TypedDict for `refine_creative`.
  • REQUIRED_METHODS entries for all three slugs (`creative-template`, `creative-generative`, `creative-ad-server`).
  • Public exports in `adcp.decisioning.all`.

Tests (11 new, 24 total in file):

  • runtime_checkable minimal-vs-full conformance per Protocol.
  • validate_platform required-method enforcement.
  • Public-export drift guard.
  • Contract pin: builder slugs share method set.
  • Architectural distinction: ad-server's required set is a strict superset of builder's (`{preview_creative, list_creatives, get_creative_delivery}` extra).
  • `RefinementMessage` TypedDict smoke test.

One existing test updated: canonical "spec-recognized but unenforced" example switched from `creative-ad-server` (now enforced) to `brand-rights` (still pending until subsequent batches).

Wire-spec note: `build_creative` is sync at the wire level — the per-tool `build-creative-response.json` `oneOf` doesn't include a Submitted arm (spec inconsistency tracked as `adcontextprotocol/adcp#3392`). Until that lands, slow generation pipelines await in-request; status changes flow via `publish_status_change`. Same posture as JS reference.

Test plan

  • `pytest tests/test_decisioning_specialisms.py tests/test_decisioning_dispatch.py` — 60 passed (24 specialism + 36 dispatch)
  • `pytest tests/` — 2232 passed (up from 2221)
  • `mypy src/adcp/decisioning/` clean
  • Pre-commit gates pass (black/ruff/mypy/bandit)

Remaining breadth-sprint queue

  • `CampaignGovernancePlatform` (governance-aware-seller, governance-spend-authority, governance-delivery-monitor)
  • `BrandRightsPlatform`, `ContentStandardsPlatform`
  • `PropertyListsPlatform`, `CollectionListsPlatform`

Release plan

Accumulates into the held release-please PR #328 alongside foundation (#316), codemod ergonomics (#329), parity rename (#330), F12 (#331), and Signals/Audience (#332). Six PRs ship together in 4.4.0 once salesagent validates.

🤖 Generated with Claude Code

bokelley and others added 2 commits April 30, 2026 21:10
… (breadth sprint Batch 2)

Second batch of the breadth-sprint per the parity audit. Ports two
creative-archetype Protocols from JS reference at
``src/lib/server/decisioning/specialisms/{creative,creative-ad-server}.ts``
(commits ``841616d7`` F13 / ``bca20dfb`` F16-F17).

New Protocols:

* ``CreativeBuilderPlatform``
  (src/adcp/decisioning/specialisms/creative.py) — covers
  ``creative-template`` (stateless transform — Bannerflow, Celtra)
  AND ``creative-generative`` (brief-to-creative AI — Pencil,
  Omneky, AdCreative.ai). Per JS commit ``841616d7`` (F13), the v6
  preview's separation of CreativeTemplatePlatform and
  CreativeGenerativePlatform had no meaningful interface distinction,
  so they're unified here.

  Required: ``build_creative``. Optional (present-or-absent, framework
  surfaces ``UNSUPPORTED_FEATURE`` to buyers when missing):
  ``preview_creative``, ``refine_creative``, ``sync_creatives``.

  ``build_creative`` returns a discriminated arm per
  ``BuildCreativeReturn``:
  ``CreativeManifest | Sequence[CreativeManifest] | BuildCreativeSuccessResponse``.
  Adopters route on ``req.target_format_ids`` (multi) vs
  ``req.target_format_id`` (single). Returning the wrong arm shape
  surfaces as schema-validation failure on the wire response.

  ``RefinementMessage`` is exported as a TypedDict so adopters
  annotate ``refine_creative(refinement: RefinementMessage)``.

* ``CreativeAdServerPlatform``
  (src/adcp/decisioning/specialisms/creative_ad_server.py) — covers
  ``creative-ad-server``. Stateful library + per-creative pricing +
  tag generation (Innovid, Flashtalking, GAM-creative, CMP-style).

  Required: ``build_creative``, ``preview_creative``,
  ``list_creatives``, ``get_creative_delivery``. Optional:
  ``sync_creatives`` (hybrid sync/handoff for brand-suitability /
  S&P review).

  Distinct from CreativeBuilderPlatform — adopters that ALSO want
  library + tag generation + delivery reporting (a full ad server on
  top of the builder) declare ``creative-ad-server`` instead.
  Multi-archetype omni agents are rare; the recommended pattern is
  to front each archetype as a separate tenant via TenantRegistry.

Required-method coverage in ``REQUIRED_METHODS_PER_SPECIALISM``:

* ``creative-template``, ``creative-generative`` — both gate on
  ``{build_creative}`` (shared CreativeBuilderPlatform).
* ``creative-ad-server`` — gates on
  ``{build_creative, preview_creative, list_creatives,
  get_creative_delivery}``.

Public re-exports added at ``adcp.decisioning.__all__``:
``CreativeBuilderPlatform``, ``CreativeAdServerPlatform``,
``RefinementMessage``.

Test coverage in ``tests/test_decisioning_specialisms.py`` (11 new
tests, 24 total in the file):

* ``runtime_checkable`` minimal-vs-full conformance per Protocol.
* ``validate_platform`` required-method enforcement per slug.
* Public-export drift guard for the new Protocols.
* Contract pin for ``creative-template`` / ``creative-generative``
  shared method set (drift tracker).
* Architectural distinction test: ad-server's required set is a
  strict superset of builder's.
* ``RefinementMessage`` TypedDict smoke test.

One existing test updated: the canonical "spec-recognized but
unenforced" example switched from ``creative-ad-server`` (now
enforced) to ``brand-rights`` (still pending until Batch 3+ ships
the brand/governance/lists Protocols).

Remaining specialism Protocols (governance-aware-seller,
governance-spend-authority, governance-delivery-monitor, brand-rights,
content-standards, property-lists, collection-lists) are queued for
subsequent breadth-sprint PRs.

2232 tests pass (up from 2221).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…efine_creative + tighten typing

Three review findings on Batch 2 Creative Protocols:

P0 (wire-spec) — ``refine_creative`` was a hallucinated wire surface.
There is no ``refine-creative-*.json`` schema in ``schemas/cache/``
and no ``refine_creative`` wire tool. Per
``schemas/cache/media-buy/build-creative-request.json``, refinement
is invoked via ``build_creative`` itself with ``creative_id``
referencing the prior build (the request schema's "For refinement…"
description). Both the JS reference (``creative.ts``) and the
initial Python port preserved a ``refine_creative`` Protocol method
mirroring an earlier preview design. Dropped here:

* Removed ``refine_creative`` method from ``CreativeBuilderPlatform``
* Removed ``RefinementMessage`` TypedDict (was its parameter type)
* Removed both from ``adcp.decisioning`` re-exports + ``__all__``
* Removed corresponding tests
* Added regression test
  (``test_creative_builder_protocol_has_no_refine_creative``) so a
  future port doesn't re-add it without checking the spec

To file against the JS reference as a follow-up.

P1 (typing) — ``sync_creatives`` declared ``req: Any`` on both
creative Protocols. The shared ``SyncCreativesRequest`` exists in
``adcp.types`` (the sales-* archetypes use it). Typing as ``Any``
defeated the typed-input contract the rest of the SDK exposes.
Tightened to ``req: SyncCreativesRequest``; the per-archetype
narrowing comment kept (the shim narrows discriminated payload
fields, not the envelope).

P1 (regression test for adcp#3392) — added
``test_build_creative_response_has_no_submitted_arm`` that walks the
``BuildCreativeResponse`` Pydantic union and asserts no arm carries
``task_id``. When the spec inconsistency at adcp#3392 lands and the
``oneOf`` adds a Submitted variant, this test breaks and forces a
coordinated SDK update to the Protocol return type.

P2 (test rename) — ``test_creative_builder_runtime_checkable_minimal``
renamed to ``test_creative_builder_runtime_checkable_is_strict_structural_match``.
The "minimal" framing implied the test was checking adopter-shape
adequacy; it's actually checking that ``runtime_checkable`` strict
structural-AND semantics are documented. Same assertion, honest name.

Punt list (filed as follow-up):

* ``BuildCreativeResponse2`` is mislabeled in
  ``src/adcp/types/aliases.py`` — currently aliased as
  ``BuildCreativeErrorResponse`` but the class actually carries
  ``creative_manifests`` (multi-success arm). The real error arm is
  ``BuildCreativeResponse3``. Real type-system bug affecting
  ``guards.py``, ``test_type_aliases.py``,
  ``public_api_snapshot.json``. Out of scope here; needs its own PR
  with public-API surface changes.
* Python's ``REQUIRED_METHODS_PER_SPECIALISM`` is stricter than JS's
  ``SPECIALISM_REQUIREMENTS``. Deliberate Python-side hardening
  across all specialism Protocols (foundation + Batches 1-2); not
  unique to Creative.
* Multi-archetype omni guidance (single tenant claiming both
  ``creative-template`` and ``creative-ad-server``) is unenforceable
  in code today.

2233 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit 5921949 into main May 1, 2026
12 checks passed
bokelley added a commit that referenced this pull request May 1, 2026
…ionLists Protocols (breadth sprint Batch 4 — FINAL) (#335)

Final batch of the breadth-sprint per the parity audit. Ports the
remaining four specialism Protocols from JS reference. With this
PR, every spec specialism slug except ``governance-aware-seller``
has a Protocol class + REQUIRED_METHODS coverage.

New Protocols:

* ``BrandRightsPlatform`` — covers ``brand-rights``. Identity
  discovery + licensing. Required (3, sync): ``get_brand_identity``,
  ``get_rights``, ``acquire_rights``. ``acquire_rights`` returns a
  3-arm discriminated success union (acquired / pending / rejected)
  — rejection-as-data for spec-defined GRANT rejection, AdcpError
  only for buyer-fixable REQUEST rejection. Async outcomes for the
  ``pending`` arm flow via ``push_notification_config`` webhook
  (NOT a polling tool).

* ``ContentStandardsPlatform`` — covers ``content-standards``.
  Brand safety policies, content adjacency rules, per-creative
  compliance verification. Required (6): list/get/create/update,
  ``calibrate_content``, ``validate_content_delivery``. Optional
  (2, analyzer reads): ``get_media_buy_artifacts``,
  ``get_creative_features``.

* ``PropertyListsPlatform`` + ``CollectionListsPlatform``
  (specialisms/lists.py) — covers ``property-lists`` and
  ``collection-lists``. Parallel CRUD shapes (5 methods each, all
  required) with token-issuance semantics: ``create_*`` returns a
  per-seller-scoped ``fetch_token``, ``delete_*`` revokes it.
  Compromise-driven revocation MUST trigger the delete path.

Required-method coverage in ``REQUIRED_METHODS_PER_SPECIALISM``:
``brand-rights`` (3), ``content-standards`` (6),
``property-lists`` (5), ``collection-lists`` (5).

Public re-exports added at ``adcp.decisioning.__all__``:
``BrandRightsPlatform``, ``ContentStandardsPlatform``,
``PropertyListsPlatform``, ``CollectionListsPlatform``.

Test coverage in ``tests/test_decisioning_specialisms.py`` (13 new
tests, 43 total in the file):

* ``runtime_checkable`` conformance per Protocol.
* ``validate_platform`` enforcement per slug — including a
  security-relevant test that ``property-lists`` REQUIRES
  ``delete_property_list`` (revocation path) so adopters can't ship
  list-publishing without revocation primitives.
* Contract pins per slug.
* **Breadth-sprint completeness pin**:
  ``test_every_spec_slug_except_governance_aware_seller_is_enforced``
  asserts that ``SPEC_SPECIALISM_ENUM - REQUIRED_METHODS.keys()``
  yields exactly ``{governance-aware-seller, signed-requests}`` —
  the two slugs unenforced by design (composition claim and
  deprecated-moved-to-universal respectively).

One existing dispatch test updated:
``test_validate_platform_warns_on_unenforced_spec_specialism``
swapped its canonical "spec-recognized but unenforced" example
from ``brand-rights`` (now enforced) to ``governance-aware-seller``
(the only remaining unenforced spec slug — by design).

**Breadth sprint COMPLETE.** All 8 missing specialism Protocols
from the parity audit are now ported. 9 PRs total accumulating in
the held release PR #328:

* #316 foundation
* #329 codemod ergonomics
* #330 parity rename + Tier 1 docs
* #331 F12 auto-emit
* #332 Signals + Audience (Batch 1)
* #333 Creative Builder + AdServer (Batch 2)
* #334 Campaign Governance (Batch 3)
* #335 Brand + Content + Lists (Batch 4 — this PR)

Ready for salesagent validation against editable install before
tagging 4.4.0.

2252 tests pass (up from 2239).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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