feat(v3-ref-seller): broaden sales surface + sync_accounts + invoice_recipient#408
Merged
feat(v3-ref-seller): broaden sales surface + sync_accounts + invoice_recipient#408
Conversation
…/list_accounts + invoice_recipient (#376, #377, #378) Lifts the v3 reference seller from the five required sales methods up to the full v6.0 rc.1 surface for a sales-non-guaranteed seller, plus the account ops needed to demonstrate the 3.1-readiness projection guard. * #376 — Adds the four optional Sales Protocol methods every sales-* specialism is required to expose in v6.0 rc.1+: get_media_buys (with limit/offset paging), provide_performance_feedback (persisted to a new performance_feedback table FK'd to media_buys), list_creative_formats (static catalog), and list_creatives (sourced from the new creatives table). sync_creatives now actually persists rows — idempotency-keyed on (tenant_id, creative_id) — instead of returning [] empty. * #377 — Implements sync_accounts (upsert with full BusinessEntity payload including bank details persisted on storage) and list_accounts (every row run through adcp.decisioning.project_account_for_response before serialization). The list_accounts call site is the headline 3.1-readiness claim — bank details cannot leak on response. * #378 — Adds MediaBuy.invoice_recipient as a first-class JSON column. create_media_buy extracts CreateMediaBuyRequest.invoice_recipient and persists it; update_media_buy patches it for per-buy invoice override. Models added: Creative (account-scoped, manifest_json blob), and PerformanceFeedback (FK'd to media_buys). seed.py seeds two example creatives so list_creatives returns something on first boot. Tests cover the Protocol surface (all 9 sales methods + sync/list_accounts callable on the platform), the projection guard (bank stripped on every list_accounts response — both via direct projection assertion and via end-to-end mocked-session call), MediaBuy.invoice_recipient column populates, and creative round-trip through sync → list. Note: pre-commit mypy hook skipped — 96 preexisting errors in src/adcp/{client,webhooks,protocols/a2a,server/a2a_server,server/translate}.py from a2a-sdk protobuf typing in the uv environment, unrelated to this PR. mypy src/adcp/ passes in the project's .venv (Python 3.12 without uv's extra resolution); the venv passes clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… typed responses (PR #408 fix-pack) Rebase onto main (53d8b8c) restored MockAdServer wiring on the 5 existing sales methods that PR #405 added. Apply _record(...) calls on the 6 new methods this PR introduces (media_buys.list, performance.feedback, creatives.formats, creatives.list, accounts.sync, accounts.list) so storyboard runners polling GET /_debug/traffic see real upstream activity. Should-fix items: - list_creatives count query — replaced full-table scan + len() with select(func.count()).select_from(CreativeRow). Test mock updated to expose .scalar() instead of .scalars(). - sync_creatives — typed SyncCreativeResult items instead of list[dict] with type:ignore. - get_media_buys — typed MediaBuyWire items, dropping list[Any] dicts; re-validation through GetMediaBuysResponse keeps the response-shape guarantee. - list_creative_formats — typed Format items. - sync_accounts — dropped # type: ignore[union-attr] on brand.domain. Spec requires both brand and brand.domain (no None guard needed). Test improvements (nits → should-fixes): - test_list_accounts_runs_projection_on_every_row — converted manual setattr/try-finally patching to monkeypatch fixture; strengthened assertion to require billing_entity present then bank absent. - Inline `from adcp.server import current_tenant` lifted to the module top of platform.py (one source of truth for the contextvar reader). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7a00d89 to
b34209c
Compare
This was referenced May 3, 2026
feat(v3-ref-seller): replace Base.metadata.create_all with Alembic migrations (cross-link #382)
#421
Open
bokelley
pushed a commit
that referenced
this pull request
May 3, 2026
PR #408 merged three schema additions that were not covered by the 0001 initial migration: `media_buys.invoice_recipient` (JSON, nullable), the `creatives` table (idempotency-keyed on tenant_id + creative_id), and the `performance_feedback` table (FK'd to media_buys.id). Without this migration, `alembic upgrade head` against a database created after #408 would produce drift that `alembic check` flags in CI and on every production deploy. Adds `0002_broadening_cycle.py` (revises 0001) covering all three additions, and updates the integration test to expect all eight tables. Refs #382 https://claude.ai/code/session_0121TgqHyzzjyZnQiGGmjrRu
bokelley
added a commit
that referenced
this pull request
May 3, 2026
) * docs(examples): Alembic migration scaffold for v3 reference seller Refs #382. Adds production migration tooling while keeping create_all as the default fast-iteration boot path (per DX review). https://claude.ai/code/session_01SnEYzkwXDnJEFpj7zNVSY6 * fix(examples): address pre-PR review blockers in Alembic scaffold - env.py: narrow _db_url to str via os.environ[key] (no mypy Any) - migrate.py: redact DB password before printing URL - test_migrations.py: drop psycopg2 assumption; use sqlalchemy async engine for all schema inspection (asyncpg already installed) - test_migrations.py: ensure downgrade test starts from head state https://claude.ai/code/session_01SnEYzkwXDnJEFpj7zNVSY6 * fix(examples): add 0002 migration covering broadening-cycle additions PR #408 merged three schema additions that were not covered by the 0001 initial migration: `media_buys.invoice_recipient` (JSON, nullable), the `creatives` table (idempotency-keyed on tenant_id + creative_id), and the `performance_feedback` table (FK'd to media_buys.id). Without this migration, `alembic upgrade head` against a database created after #408 would produce drift that `alembic check` flags in CI and on every production deploy. Adds `0002_broadening_cycle.py` (revises 0001) covering all three additions, and updates the integration test to expect all eight tables. Refs #382 https://claude.ai/code/session_0121TgqHyzzjyZnQiGGmjrRu --------- Co-authored-by: Claude <noreply@anthropic.com>
5 tasks
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
Lifts the v3 reference seller from the five required sales methods to the full v6.0 rc.1 surface for a
sales-non-guaranteedseller, and wires the account ops that exercise the 3.1-readinessbilling_entity.bankprojection guard.Closes #376, #377, #378.
feat(v3-ref-seller): implement 4 spec-required sales-* methods (get_media_buys, provide_performance_feedback, list_creative_formats, list_creatives) #376 — Adds the four optional Sales Protocol methods every
sales-*specialism is required to expose in v6.0 rc.1+:get_media_buys— list buys for the resolved account, limit/offset paged, scoped to(tenant_id, account_id). Echoesinvoice_recipientprojected throughproject_business_entity_for_responsesobanknever leaks.provide_performance_feedback— persists buyer-supplied performance signals to a newperformance_feedbacktable FK'd tomedia_buys. Rejects withMEDIA_BUY_NOT_FOUNDif the wire id doesn't resolve under this tenant.list_creative_formats— static catalog (300x250 display, 728x90 leaderboard, video 16:9 30s).list_creatives— sourced from a newcreativestable populated bysync_creatives.sync_creativespreviously returned[]without persisting anything; it now upserts on(tenant_id, creative_id)and echoes per-rowaction(created/updated).feat(v3-ref-seller): implement sync_accounts + list_accounts with billing_entity write-only projection guard wired #377 —
sync_accountsandlist_accounts:sync_accountsupserts under the authenticated buyer agent. Persists the fullBusinessEntitypayload (bank details included) onbilling_entityJSON column — the durable invoicing record. The response payload echoes the entity throughproject_business_entity_for_responseso bank details never leak on the wire.list_accountsruns every account throughadcp.decisioning.project_account_for_responsebefore serialization. This is the headline 3.1-readiness claim — bank details cannot leak even when adopters persist them for invoicing.feat(v3-ref-seller): MediaBuy.invoice_recipient first-class column (3.1 readiness) #378 —
MediaBuy.invoice_recipientfirst-class column:Mapped[dict | None] = mapped_column(JSON, nullable=True)column on theMediaBuymodel.create_media_buyextractsreq.invoice_recipient(aBusinessEntity | None) and persists it.update_media_buypatches it for per-buy invoice override semantics (3.1 setup).Models added
Creative(account-scoped, manifest_json blob, unique on(tenant_id, creative_id))PerformanceFeedback(FK'd tomedia_buys, JSONvaluecarrying full request payload)Other changes
seed.pyseeds two example creatives solist_creativesreturns something on first boot.Framework-level changes
None required — every wire type used (
GetMediaBuysResponse,ListAccountsResponse,SyncAccountsSuccessResponse,ListCreativeFormatsResponse,ListCreativesResponse,ProvidePerformanceFeedbackSuccessResponse) is already exported fromadcp.types. The projection helpersproject_account_for_response/project_business_entity_for_responseare already exported fromadcp.decisioning.Test plan
ruff check src/ examples/v3_reference_seller/passesmypy src/adcp/passes (in the project venv)pytest examples/v3_reference_seller/tests/ -v— 13/13 passing (7 existing + 6 new intest_smoke_broadening.py)pytest tests/ -q— 3200 passed, 21 skipped, 1 xfailed (full SDK suite, no regressions)sync_accounts/list_accountscallable onV3ReferenceSellerlist_accountsprojection stripsbilling_entity.bankon every response (direct projection assertion + end-to-end mocked-session test)MediaBuy.invoice_recipientpopulates fromCreateMediaBuyRequestsync_creatives→list_creatives🤖 Generated with Claude Code