feat(server): createMediaBuyStore — opt-in targeting_overlay echo (#1415)#1424
Conversation
Address PR #1424 code-reviewer feedback: - Document the optimistic-on-success persistence in updateMediaBuy. Persistence runs after the platform method returns without throwing; publishers returning error envelopes on the success path will see the persisted overlay diverge from their internal view. - Document `backfill` mutation semantics on the public MediaBuyStore interface (was previously only on the implementation). - Document why `new_packages[]` entries without `targeting_overlay` are intentionally not tracked (overlay-only store). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Fold candidate — issue #1430 (filed from the protocol-expert review on this PR).
The fix is small: in targeting_overlay: reqPkg.targeting_overlay,with: targeting_overlay: respPkg?.targeting_overlay ?? reqPkg.targeting_overlay,
Generated by Claude Code |
… sellers (#1415) `createAdcpServerFromPlatform` gains a `mediaBuyStore` option. When wired, the framework persists `packages[].targeting_overlay` from `create_media_buy`, echoes it on `get_media_buys`, and deep-merges `update_media_buy` patches against the prior persisted overlay (omitted keys keep prior, non-null values replace, explicit `null` clears). Backed by any `AdcpStateStore`, account-scoped via `scopedStore`. Failures are logged + swallowed — auto-echo never breaks a successful seller response. Closes the seller spec gap on `schemas/cache/3.0.5/media-buy/get-media-buys-response.json`'s mandated echo of persisted `property_list` / `collection_list` refs for sellers claiming property-lists / collection-lists specialisms. Worked example `hello_seller_adapter_guaranteed.ts` now wires `mediaBuyStore` by default — every fork inherits the contract for free. Drops four stale `UPSTREAM_SCHEMA_DRIFT` suppressors in `test/lib/storyboard-drift.test.js`. The cited adcp#2488 was resolved by the AdCP 3.0 GA schema update — `PackageStatus.targeting_overlay` is on the wire response shape, so the assertions are now valid and the SDK is positioned to satisfy them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address PR #1424 code-reviewer feedback: - Document the optimistic-on-success persistence in updateMediaBuy. Persistence runs after the platform method returns without throwing; publishers returning error envelopes on the success path will see the persisted overlay diverge from their internal view. - Document `backfill` mutation semantics on the public MediaBuyStore interface (was previously only on the implementation). - Document why `new_packages[]` entries without `targeting_overlay` are intentionally not tracked (overlay-only store). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
478056a to
f038199
Compare
… persistFromCreate For normalizing sellers that resolve agent_url, strip unknown fields, or canonicalize list_id before persisting, the echoed targeting_overlay must reflect what was actually stored, not a verbatim copy of the request value. Also adds targeting_overlay to CreateMediaBuyResultForStore packages shape to type-check the respPkg access. Refs #1430. https://claude.ai/code/session_01LnaXzvFxKDS9b99ZUw79bN
|
Applied the fold from #1430 in commit 3b7e6ab:
All 9 Generated by Claude Code |
|
Acknowledged — the fold from #1430 looks clean. The Triaged by Claude Code. Session: https://claude.ai/code/session_011fctMz3kLEKYpnQz1unL8L Generated by Claude Code |
…1430) (#1433) * test(server): lock in seller-response preference over request in MediaBuyStore (#1430) Adds a unit test to mediaBuyStore.persistFromCreate that exercises the spec contract surfaced in PR #1424 protocol-expert review: when both request and response carry packages[i].targeting_overlay, the persisted echo reflects the seller's response (not the buyer's request copy). The behavior shipped in #1424 already prefers response over request, but no test pinned it — adding one prevents regression. Closes #1430. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: add empty changeset for test-only #1430 PR --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final review of what's queued for 6.7 ship surfaced four adopter-relevant changes that weren't in the migration recipe yet: New recipes (12 → 15): - #13 createTenantStore — opinionated multi-tenant AccountStore builder with built-in fail-closed tenant-isolation gate. Resolves the screenshot's "RosterAccountStore (Postgres)" item — Postgres was always blocked on adopter persistence layer; the helper is the SDK side of that contract. - #14 state-machine helpers (MEDIA_BUY_TRANSITIONS, assertMediaBuyTransition, CREATIVE_ASSET_TRANSITIONS, assertCreativeTransition). Drop-in replacements for the local status-graph copy three example files were carrying. - #15 createMediaBuyStore — opt-in targeting_overlay echo on get_media_buys for sellers claiming property-lists / collection-lists. Recipe updates: - #5 — note AccountStore.syncGovernance is now typed (drops the v5 escape-hatch + `as any` pattern); credentials stripped on emit. - #10 — add Shape C (createRosterAccountStore) for publisher-curated rosters alongside Shape A (InMemoryImplicitAccountStore) and Shape B (createOAuthPassthroughResolver). Three-shape table. - #12 — `createOAuthPassthroughResolver` now also re-exported from `@adcp/sdk/server` (the patch landed on main); all three import paths work. What-else-changed additions: - Account<TCtxMeta> v3 wire alignment — billing_entity (with bank stripped on emit), rate_card, payment_terms, credit_limit, setup (drives pending_approval → active lifecycle), account_scope, governance_agents, reporting_bucket. Promoted from a footnote per adtech-product-expert's earlier review. - multi_id truncation dev warn paired with the existing media_buy_ids[] fan-out item. - Cross-specialism dispatch docs — no ctx.platform.<X> accessor; class instance + this OR closure capture. - composeMethod testing recipes link (docs/recipes/composeMethod-testing.md is now merged on main from #1371). - Sales-social + sales-guaranteed planning surface mocks (delivery_estimate / forecast / availability) — adopters can now wire Product.forecast against the mocks. - narrowAccountRef export. - Storyboard-runner task_completion.<key> capture + webhook fallback. CLAUDE.md and AGENTS.md blurbs updated from "twelve" to "fifteen" with the three new helper names called out. Picked up via merge from main (20 commits since the last sync): createTenantStore (#1420), createMediaBuyStore (#1424), state-machine helpers (#1427), createRosterAccountStore + Shape C docs, syncGovernance typed, Account v3 wire alignment, Account.bank strip, sales-* planning surface (#1414), task_completion capture (#1426), runner webhook fallback (#1434), narrowAccountRef (#1428), composeMethod testing recipes (#1371 merged), oauth-passthrough re-export, multi-id truncation warn. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ne (#1416) (#1480) The Hello adapter was the only example carrying an EXPECTED_FAILURES allowlist (3 entries). Investigation revealed: - #1416 (NOT_CANCELLABLE) was a real adapter-side gap. Wired now: localBuyStatus Map tracks per-buy MediaBuyStatus, createMediaBuy stamps pending_creatives, updateMediaBuy calls assertMediaBuyTransition (newly imported from @adcp/sdk/server) on canceled/paused patches and updates the tracker. Re-cancel throws NOT_CANCELLABLE per core/state-machine.yaml. - #1415 (targeting_overlay echo) and #1417 (HITL media_buy_id capture) are fixture-side gaps — both SDK fixes already shipped (createMediaBuyStore in PR #1424, runner `task_completion.<path>` support in PR #1426). The storyboards in adcontextprotocol/adcp need migration to consume them: - #1415: every step's account ref needs `sandbox: true` so create + get resolve to the same namespace in the synthesis-branch resolver. - #1417: sales_guaranteed/create_media_buy needs `task_completion.media_buy_id` instead of bare `media_buy_id` for HITL completion-artifact capture. Allowlist shrinks 3 → 2; remaining entries' `reason` strings updated to spell out which side of the boundary closes each. Header comment refreshed. The other four hello adapters (creative-template, seller-social, signals-marketplace, si-brand) have no allowlist and already pass clean. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Closes #1415.
createAdcpServerFromPlatformgains amediaBuyStoreopt-in. When wired, the framework satisfies the seller spec contract fortargeting_overlayecho onget_media_buyswithout each adapter persisting + merging by hand.The framework then:
packages[].targeting_overlayfromcreate_media_buyrequests, joined with seller-assignedpackage_id(orbuyer_refwhen supplied). Runs in the createMediaBuy projection so it covers both the sync arm AND the HITL completion arm.get_media_buys, backfills missingpackages[].targeting_overlayfrom the store. Packages the seller already echoed are left untouched.update_media_buy, deep-merges the patched overlay against prior persisted: omitted keys keep prior, non-null values replace, explicitnullclears.new_packages[]are persisted as fresh entries.Backed by any
AdcpStateStore(InMemoryStateStorefor dev,PostgresStateStorefor production). Account-scoped per-tenant viascopedStore. Failures logged + swallowed — auto-echo never breaks a successful seller response.hello_seller_adapter_guaranteed.tsnow wiresmediaBuyStoreby default — every fork inherits the contract for free.Triage resolution: drops four stale suppressors
Triage flagged that
test/lib/storyboard-drift.test.js:291–299suppressed the fourmedia_buy_seller/inventory_list_targeting/get_after_*assertions asUPSTREAM_SCHEMA_DRIFTperadcontextprotocol/adcp#2488. Verified directly:schemas/cache/3.0.5/media-buy/get-media-buys-response.jsonincludestargeting_overlayonPackageStatuswith prose mandating echo for sellers claiming property-lists/collection-lists. adcp#2488 is resolved by the 3.0 GA schema update; the suppressors are stale and dropped in this PR.Test plan
npx vitest run src/lib/server/media-buy-store.test.ts— 9/9 passnode --test test/lib/storyboard-drift.test.js— 498/499 pass (1 unrelated skip), 0 failnpx tsc --noEmit -p tsconfig.lib.jsoncleannpm run format:checkcleanmedia_buy_seller/inventory_list_targeting/get_after_createpasses against the worked example (manual repro)Companion follow-ups
MEDIA_BUY_TRANSITIONS+assertMediaBuyTransition(still triaging at PR open).context_outputs.pathcapture from task completion artifact (deferred; triage suggests Option 1 explicit-prefix or Option 4 storyboard YAML redesign).🤖 Generated with Claude Code