feat(testing): brand-rights + SI seeded bridges (#1755 phase 4)#1773
Merged
Conversation
5 tasks
bokelley
added a commit
that referenced
this pull request
May 16, 2026
Captures protocol-review nit on PR #1773: when a stateful SI session bridge ships (phase 5+ of #1755), the seeded offering_token here must match the seeded session's offering_token, or si_initiate_session will reject as stale-token. Today si_initiate_session is out of scope so no coupling exists; the note is a tripwire for future readers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Author
Doc-only nit addressedAdded a JSDoc note on |
Extends `TestControllerBridge<TAccount>` with three opt-in callbacks so platform-proxy sellers can seed brand-rights and sponsored-intelligence fixtures into conformance storyboards without driving real upstream calls: - getSeededBrandIdentity (brand_id) → get_brand_identity (singleton replace) - getSeededRights (rights_id) → get_rights (append-merge) - getSeededSiOffering (offering.offering_id)→ si_get_offering (singleton replace) Singleton replace for `get_brand_identity` and `si_get_offering` mirrors the precedent set by `replaceContentStandardsIfSeeded` — seeded fixture is authoritative on the response body; framework-managed `context` / `ext` round-trip from the handler. Append-merge for `get_rights` mirrors `getSeededProducts` — `get_rights` is a discovery / search tool with an NL `query`, so the response carries an array; no pagination / query_summary blocks per AdCP 3.0.11. `si_get_offering` is a stateless catalog lookup despite the broader SI flow being session-keyed: the response PRODUCES an `offering_token` for a future session but does not CONSUME one, so the singleton-replace pattern fits cleanly. The session-stateful SI tools (`si_initiate_session`, `si_send_message`, `si_terminate_session`) are intentionally NOT bridged — they consume session state that a static fixture can't honestly serve. The mutating brand-rights tools (`acquire_rights`, `update_rights`) are NOT bridged either — seeded read paths only, per phase 4 scope. Same triply-gated sandbox contract (controller present + sandbox marker on request + resolved account is `sandbox: true` when `resolveAccount` produced one), seeded-wins collision precedent, warn-and-drop validation contract (never throws). `BridgeFromSessionStoreOptions` gains matching `selectSeededBrandIdentity` / `selectSeededRights` / `selectSeededSiOffering` selectors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures protocol-review nit on PR #1773: when a stateful SI session bridge ships (phase 5+ of #1755), the seeded offering_token here must match the seeded session's offering_token, or si_initiate_session will reject as stale-token. Today si_initiate_session is out of scope so no coupling exists; the note is a tripwire for future readers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c60adfd to
46b1fce
Compare
| // body; handler's `context` / `ext` round-trip. The response is | ||
| // a union (success | error) — the dispatcher already gated on | ||
| // `!isErrorResponse`, so we narrow defensively when reading. | ||
| else if (toolName === 'get_brand_identity' && testControllerBridge.getSeededBrandIdentity) { |
| // get_rights — append-merge keyed by `rights_id`. Discovery / | ||
| // search tool (NL `query`); the response carries `rights[]`. | ||
| // Drops to a no-op on the error arm of the response union. | ||
| else if (toolName === 'get_rights' && testControllerBridge.getSeededRights) { |
| // Stateless catalog lookup; the response's `offering_token` is | ||
| // produced for a future session but the lookup itself does not | ||
| // consume one. Handler's `context` / `ext` round-trip. | ||
| else if (toolName === 'si_get_offering' && testControllerBridge.getSeededSiOffering) { |
This was referenced May 16, 2026
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
Phase 4 of the
TestControllerBridgeseeded-callback umbrella (#1755). ExtendsTestControllerBridge<TAccount>with three opt-in callbacks for brand-rights and sponsored-intelligence read tools:getSeededBrandIdentityget_brand_identitybrand_idgetSeededRightsget_rightsrights_idgetSeededSiOfferingsi_get_offeringoffering.offering_idSI investigation outcome
The umbrella issue flagged SI as a "design wildcard" because the SI flow has session-keyed state (
si_initiate_sessionissues sessions;si_send_messageadvances them). Schema audit showssi_get_offeringis the OUT-LIER inside that flow — it is a stateless catalog lookup:SIGetOfferingRequest,src/lib/types/tools.generated.ts:12571): top-leveloffering_id: stringis the only addressing field. Nosession_id, nooffering_token.SIGetOfferingResponse, line 12600): the response PRODUCES anoffering_tokenfor a FUTURE session but does not CONSUME one. The lookup itself is request-shaped, not session-shaped.Conclusion: the standard singleton-replace pattern fits cleanly. Bridge is shipped.
The session-stateful SI tools (
si_initiate_session,si_send_message,si_terminate_session) remain intentionally out of scope — a seeded fixture cannot honestly serve session state accumulated across prior conversational turns. If a future storyboard needs to seed in-session messages, that will need a different seam (likely session-store-keyed, not bridge-keyed).Schema citations
get_brand_identity—GetBrandIdentityRequest(core.generated.ts:5939,brand_id: stringrequired),GetBrandIdentitySuccess(line 5996, top-levelbrand_id: string). Singleton response.get_rights—GetRightsRequest(line 6328, NLquery: string),GetRightsSuccess.rights[](line 6386, each entry hasrights_id: string). Array response (discovery / search). Nopagination/query_summaryper AdCP 3.0.11.si_get_offering—SIGetOfferingRequest(tools.generated.ts:12571,offering_id: stringrequired),SIGetOfferingResponse(line 12600,offering?: { offering_id?: string, ... }). Singleton response.Tools skipped + why
acquire_rights/update_rights— mutating; seeded read paths only per phase 4 scope. The original umbrella explicitly lists these as out of scope.si_initiate_session/si_send_message/si_terminate_session— mutating + session-stateful. A seeded fixture would lie about session state. Documented in code + changeset.creative_approval— listed underBrandRightsHandlersin the SDK but is webhook-only (not an MCP/A2A tool) per AdCP 3.0; not a discovery / read tool.Established precedents followed
sandbox: truewhen presentcontextANDext(PR feat(testing): governance/property-lists/content-standards/collection-lists seeded bridges (#1755 phase 2) #1761 lesson)filterValidSeededAccountFinancials)core.generatedfor brand-rights types (persrc/lib/types/index.ts:268),tools.generatedfor SI typesTest count delta
+21 new tests, all passing.
get_brand_identity(replace match / no match / context+ext preserve / sandbox-gate / validation-drop+duplicate)get_rights(append / seeded-only / handler-only / collision / sandbox-gate / validation-drop)si_get_offering(replace match / no match / context+ext preserve / sandbox-gate / validation-drop+duplicate)bridgeFromSessionStoreselector wiring (one per new callback)Total: 8716 pass / 0 fail across full
npm test.Doc + selectors added
BridgeFromSessionStoreOptionsgainsselectSeededBrandIdentity,selectSeededRights,selectSeededSiOfferingdocs/guides/VALIDATE-YOUR-AGENT.md"Platform-proxy sellers" table extended with the three new selectors mapped to their specialism phases (brand-rights identity / rights discovery; sponsored-intelligence offering lookup)Test plan
npm run typecheck— clean on changed filesnpm run format:check— cleannpm test(full root suite) — 8716 pass / 0 failnpm run build:lib— succeedsRelated
TestControllerBridgeseeded callbacks for proxy sellers #1755getSeededMediaBuyDelivery)🤖 Generated with Claude Code