Skip to content

feat(examples): v3 reference seller — runnable Tier 2 + v3 wiring (#357)#373

Merged
bokelley merged 3 commits into
mainfrom
bokelley/v3-reference-seller
May 3, 2026
Merged

feat(examples): v3 reference seller — runnable Tier 2 + v3 wiring (#357)#373
bokelley merged 3 commits into
mainfrom
bokelley/v3-reference-seller

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented May 2, 2026

Summary

Multi-file directory under `examples/v3_reference_seller/` that wires every Tier 2 / v3-supporting component into one runnable adopter pattern. Spec 3.0-compliant on the wire, 3.1-ready in architecture and storage.

The directory is the canonical fork-and-modify starting point for adopters building a v3 seller.

What's wired

Component Module
Multi-tenant root `src/models.py` (Tenant) + `src/tenant_router.py`
Tier 2 commercial-identity gate `src/models.py` (BuyerAgent) + `src/buyer_registry.py`
3.1-ready Account schema `src/models.py` (Account with billing_entity + reporting_bucket)
Audit trail `src/audit.py` (DbAuditSink)
Platform impl `src/platform.py` (sales-non-guaranteed)
Unified MCP+A2A binary `src/app.py` (`serve(transport="both")`)
Dev fixtures `seed.py`

What's NOT wired (yet)

Separable follow-ups; framework components exist:

  • HTTP-Sig verifier middleware in context_factory (gated on AAO brand.json registry)
  • Brand authorization Tier 3 (gated on ADCP #3690)
  • Postgres TaskRegistry / WebhookDeliverySupervisor swap (one-line change)
  • Alembic migrations (currently `create_all` at boot)
  • Admin CRUD REST API

Closes #357 (MVP).

Test plan

  • 7 smoke tests in `tests/test_smoke.py` — imports, Protocol conformance, tenant-context fallthrough (no PG required)
  • End-to-end: `docker compose up postgres` + seed.py creates schema and persists fixtures against postgres:16
  • Public surface: imports use only the stable `adcp.decisioning` / `adcp.server` / `adcp.audit_sink` paths
  • ruff/black clean

🤖 Generated with Claude Code

@gitguardian
Copy link
Copy Markdown

gitguardian Bot commented May 2, 2026

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

Multi-file directory under ``examples/v3_reference_seller/`` that
wires every Tier 2 / v3-supporting component into one runnable
adopter pattern. Spec 3.0-compliant on the wire, 3.1-ready in
architecture and storage.

* ``src/models.py`` — SQLAlchemy 2.0 models (Tenant, BuyerAgent,
  Account, MediaBuy) with 3.1-ready columns.
* ``src/tenant_router.py`` — SQL-backed SubdomainTenantRouter with
  in-process cache.
* ``src/buyer_registry.py`` — tenant-scoped BuyerAgentRegistry.
* ``src/audit.py`` — DbAuditSink writing audit_events.
* ``src/platform.py`` — sales-non-guaranteed impl (the bulk of
  what an adopter customizes).
* ``src/app.py`` — main entrypoint wiring serve(transport="both")
  + a ``build_app`` helper composing SubdomainTenantMiddleware on
  the parent Starlette app.
* ``seed.py`` + ``docker-compose.yml`` — dev fixtures + Postgres.
* ``README.md`` — clone-and-modify guide.
* ``tests/test_smoke.py`` — 7 import + Protocol-conformance tests.

End-to-end verified locally against postgres:16. The compose uses
``POSTGRES_HOST_AUTH_METHOD=trust`` for loopback dev — no literal
password in version control; production sellers source DB
credentials from a secrets backend.

Closes #357 (MVP). Follow-ups: HTTP-Sig verifier middleware in
context_factory, admin CRUD API, Alembic migrations, swap to
PgTaskRegistry / PgWebhookDeliverySupervisor for production.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley force-pushed the bokelley/v3-reference-seller branch from 44a66eb to 76ee0b5 Compare May 2, 2026 22:22
…are=)

The 4-expert review of #357 surfaced multiple convergent blockers
in the v3 reference seller MVP:

* `app.py` `build_app()` imported `_build_mcp_and_a2a_app` from the
  wrong module → ImportError at adopter call time
* `main()` never wired `SubdomainTenantMiddleware` → multi-tenant
  claim was false; `current_tenant()` would return None
* `platform.create_media_buy` extracted `total_budget` / `currency` /
  `start_time` / `end_time` against a wrong-shape API surface
  (TotalBudget object, StartTiming union) — every persisted media
  buy stored corrupted data or NULL
* `media_buy_id = f"mb_{uuid[:16]}"` collides at scale → IntegrityError

Bundled fix:

* Framework: add `asgi_middleware=Sequence[(cls, kwargs)]` kwarg to
  `adcp.server.serve()` (and forward through
  `adcp.decisioning.serve()`) so adopters layer Starlette-style
  ASGI middleware without dropping to `_build_mcp_and_a2a_app`.
* Example: `main()` now passes `asgi_middleware=[(SubdomainTenant
  Middleware, {"router": router})]`. Dead `build_app()` helper
  removed.
* `platform.py`: project `req.total_budget.amount/.currency` and
  unwrap `StartTiming.root` (`'asap'` → now()); `media_buy_id` is a
  full UUID (the (tenant_id, idempotency_key) unique constraint
  already guards replay).
* `models.py` + `buyer_registry.py` + `seed.py`: `billing_capabilities`,
  `default_terms`, `allowed_brands` switched from JSON-encoded
  String columns to native JSON columns (matches the rest of the
  schema).
* `audit.py`: docstring corrected — `record()` is best-effort but
  awaited inline (not fire-and-forget).
* `tenant_router.py`: cache renamed from "LRU-ish" to bounded FIFO
  to match actual eviction behavior.
* `README.md`: drop inaccurate claims (HTTP-Sig wired,
  project_account_for_response wired inline). Add dev-only callout
  for docker-compose trust auth + seed bearer token.

Test: `tests/test_serve_asgi_middleware.py` covers the new kwarg's
no-op + outermost-first composition contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Protocol-expert review surfaced one BLOCKER on commit 8d6d783:
``pricing_options`` in the get_products stub didn't conform to the
spec's ``CpmPricingOption`` shape — used ``type``/``rate`` keys
instead of the discriminator ``pricing_model`` and required
``fixed_price``, and was missing the required
``pricing_option_id``. Buyer agents validating the response
against the discriminated union would reject the seller's catalog.

Also addresses two should-fix items:

* ``serve(asgi_middleware=)`` docstring now warns adopter middleware
  to pass through ``lifespan`` and ``websocket`` scopes so the
  framework's lifespan composition still runs.
* README "Alembic migrations" callout clarifies that
  ``create_all`` does NOT detect column renames or type changes
  on existing tables — adopters who prototyped against earlier
  branches need to drop/recreate the dev database after pulling
  the JSON-column rename.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit cda07f9 into main May 3, 2026
20 of 21 checks passed
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.

feat(examples): v3 reference seller — canonical multi-tenant runnable example

1 participant