Conversation
…dth sprint Batch 1)
First batch of the breadth-sprint per the parity audit (8 missing
specialism Protocols). Ports two from JS reference at
``src/lib/server/decisioning/specialisms/{signals,audiences}.ts``.
New Protocols:
* ``SignalsPlatform`` (src/adcp/decisioning/specialisms/signals.py) —
covers ``signal-marketplace`` (third-party data brokers like
LiveRamp, Oracle Data Cloud) AND ``signal-owned`` (first-party data
providers like publisher first-party data, retailer customer-graph).
Two methods: ``get_signals`` (sync catalog discovery) and
``activate_signal`` (provisioning onto destination platforms).
Activation is sync at the wire level — no Submitted arm. Long-running
activation pipelines (identity-graph match: 5-30 min) return the
success-arm shape with ``deployments`` rows in ``pending`` state and
drive lifecycle via ``ctx.publish_status_change``.
* ``AudiencePlatform`` (src/adcp/decisioning/specialisms/audience.py) —
covers ``audience-sync``. Two methods: ``sync_audiences`` (wire-required;
push first-party CRM audiences with delta upsert) and
``poll_audience_statuses`` (adopter-internal; batch state read for
cross-platform orchestration). Match-rate computation runs in the
adopter's background; per-audience terminal state via
``publish_status_change``.
Required-method coverage in ``REQUIRED_METHODS_PER_SPECIALISM``:
* ``signal-marketplace``, ``signal-owned`` — both gate on
``{get_signals, activate_signal}`` (shared Protocol).
* ``audience-sync`` — gates only on ``sync_audiences`` since
``poll_audience_statuses`` is adopter-internal.
Public re-exports added at ``adcp.decisioning.__all__``:
``SalesPlatform``, ``SignalsPlatform``, ``AudiencePlatform``. Closes
a small drift bug — ``SalesPlatform`` was referenced in the
quickstart docstring but never actually re-exported through the
public surface.
Test coverage in ``tests/test_decisioning_specialisms.py`` (13 new
tests):
* ``runtime_checkable`` conformance per Protocol.
* ``validate_platform`` required-method enforcement.
* Public-export pinning (drift-guard against ``adcp.decisioning.__all__``).
* Cross-specialism composition (claiming sales-* + signal-* together
satisfies both Protocols).
* ``audience-sync`` minimal-implementation passes (only
``sync_audiences`` required).
One existing test updated:
``test_validate_platform_warns_on_unenforced_spec_specialism``
switched its canonical "spec-recognized but unenforced" example from
``signal-marketplace`` (now enforced) to ``creative-ad-server`` (still
pending until Batch 2 ships Creative Protocols).
Remaining specialism Protocols (creative-*, governance-*,
brand-rights, content-standards, property-lists, collection-lists)
are queued for subsequent breadth-sprint PRs.
2221 tests pass (up from 2208).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… brittle test Two converging expert-review findings: P1 (audience.py:45,53): two string-literal globals were port artifacts from the JS reference that never compiled to anything meaningful in Python: Audience = "SyncAudiencesAudience" SyncAudiencesRow = "SyncAudiencesSuccessResponse.audiences[number]" The first is a string constant masquerading as a forward-ref but unimported; the second is TypeScript indexed-access syntax (``T['audiences'][number]``) which has no Python meaning and would raise NameError if ``typing.get_type_hints`` ever resolved it. Replaced with a comment block pointing adopters at the canonical ``adcp.types.SyncAudiencesAudience`` / ``adcp.types.SyncAudiencesSuccessResponse`` imports. P2 (test_decisioning_specialisms.py:284): the smoke check pinned on ``hasattr(SalesPlatform, '_is_protocol')`` — a private CPython typing internal that's brittle against typing-module changes. Replaced with an ``isinstance`` check against a minimal-but-complete shim that exercises all 9 SalesPlatform methods. Same invariant via a durable public assertion. Punt list (P2/P3 reviewer findings) deferred to follow-up: * JS exports ``Audience``, ``SyncAudiencesRow``, ``AudienceStatus`` type aliases that Python doesn't re-export. Adopters import directly from ``adcp.types`` today; not blocking. * Cross-language adopter-shape divergence on ``sync_audiences`` (JS returns rows, Python returns full response). Pick one in a follow-up RFC; both produce the same wire output. 13 tests pass; mypy + ruff clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 1, 2026
Merged
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>
This was referenced May 1, 2026
bokelley
added a commit
that referenced
this pull request
May 1, 2026
* fix(decisioning): handler shims for every non-sales wire tool The Emma DX smoke test (AudioStack creative-generative agent) caught a load-bearing gap: the breadth-sprint shipped 10 Protocol classes + REQUIRED_METHODS coverage at the static layer, but ``PlatformHandler`` only had 9 sales-* shims. Every non-sales wire tool (``build_creative``, ``get_signals``, ``check_governance``, ``get_brand_identity``, ``list_content_standards``, ``create_property_list``, ``create_collection_list``, ...) was 404 at the wire even though capabilities advertised the slug, ``validate_platform`` reported green, and the wire-tool registry at ``mcp_tools.py`` already mapped each tool name to its Request type. A creative-generative adopter wrapping AudioStack (Emma-style real-world test) could implement the Protocol shape correctly, ``serve()`` started, ``tools/list`` returned the advertised set — but ``tools/call name="build_creative"`` got ``Unknown tool: build_creative``. Silent buyer-facing failure with the framework reporting green at boot. Fix: 31 new shims following the existing sales-* template, one per wire tool the breadth-sprint Protocols cover. Per-Protocol-family advertised tool sets, kept as separate frozensets for readability and future selective filtering. The ``advertised_tools`` ClassVar is the union of all 9 sets (40 tools total). New runtime gate: ``_OPTIONAL_PLATFORM_METHODS`` + ``_require_platform_method`` helper. For methods marked optional on the per-specialism Protocol (``preview_creative`` on CreativeBuilderPlatform, ``get_media_buy_artifacts`` / ``get_creative_features`` on ContentStandardsPlatform), the shim pre-checks ``hasattr(platform, method_name)`` and surfaces ``AdcpError(code='UNSUPPORTED_FEATURE')`` to buyers when the adopter chose not to wire the method. Without this, AttributeError would get wrapped to ``INTERNAL_ERROR`` — adopter contract violation, not buyer-fixable. Required methods are caught at server boot by ``validate_platform``; the optional set complements that gate at runtime. Account-resolution: every new shim uses ``getattr(params, "account", None)`` — most non-sales tools don't carry ``account`` on the wire (catalog reads, governance plans, brand-rights queries). The ``AccountStore`` impl handles the no-ref case per its resolution mode (``'derived'`` tolerates None; ``'implicit'`` raises ``AUTH_INVALID``). Arg-projection: ``sync_audiences`` arg-projects ``audiences=params.audiences`` to match the JS-side adopter ergonomic (the AudiencePlatform method signature is ``sync_audiences(audiences, ctx)`` per PR #332). Test coverage in ``tests/test_decisioning_handler_shims.py`` (42 new tests): * ``advertised_tools`` covers every spec wire tool (drift guard against accidentally shipping unrouted advertised tools). * Parametrized: every advertised non-sales tool has a shim method on PlatformHandler. * End-to-end shim → ``_invoke_platform_method`` → platform method for one tool per Protocol family. * ``sync_audiences`` arg-projection regression test. * ``UNSUPPORTED_FEATURE`` surface for optional methods. * AudioStack DX regression test — pinned by name so the Emma fix doesn't silently regress. 2308 tests pass (up from 2266). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(decisioning): expert P0/P1 fixes on handler shims P0: - F12 auto-emit on 12 webhook-eligible shims (activate_signal, get_signals, get_creative_delivery, sync_audiences, get_brand_identity, get_rights, acquire_rights, all 5 property_list ops). Mirrors the sales-* shim pattern. Fires only when (a) request schema allows push_notification_config and the buyer registered a URL, and (b) method_name is in SPEC_WEBHOOK_TASK_TYPES. - build_creative bare-manifest / list / envelope projection helper (_project_build_creative) — JS-side projectBuildCreativeReturn parity. The CreativeBuilderPlatform.build_creative Protocol declares 3 return arms (single CreativeManifest, list, fully-shaped success envelope); the wire envelope has only 2 success arms ({creative_manifest} vs {creative_manifests}). Bare-manifest/list returns now project to wire shape instead of failing oneOf validation. - sync_audiences list arm projection (_project_sync_audiences) — wire envelope is {audiences: [...]} but Protocol allows list-of-rows ergonomic. Mirrors JS-side wrapping. - build_creative gated via _require_platform_method so a sales-only adopter routing here surfaces UNSUPPORTED_FEATURE instead of leaking AttributeError -> INTERNAL_ERROR. P1: - update_rights shim (BrandRightsPlatform mutation: extend term, change scope, revoke). Added to _BRAND_RIGHTS_ADVERTISED_TOOLS. - acquire_rights docstring corrected from 3-arm to 4-arm (acquired/pending/rejected/error). - Governance/brand-rights/content-standards shims switched from getattr(params, "account", None) to explicit None for the schemas that declare additionalProperties:false on the request type — the account field is forbidden, getattr was a foot-gun for future drift. get_media_buy_artifacts and get_creative_features keep getattr since their schemas DO carry account. Tests: - _project_build_creative and _project_sync_audiences arm coverage. - F12 auto-emit fires on get_signals, acquire_rights, get_creative_delivery, sync_audiences (with projected envelope). - update_rights routes through the shim. - update_rights does NOT auto-emit (not in SPEC_WEBHOOK_TASK_TYPES). - property_list ops do NOT auto-emit (wire schema forbids push_notification_config); regression-pin so a future schema change surfaces here. - build_creative UNSUPPORTED_FEATURE when platform doesn't implement. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Breadth sprint Batch 1 per the parity audit. Ports two of the eight missing specialism Protocols from the JS reference at `src/lib/server/decisioning/specialisms/{signals,audiences}.ts`.
New:
Tests (13 new):
One existing test updated: `test_validate_platform_warns_on_unenforced_spec_specialism` switched its canonical "unenforced spec slug" example from `signal-marketplace` (now enforced) to `creative-ad-server` (still pending until Batch 2).
Test plan
Remaining breadth-sprint queue
Per the parity audit, remaining specialism Protocols for Batch 2+:
Release plan
Accumulates into the held release-please PR #328 alongside foundation (#316), codemod ergonomics (#329), parity rename + Tier 1 docs (#330), and F12 auto-emit (#331). All five ship together in 4.4.0 once salesagent validates.
🤖 Generated with Claude Code