Skip to content

feat(testing): signals/creative-delivery/creative-features seeded bridges (#1755 phase 3)#1772

Merged
bokelley merged 2 commits into
mainfrom
feat/seeded-signals-creative-bridges
May 16, 2026
Merged

feat(testing): signals/creative-delivery/creative-features seeded bridges (#1755 phase 3)#1772
bokelley merged 2 commits into
mainfrom
feat/seeded-signals-creative-bridges

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Phase 3 of #1755 — extends TestControllerBridge with the next batch of read-path bridges so platform-proxy sellers can seed signal + creative fixtures into compliance storyboards without driving real upstream calls.

Summary

Bridges shipped (3)

Bridge Tool Shape Specialism(s) unblocked
getSeededSignals get_signals list-merge by signal_id (canonical) signal-marketplace, signal-owned (one bridge handles both — signal_type is the per-entry discriminator)
getSeededCreativeDelivery get_creative_delivery list-merge by creative_id, pagination.total drift creative-ad-server, creative-template, creative-generative (delivery readback)
getSeededCreativeFeatures get_creative_features merge into success-arm results[] by feature_id (oneOf-aware no-op on error arm) creative-* governance feature evaluation

Bridges skipped (2 — schemas don't exist in 3.0.11)

  • list_audiences — not a 3.0.11 tool. Audience discovery is folded into sync_audiences (the discovery-only call omits the request audiences array but still returns audiences[]). See src/lib/testing/storyboard/default-invariants.ts:725-727 and the absence of any audiences/ directory under schemas/cache/3.0.11/bundled/.
  • list_targeting_categories — not a 3.0.11 tool. Targeting capabilities surface via get_adcp_capabilities. No list-targeting-*.json schemas exist under schemas/cache/3.0.11/.

Schema citations

  • schemas/cache/3.0.11/bundled/signals/get-signals-response.jsonsignals: [{ signal_id, ... }], dedup key signal_id (discriminated union: {source:'catalog', data_provider_domain, id} or {source:'agent', agent_url, id} — canonical dedup key is ${source}|${origin}|${id}).
  • schemas/cache/3.0.11/bundled/creative/get-creative-delivery-response.jsoncreatives: [{ creative_id, ... }], optional pagination.total (field name total, distinct from total_count elsewhere).
  • schemas/cache/3.0.11/bundled/creative/get-creative-features-response.jsononeOf envelope. Success arm: results: CreativeFeatureResult[] (dedup key feature_id); error arm: errors: Error[]. No top-level entity id.

Implementation notes

  • get_creative_features is the first nested-array bridge. The seeded array (CreativeFeatureResult[]) merges into a property of the success arm (results), not the top-level response. The handler computes everything else; the bridge just augments per-feature evaluations. Error arm is a no-op (the seeded results array can't be grafted onto an error envelope without producing a wire-incorrect mixed shape).
  • Signal id is a discriminated union. SignalID is {source:'catalog', data_provider_domain, id} or {source:'agent', agent_url, id} — two signals with the same id from different sources are distinct, so dedup keys on the full tuple. Validation drops entries with malformed source/origin/id triples.
  • Triply-gated dispatch + seeded-wins on collision preserved across all three bridges (matches the existing precedent from feat(testing): getSeededMediaBuyDelivery bridge callback + nit tests (#1755 phase 1) #1759 / feat(testing): governance/property-lists/content-standards/collection-lists seeded bridges (#1755 phase 2) #1761).
  • get_creative_features envelope fields preserved. context, ext, detail_url, pricing_option_id, vendor_cost, currency, consumption round-trip from the handler verbatim — only results is augmented.

bridgeFromSessionStore selectors added

  • selectSeededSignals
  • selectSeededCreativeDelivery
  • selectSeededCreativeFeatures

Omitted selectors leave the corresponding bridge callback unset (opt-in by presence, same pattern as existing selectors).

Documentation

docs/guides/VALIDATE-YOUR-AGENT.md "Platform-proxy sellers" table extended with the three new entries, plus a new bullet documenting get_creative_features's oneOf-aware nested-array merge semantics.

Test plan

  • 28 new wiring tests in test/lib/seed-per-tool-wiring.test.js:
    • signals (8 tests): seeded-only, handler-only, canonical collision, mixed collision, catalog/agent same-id distinct, sandbox refusal, invalid signal_id drop
    • creative-delivery (8 tests): seeded-only, handler-only, append-merge, collision, mixed collision, pagination.total append + collision no-drift, sandbox refusal, validation drop
    • creative-features (8 tests): merge into results, handler-only, collision, mixed collision, error-arm no-op, envelope-fields preserved (context/ext/detail_url/vendor_cost), sandbox refusal, validation drop
    • bridgeFromSessionStore wiring (4 tests): three selectors wire matching callbacks; absent selectors omit them
  • Total wiring suite: 58 → 115 (115 pass, 0 fail).
  • npm run typecheck clean
  • npm run format:check clean
  • Full npm test from root: 8745 pass, 3 skipped, 0 fail (+ eslint-plugin workspace 30/30 pass)

…-features seeded bridges (#1755 phase 3)

Extends TestControllerBridge with three opt-in callbacks for platform-proxy sellers to seed signal + creative read-path fixtures into conformance storyboards without driving real upstream calls:

- getSeededSignals → get_signals (append-merge, canonical signal_id dedup across catalog/agent discriminator)
- getSeededCreativeDelivery → get_creative_delivery (append-merge by creative_id, pagination.total drift)
- getSeededCreativeFeatures → get_creative_features (oneOf-aware: merge CreativeFeatureResult[] into success-arm results by feature_id; no-op on error arm; preserves handler envelope fields)

list_audiences and list_targeting_categories deliberately not added: neither tool exists in AdCP 3.0.11 (audience discovery folds into sync_audiences; targeting capabilities surface via get_adcp_capabilities). No fabricated bridges.

BridgeFromSessionStoreOptions gains matching selectSeededSignals / selectSeededCreativeDelivery / selectSeededCreativeFeatures.

28 new wiring tests covering: seeded-only, handler-only, append-merge, collision dedup, mixed-collision (handler [A,B] + bridge [B,C] → [A,B-seeded,C]), source-discriminator preservation (catalog/agent signal_id with same id stay distinct), pagination.total drift on creative_delivery, oneOf error-arm pass-through and envelope-field preservation on creative_features, sandbox-gating refusal, validation drop, plus bridgeFromSessionStore selector wiring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Correct getSeededSignals / mergeSeededSignalsIntoResponse JSDoc: PaginationResponse.total_count exists but is intentionally not recomputed on partial-page merges (cross-page total context lives outside the seeded fixture).
- Stamp sandbox: true on mergeSeededCreativeDeliveryIntoResponse for symmetry with mergeSeededSignalsIntoResponse and the other list-merge bridges.
- Trim changeset summary to the bridges that actually ship (signals, creative-delivery, creative-features); audiences/targeting were deliberately skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley
Copy link
Copy Markdown
Contributor Author

Review nits addressed

Three small fixes from the code-review + protocol-expert reviews:

  • JSDoc correction: getSeededSignals previously claimed PaginationResponse has no total_count; that's wrong. The field exists but isn't recomputed on partial-page merges (justification updated in the JSDoc).
  • sandbox stamp: mergeSeededCreativeDeliveryIntoResponse now stamps sandbox: true for consistency with mergeSeededSignalsIntoResponse.
  • Changeset accuracy: summary no longer lists skipped audiences/targeting tools.

@bokelley bokelley merged commit eb0cede into main May 16, 2026
10 checks passed
@bokelley bokelley deleted the feat/seeded-signals-creative-bridges branch May 16, 2026 09:53
bokelley added a commit that referenced this pull request May 16, 2026
…1779)

* docs(bridge): name resolveAccount trust boundary + multi-tenant keying as adopter responsibility

Adds two paragraphs to the top-of-file JSDoc on TestControllerBridge:

1. Scope of verification — a storyboard pass through this bridge proves
   wire conformance against fixture data, not adapter health against
   the real upstream. Sellers must still exercise adapters against a
   live-OAuth sandbox runner separately. Cross-references the
   runner-visible-bridge-marker ask at #1775.

2. Adopter responsibilities — names two patterns the SDK can't enforce:
   (a) resolveAccount is the trust boundary; production bindings MUST
   configure it or the request-signal check is the only line of defense,
   because the dispatcher gate falls through to permissive when
   ctx.account === undefined. (b) Multi-tenant keying is the adopter's
   job; the SDK does no defensive cross-check between fixture-entry
   account IDs and the resolved ctx.account.

No code change. Security-review-driven during the post-merge review of
#1754 — main shipped the bridge surface (#1753 + phases #1759/#1761/
#1772) but no public-surface warning about either trust pattern.

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

* docs(bridge): docs-expert tightenings on JSDoc PR review

- "bypassed by the post-handler merge" → "shadowed by the post-handler
  merge" (more accurate: the upstream is called, then its response is
  overridden).
- "request-signal check is the only line of defense" → "buyer-supplied
  sandbox marker is the only gate" (flatter, more accurate).
- Drop the "Snap, Meta, TikTok, Google Ads" brand list → "social / search
  / programmatic inventory APIs" (illustrative without pulling toward a
  specific adopter or violating fictional-names-only convention).
- Echo a one-paragraph trust-boundary note on createAdcpServer's
  testController config field — that's where a wiring author lands when
  they hit autocomplete, and the last chance to warn before
  resolveAccount gets omitted. Cross-references the top-of-file JSDoc
  on TestControllerBridge.

All four changes from the docs-expert review of PR #1779. No code
behavior change; pure JSDoc.

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

---------

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