Skip to content

feat(testing): governance/property-lists/content-standards/collection-lists seeded bridges (#1755 phase 2)#1761

Merged
bokelley merged 2 commits into
mainfrom
feat/seeded-governance-bridges
May 15, 2026
Merged

feat(testing): governance/property-lists/content-standards/collection-lists seeded bridges (#1755 phase 2)#1761
bokelley merged 2 commits into
mainfrom
feat/seeded-governance-bridges

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Phase 2 of #1755 — extends TestControllerBridge<TAccount> with seeded bridges for the governance-domain read tools so platform-proxy sellers can run the storyboards that gate property-lists / collection-lists / content-standards / governance-aware-seller specialisms without rewriting upstream adapters.

Summary

Three new opt-in callbacks. Each seeded array feeds both the list and the get path for its entity, matching the user's instinct ("all or nothing, DX is odd otherwise"):

Callback Dedup key List path (append-merge, seeded wins) Singleton path (replace) Specialism unblocked
getSeededPropertyLists list_id (verified PropertyList.list_id: string required) list_property_lists — merge lists: PropertyList[], increment pagination.total_count by non-colliding count get_property_list — pick by request.list_id, replace response.list, preserve handler identifiers / pagination / resolved_at / cache_valid_until / coverage_gaps / context / ext property-lists, governance-aware-seller (property catalog seeding)
getSeededCollectionLists list_id (verified CollectionList.list_id: string required) list_collection_lists — symmetric with property lists get_collection_list — symmetric; preserves handler collections / pagination / resolved_at / cache_valid_until / coverage_gaps / context / ext collection-lists (program-level brand safety via IMDb/Gracenote/EIDR IDs)
getSeededContentStandards standards_id (verified ContentStandards.standards_id: string required) list_content_standards — success arm standards: ContentStandards[]; merge + update pagination.total_count get_content_standards — success arm IS ContentStandards directly (NO envelope wrapper per AdCP 3.0.11) — replace entire response, preserve only handler ext (no context in the success arm shape) content-standards (brand-safety / suitability policies)

All three bridges follow the established seeded-wins-on-collision precedent set by the six existing bridges (mergeSeededProducts / mergeSeededCreatives / mergeSeededMediaBuys / mergeSeededAccounts / mergeSeededMediaBuyDelivery / mergeSeededCreativeFormats) and the same triply-gated sandbox contract (controller present + sandbox marker on request + resolved account is sandbox: true when resolveAccount produced one). BridgeFromSessionStoreOptions gains matching selectSeededPropertyLists / selectSeededCollectionLists / selectSeededContentStandards selectors.

Tool skipped

list_authorized_properties — DELIBERATELY NOT ADDED. That tool was removed from AdCP and replaced by the get_adcp_capabilities discovery surface; no schema exists in schemas/cache/3.0.11/bundled/ (verified). The AuthorizedProperty type only survives in src/lib/types/v2-5/tools.generated.ts for legacy 2.5 compat. Old references in src/lib/discovery/property-crawler.ts (line 175) and src/lib/testing/scenarios/ confirm the migration ("Use capabilities API which replaced list_authorized_properties").

Schema-driven decisions

Verified each schema before designing the bridge:

  • schemas/cache/3.0.11/bundled/property/list-property-lists-response.json — plain interface, required: [lists], pagination? optional. ✅ list-shape only.
  • schemas/cache/3.0.11/bundled/property/get-property-list-response.json — required list, optional identifiers / pagination / resolved_at / cache_valid_until / coverage_gaps / context / ext. ✅ singleton replace of list field, preserve auxiliary data.
  • schemas/cache/3.0.11/collection/list-collection-lists-response.json + get-collection-list-response.json — symmetric with property lists. ✅ same shape decision.
  • schemas/cache/3.0.11/bundled/content-standards/list-content-standards-response.jsononeOf (success arm standards: ContentStandards[] / error arm errors: []). ✅ append-merge into success arm; error arm gated out by dispatcher's pre-existing !isErrorResponse(formatted) check.
  • get-content-standards-response.jsononeOf where the success arm IS ContentStandards directly (no envelope). ✅ replace entire response, preserve handler ext only.

Test plan

  • NODE_ENV=test npm test — 8769 pass / 0 fail / 3 skipped (pre-existing). Previous baseline 8748; +35 new test cases in test/lib/seed-per-tool-wiring.test.js (51 → 86 it() blocks).
  • npm run typecheck — clean.
  • npm run format:check — clean.
  • npm run build — clean.
  • Verified at runtime: seeded-only, handler-only, merge with new entries, collision dedup (seeded wins), mixed [A,B] + [B,C][A, B-seeded, C] with correct counts, sandbox-gating refusal, validation drop for missing/empty list_id / standards_id, singleton-replace passthrough on no-match, context+ext preservation on singleton replace (PropertyList / CollectionList preserve full envelope; ContentStandards preserves only ext).

Test mix per bridge (caveat: list+get share the seeded callback so I split the suite by tool):

  • getSeededPropertyLists — 7 list-path tests + 4 get-path tests
  • getSeededCollectionLists — 6 list-path tests + 4 get-path tests
  • getSeededContentStandards — 6 list-path tests + 4 get-path tests
  • bridgeFromSessionStore — 4 governance-selector wiring tests

Doc updates

  • docs/guides/VALIDATE-YOUR-AGENT.md § Platform-proxy sellers — extended the selector table with 3 new entries (each tied to the specialism it unblocks), updated the "Singleton exception" callout to mention the new singleton-replace paths and ContentStandards' bare-entity success arm, updated the "Validation drops" bullet with the new dedup keys.

Files

  • src/lib/server/test-controller-bridge.ts — interface additions; filterValidSeededPropertyLists / filterValidSeededCollectionLists / filterValidSeededContentStandards (reuse filterValidById); mergeSeededPropertyListsIntoResponse / mergeSeededCollectionListsIntoResponse / mergeSeededContentStandardsIntoResponse; pickSeededPropertyListForRequest / replacePropertyListIfSeeded; matching pairs for collection-list and content-standards; selector wiring in bridgeFromSessionStore.
  • src/lib/server/create-adcp-server.ts — 3 new dispatch branches (each handles both list_* and get_* for its entity) added after the list_creative_formats branch, behind the same triply-gated sandbox check.
  • test/lib/seed-per-tool-wiring.test.js — 35 new test cases, 7 new describe blocks.
  • .changeset/seeded-governance-bridges.md — minor.

🤖 Generated with Claude Code

bokelley and others added 2 commits May 14, 2026 13:54
…-lists seeded bridges (#1755 phase 2)

Extends `TestControllerBridge<TAccount>` with three opt-in callbacks so
platform-proxy sellers can seed governance-domain fixtures into
conformance storyboards without driving real upstream calls. Each
callback's seeded array feeds both the list AND the get path for that
entity (dedup key in parens):

  - getSeededPropertyLists       (list_id)       → list_property_lists / get_property_list
  - getSeededCollectionLists     (list_id)       → list_collection_lists / get_collection_list
  - getSeededContentStandards    (standards_id)  → list_content_standards / get_content_standards

List path: append-merge with seeded wins on collision; pagination.total_count
increments by the non-colliding seeded count. Singleton path for property /
collection: pick by request id, replace the response's `list` field, preserve
handler's identifiers / pagination / resolved_at / cache_valid_until /
coverage_gaps / context / ext. Singleton path for content-standards: success
arm IS ContentStandards directly (no envelope wrapper per AdCP 3.0.11) — replace
the entire response and preserve only handler ext.

Same triply-gated sandbox contract (controller present + sandbox marker +
resolved account is sandbox: true when resolveAccount produced one).
BridgeFromSessionStoreOptions gains matching selectSeededPropertyLists /
selectSeededCollectionLists / selectSeededContentStandards selectors.

Unblocks the property-lists, governance-aware-seller, collection-lists, and
content-standards storyboards on platform-proxy sellers.

list_authorized_properties was deliberately skipped — that tool was removed
from AdCP and replaced by get_adcp_capabilities discovery; no schema exists
in schemas/cache/3.0.11/.
…ntext

Both reviewers flagged that the get_content_standards response success arm
carries context alongside ext (top-level optional properties on the
response, even though allOf: [ContentStandards] is the body shape per
schemas/cache/3.0.11/content-standards/get-content-standards-response.json).
The initial commit dropped context on replace.

replaceContentStandardsIfSeeded now mirrors replaceAccountFinancialsIfSeeded
preservation: seeded fixture is authoritative on the ContentStandards body,
framework-managed envelope fields (context, ext) round-trip from the handler.
JSDoc on the bridge field, dispatcher comment, changeset, and
VALIDATE-YOUR-AGENT.md callout corrected. New test asserts handler
context + ext survive replace when seeded fixture lacks them.

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

Fix: ContentStandards singleton replace now preserves handler context

Both reviewers caught that the schema's success arm carries context alongside ext (top-level optional properties on the response, even though allOf: [ContentStandards] is the body shape). The initial commit dropped context on replace. Patched to mirror replaceAccountFinancialsIfSeeded's preservation pattern. Test added; JSDoc and VALIDATE-YOUR-AGENT.md callout corrected.

@bokelley bokelley merged commit b7aed85 into main May 15, 2026
10 checks passed
@bokelley bokelley deleted the feat/seeded-governance-bridges branch May 15, 2026 14:19
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