From 445f166c7fb13e9761de8ea2f39c06f2e1156309 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Wed, 22 Apr 2026 04:38:15 -0400 Subject: [PATCH 1/8] chore(deps): bump @adcp/client to 5.12.0; adjust storyboard baselines Picks up the SDK's spec-shaped request-builders for log_event, create_media_buy, list_creative_formats, si_*, sync_governance (adcp-client#794, #789, #802) and the VALIDATION_ERROR retry-storm guard (adcp-client#758). Closes most of the framework-mode zod-parity gap. Both legacy and framework regress on the bump because adcp-client#794 makes the runner emit every authored package on create_media_buy, exposing per-package products our catalog does not seed. Adjusts CI baselines to the new floor; fixture-seeding follow-up tracked separately. Framework-only delta vs. legacy is now 5 storyboards / 7 step failures, listed in the changeset. --- .changeset/bump-adcp-client-5-12.md | 22 +++++++++++ .../workflows/training-agent-storyboards.yml | 37 ++++++++++--------- package-lock.json | 8 ++-- package.json | 2 +- 4 files changed, 47 insertions(+), 22 deletions(-) create mode 100644 .changeset/bump-adcp-client-5-12.md diff --git a/.changeset/bump-adcp-client-5-12.md b/.changeset/bump-adcp-client-5-12.md new file mode 100644 index 0000000000..5b6aaad1f6 --- /dev/null +++ b/.changeset/bump-adcp-client-5-12.md @@ -0,0 +1,22 @@ +--- +--- + +Bump `@adcp/client` to `^5.12.0`. Closes most of the framework-mode +storyboard zod-parity gap by picking up the SDK's spec-shaped +request-builders for `log_event`, `create_media_buy`, +`list_creative_formats`, `si_*`, and `sync_governance` +(adcp-client#794, #789, #802). Also pulls in the VALIDATION_ERROR +retry-storm fix (adcp-client#758). + +CI baselines drop on both modes because the runner now emits every +authored package on `create_media_buy` (adcp-client#794), which exposes +that several storyboards reference per-package products our catalog +does not seed (`outdoor_video_q2`, `late_fringe_15s_mf`, +`outdoor_ctv_q2_guaranteed`, `lifestyle_display_q2`). Wiring those +through the new `controller_seeding: true` + `fixtures:` SDK feature +(adcp-client#790) is tracked as a separate task. + +Framework-only delta vs. legacy is now 5 storyboards / 7 step failures: +`acquire_rights_denied` error code, `calibrate_content` verdict, +`log_event` / `provide_feedback` session lookups, `report_usage` +`accepted` field type. Residual zod-parity follow-up. diff --git a/.github/workflows/training-agent-storyboards.yml b/.github/workflows/training-agent-storyboards.yml index 0f326e71e2..5139b5543b 100644 --- a/.github/workflows/training-agent-storyboards.yml +++ b/.github/workflows/training-agent-storyboards.yml @@ -38,25 +38,28 @@ jobs: flag_value: '0' # Baseline on main — keep in sync with the last clean run reported # in the PR description. Rising is fine; regressing fails CI. - # 36→44 after cross-agent format references stopped rejecting - # storyboards that point at creative.adcontextprotocol.org for - # format_id.agent_url (sales_social, catalog_driven). The seller - # only validates format_ids it hosts itself. - # Bumped again in adcp#2846 after @adcp/client 5.11.0 restored - # sample_request handling in the get_rights request builder - # (brand_rights/governance_denied and peer scenarios). - min_clean_storyboards: 46 - min_passing_steps: 338 + # 46→42 on the @adcp/client 5.12.0 bump: the storyboard runner + # now emits every authored package on create_media_buy + # (adcp-client#794), which exposes that several storyboards + # reference per-package products our catalog does not seed + # (outdoor_video_q2, late_fringe_15s_mf, outdoor_ctv_q2_guaranteed, + # lifestyle_display_q2). Tracked as a separate fixture-seeding + # task — wiring `controller_seeding: true` + `fixtures:` blocks + # on the affected storyboards (adcp-client#790 SDK side already + # shipped). Cascades downstream get_delivery on the same buys. + min_clean_storyboards: 42 + min_passing_steps: 319 - mode: framework flag_value: '1' - # 21→37 after wrapping framework dispatch in - # runWithSessionContext + flushDirtySessions so created media - # buys / creatives / governance state persist across MCP calls - # under the framework path (parity with legacy). Also picks up - # the cross-agent format-reference delta. - # Additional lift in adcp#2846 from the @adcp/client 5.11.0 bump. - min_clean_storyboards: 42 - min_passing_steps: 339 + # 42→37 on the @adcp/client 5.12.0 bump: same root cause as + # legacy (multi-package authoring exposes missing seeds). The + # framework-only delta vs. legacy is now 5 storyboards / 7 step + # failures (acquire_rights_denied error code, calibrate_content + # verdict, log_event/provide_feedback session lookups, + # report_usage `accepted` field type) — tracked as the residual + # zod-parity follow-up. See PR description for the diff. + min_clean_storyboards: 37 + min_passing_steps: 319 steps: - uses: actions/checkout@v6 diff --git a/package-lock.json b/package-lock.json index a703daf179..c62a500cbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "adcontextprotocol", "version": "3.0.0-rc.3", "dependencies": { - "@adcp/client": "^5.11.0", + "@adcp/client": "^5.12.0", "@anthropic-ai/sdk": "^0.90.0", "@asteasolutions/zod-to-openapi": "^8.5.0", "@contentauth/c2pa-node": "^0.5.4", @@ -120,9 +120,9 @@ } }, "node_modules/@adcp/client": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/@adcp/client/-/client-5.11.0.tgz", - "integrity": "sha512-UuSHIz9hbmAUQeFwZMBuZGuSIfx5ReJ+a4KUVSAVRb1g4M0rud8+LFpJ91eQZk3012c8ZQZlRS0YR5mVkUAWLw==", + "version": "5.12.0", + "resolved": "file:../../../adcp-client/adcp-client-5.12.0.tgz", + "integrity": "sha512-xx8b7dZqir180ZnFHadAqP2aWGcacjKgelS5Sx8yOAFno0N4VXiPByYipnTVlmjnJOoxmPn+WlA8yWA9gEjdXw==", "license": "Apache-2.0", "dependencies": { "ajv": "^8.18.0", diff --git a/package.json b/package.json index 75e4640caf..eb7d9ab8b1 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "check:images": "bash scripts/check-image-quality.sh" }, "dependencies": { - "@adcp/client": "^5.11.0", + "@adcp/client": "^5.12.0", "@anthropic-ai/sdk": "^0.90.0", "@asteasolutions/zod-to-openapi": "^8.5.0", "@contentauth/c2pa-node": "^0.5.4", From 444b0ea729d453cdab362fa645b8a60f3b220af4 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Wed, 22 Apr 2026 05:12:05 -0400 Subject: [PATCH 2/8] fix(storyboards): seed per-package products for multi-package create_media_buy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After adcp-client#794 makes the runner emit every authored package instead of dropping packages[1+], sellers without matching products in their default catalog hit `PRODUCT_NOT_FOUND: Package 1: Product not found: ` and cascading step failures. Wire adcp-client#790's fixture-seeding feature on the six storyboards whose create_media_buy steps author multi-package payloads. Each now declares `controller_seeding: true` + a fixtures block so the runner pre-seeds the per-package products and pricing options via comply_test_controller before phase 1 — and packages[1+].product_id / pricing_option_id resolve regardless of the seller's default catalog. Affected storyboards: - protocols/media-buy/index.yaml - protocols/media-buy/scenarios/delivery_reporting.yaml - protocols/media-buy/scenarios/governance_approved.yaml - specialisms/creative-generative/generative-seller.yaml - specialisms/sales-broadcast-tv/index.yaml - specialisms/sales-guaranteed/index.yaml --- .changeset/seed-multi-package-storyboards.md | 36 +++++++++++++++++++ .../source/protocols/media-buy/index.yaml | 25 +++++++++++++ .../scenarios/delivery_reporting.yaml | 25 +++++++++++++ .../scenarios/governance_approved.yaml | 25 +++++++++++++ .../generative-seller.yaml | 25 +++++++++++++ .../specialisms/sales-broadcast-tv/index.yaml | 25 +++++++++++++ .../specialisms/sales-guaranteed/index.yaml | 25 +++++++++++++ 7 files changed, 186 insertions(+) create mode 100644 .changeset/seed-multi-package-storyboards.md diff --git a/.changeset/seed-multi-package-storyboards.md b/.changeset/seed-multi-package-storyboards.md new file mode 100644 index 0000000000..12f26fb9ae --- /dev/null +++ b/.changeset/seed-multi-package-storyboards.md @@ -0,0 +1,36 @@ +--- +--- + +Storyboards: add `controller_seeding: true` + `fixtures:` blocks on six +storyboards whose `create_media_buy` steps author multi-package payloads +referencing products that don't exist in any reasonable seller catalog +by default. After `@adcp/client` 5.12 (adcp-client#794), the storyboard +runner emits every authored package instead of silently dropping +`packages[1+]`. Sellers without these products then hit +`PRODUCT_NOT_FOUND: Package 1: Product not found: ` and cascading +step failures in their conformance runs. + +Wires the SDK's fixture-seeding feature (adcp-client#790) on the six +storyboards so `packages[1+].product_id` / `pricing_option_id` resolve +against seeded fixtures regardless of the seller's default catalog: + +- `protocols/media-buy/index.yaml` — seeds `sports_preroll_q2`, + `lifestyle_display_q2` + pricing. +- `protocols/media-buy/scenarios/delivery_reporting.yaml` — seeds + `outdoor_display_q2`, `outdoor_video_q2` + pricing. +- `protocols/media-buy/scenarios/governance_approved.yaml` — same pair. +- `specialisms/creative-generative/generative-seller.yaml` — same pair. +- `specialisms/sales-broadcast-tv/index.yaml` — seeds `primetime_30s_mf`, + `late_fringe_15s_mf` with broadcast-spot formats + unit pricing. +- `specialisms/sales-guaranteed/index.yaml` — seeds + `sports_preroll_q2_guaranteed`, `outdoor_ctv_q2_guaranteed` with + guaranteed-fixed CPM pricing. + +All pricing uses `fixed_price` (rather than `floor_price`) so the +non-auction bid-price requirement doesn't trip storyboards that omit +`bid_price`. + +Validated by overlaying the edited YAMLs into `@adcp/client`'s +compliance cache and re-running via `adcp storyboard run` — seed phase +fires end-to-end and `create_media_buy` no longer throws +`PRODUCT_NOT_FOUND`. diff --git a/static/compliance/source/protocols/media-buy/index.yaml b/static/compliance/source/protocols/media-buy/index.yaml index 1348818606..a12cdd7146 100644 --- a/static/compliance/source/protocols/media-buy/index.yaml +++ b/static/compliance/source/protocols/media-buy/index.yaml @@ -53,6 +53,31 @@ prerequisites: The test kit provides a sample brand (Acme Outdoor) with campaign parameters suitable for testing the full media buy flow. test_kit: "test-kits/acme-outdoor.yaml" + controller_seeding: true + +fixtures: + products: + - product_id: "sports_preroll_q2" + delivery_type: "guaranteed" + channels: ["video"] + format_ids: + - id: "video_30s" + - product_id: "lifestyle_display_q2" + delivery_type: "guaranteed" + channels: ["display"] + format_ids: + - id: "display_300x250" + pricing_options: + - product_id: "sports_preroll_q2" + pricing_option_id: "cpm_guaranteed" + pricing_model: "cpm" + currency: "USD" + fixed_price: 22.0 + - product_id: "lifestyle_display_q2" + pricing_option_id: "cpm_standard" + pricing_model: "cpm" + currency: "USD" + fixed_price: 8.0 phases: - id: capability_discovery diff --git a/static/compliance/source/protocols/media-buy/scenarios/delivery_reporting.yaml b/static/compliance/source/protocols/media-buy/scenarios/delivery_reporting.yaml index c54f3ae8bb..0e9109541e 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/delivery_reporting.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/delivery_reporting.yaml @@ -37,6 +37,31 @@ prerequisites: scenario. This allows the test harness to inject delivery metrics into a media buy so get_media_buy_delivery has data to return. test_kit: "test-kits/acme-outdoor.yaml" + controller_seeding: true + +fixtures: + products: + - product_id: "outdoor_display_q2" + delivery_type: "guaranteed" + channels: ["display"] + format_ids: + - id: "display_300x250" + - product_id: "outdoor_video_q2" + delivery_type: "non_guaranteed" + channels: ["video"] + format_ids: + - id: "video_30s" + pricing_options: + - product_id: "outdoor_display_q2" + pricing_option_id: "cpm_standard" + pricing_model: "cpm" + currency: "USD" + fixed_price: 8.0 + - product_id: "outdoor_video_q2" + pricing_option_id: "cpm_standard" + pricing_model: "cpm" + currency: "USD" + fixed_price: 15.0 phases: - id: setup diff --git a/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml b/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml index 5b7ae3c119..57ad880d79 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml @@ -36,6 +36,31 @@ prerequisites: description: | A governance agent that supports sync_plans and check_governance. test_kit: "test-kits/acme-outdoor.yaml" + controller_seeding: true + +fixtures: + products: + - product_id: "outdoor_display_q2" + delivery_type: "guaranteed" + channels: ["display"] + format_ids: + - id: "display_300x250" + - product_id: "outdoor_video_q2" + delivery_type: "non_guaranteed" + channels: ["video"] + format_ids: + - id: "video_30s" + pricing_options: + - product_id: "outdoor_display_q2" + pricing_option_id: "cpm_standard" + pricing_model: "cpm" + currency: "USD" + fixed_price: 8.0 + - product_id: "outdoor_video_q2" + pricing_option_id: "cpm_standard" + pricing_model: "cpm" + currency: "USD" + fixed_price: 15.0 phases: - id: governance_plan_setup diff --git a/static/compliance/source/specialisms/creative-generative/generative-seller.yaml b/static/compliance/source/specialisms/creative-generative/generative-seller.yaml index d5a178d10f..89301370a7 100644 --- a/static/compliance/source/specialisms/creative-generative/generative-seller.yaml +++ b/static/compliance/source/specialisms/creative-generative/generative-seller.yaml @@ -65,6 +65,31 @@ prerequisites: tone — that the generative seller uses to produce on-brand creatives. The test kit provides a sample brand with campaign parameters. test_kit: "test-kits/acme-outdoor.yaml" + controller_seeding: true + +fixtures: + products: + - product_id: "outdoor_display_q2" + delivery_type: "guaranteed" + channels: ["display"] + format_ids: + - id: "display_300x250" + - product_id: "outdoor_video_q2" + delivery_type: "non_guaranteed" + channels: ["video"] + format_ids: + - id: "video_30s" + pricing_options: + - product_id: "outdoor_display_q2" + pricing_option_id: "cpm_guaranteed" + pricing_model: "cpm" + currency: "USD" + fixed_price: 12.0 + - product_id: "outdoor_video_q2" + pricing_option_id: "cpm_standard" + pricing_model: "cpm" + currency: "USD" + fixed_price: 15.0 phases: - id: capability_discovery diff --git a/static/compliance/source/specialisms/sales-broadcast-tv/index.yaml b/static/compliance/source/specialisms/sales-broadcast-tv/index.yaml index 9cdf8073b7..deb907b99d 100644 --- a/static/compliance/source/specialisms/sales-broadcast-tv/index.yaml +++ b/static/compliance/source/specialisms/sales-broadcast-tv/index.yaml @@ -64,6 +64,31 @@ prerequisites: kit provides the Nova Motors Volta EV launch campaign — an automotive brand with national broadcast reach goals and budget appropriate for primetime linear TV. test_kit: "test-kits/nova-motors.yaml" + controller_seeding: true + +fixtures: + products: + - product_id: "primetime_30s_mf" + delivery_type: "guaranteed" + channels: ["video"] + format_ids: + - id: "broadcast_spot_30s" + - product_id: "late_fringe_15s_mf" + delivery_type: "guaranteed" + channels: ["video"] + format_ids: + - id: "broadcast_spot_15s" + pricing_options: + - product_id: "primetime_30s_mf" + pricing_option_id: "unit_primetime_30" + pricing_model: "flat_rate" + currency: "USD" + fixed_price: 4500.0 + - product_id: "late_fringe_15s_mf" + pricing_option_id: "unit_fringe_15" + pricing_model: "flat_rate" + currency: "USD" + fixed_price: 1200.0 phases: - id: capability_discovery diff --git a/static/compliance/source/specialisms/sales-guaranteed/index.yaml b/static/compliance/source/specialisms/sales-guaranteed/index.yaml index 7d1d397db2..81bad5d0ed 100644 --- a/static/compliance/source/specialisms/sales-guaranteed/index.yaml +++ b/static/compliance/source/specialisms/sales-guaranteed/index.yaml @@ -61,6 +61,31 @@ prerequisites: The test kit provides a sample brand (Acme Outdoor) with campaign parameters suitable for testing the guaranteed approval flow. test_kit: "test-kits/acme-outdoor.yaml" + controller_seeding: true + +fixtures: + products: + - product_id: "sports_preroll_q2_guaranteed" + delivery_type: "guaranteed" + channels: ["video"] + format_ids: + - id: "video_30s" + - product_id: "outdoor_ctv_q2_guaranteed" + delivery_type: "guaranteed" + channels: ["ctv"] + format_ids: + - id: "video_30s" + pricing_options: + - product_id: "sports_preroll_q2_guaranteed" + pricing_option_id: "cpm_guaranteed_fixed" + pricing_model: "cpm" + currency: "USD" + fixed_price: 35.0 + - product_id: "outdoor_ctv_q2_guaranteed" + pricing_option_id: "cpm_guaranteed_fixed" + pricing_model: "cpm" + currency: "USD" + fixed_price: 45.0 phases: - id: capability_discovery From 71772e247c75958376d76884876a94052df00edc Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Wed, 22 Apr 2026 05:16:53 -0400 Subject: [PATCH 3/8] fix: restore registry resolution for @adcp/client in lockfile The initial bump used a local tarball during validation; the committed lockfile carried that file:// path, breaking CI which can't resolve it. Reinstall from the registry so the lockfile points at npm. --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c62a500cbc..76581e92d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -121,8 +121,8 @@ }, "node_modules/@adcp/client": { "version": "5.12.0", - "resolved": "file:../../../adcp-client/adcp-client-5.12.0.tgz", - "integrity": "sha512-xx8b7dZqir180ZnFHadAqP2aWGcacjKgelS5Sx8yOAFno0N4VXiPByYipnTVlmjnJOoxmPn+WlA8yWA9gEjdXw==", + "resolved": "https://registry.npmjs.org/@adcp/client/-/client-5.12.0.tgz", + "integrity": "sha512-xrYzPUq/I1+4TAW9u+yyzWpprfQaxjl+kkovZJvWnYhNVHxtVioTSaXnai4lRe3SvuOoco7eM0acgXIBe/JegQ==", "license": "Apache-2.0", "dependencies": { "ajv": "^8.18.0", From ac02730b5e682ebe85569fa04c0fb56895bbc024 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Wed, 22 Apr 2026 05:24:24 -0400 Subject: [PATCH 4/8] chore(tests): drop run-one-storyboard.ts in favor of the adcp CLI (#2895) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every use case run-one-storyboard.ts supported is now covered by the published adcp CLI (shipped in @adcp/client 5.12): - Single-storyboard diagnostic run: adcp storyboard run - Single-step debug: adcp storyboard step - JSON + JUnit output native run-storyboards.ts stays — the dual-mode CI matrix and per-storyboard test_kit / /mcp-strict routing it encodes doesn't fit the helper's single-agent-one-config shape. Retirement tracked as an upstream RFC for per-storyboard config on runAgainstLocalAgent. --- .changeset/drop-run-one-storyboard-for-cli.md | 22 +++++ .../workflows/training-agent-storyboards.yml | 2 - server/tests/manual/run-one-storyboard.ts | 94 ------------------- 3 files changed, 22 insertions(+), 96 deletions(-) create mode 100644 .changeset/drop-run-one-storyboard-for-cli.md delete mode 100644 server/tests/manual/run-one-storyboard.ts diff --git a/.changeset/drop-run-one-storyboard-for-cli.md b/.changeset/drop-run-one-storyboard-for-cli.md new file mode 100644 index 0000000000..c28eaf4d8b --- /dev/null +++ b/.changeset/drop-run-one-storyboard-for-cli.md @@ -0,0 +1,22 @@ +--- +--- + +Delete `server/tests/manual/run-one-storyboard.ts`. Every use case the +script supported is now covered by the published `adcp` CLI (shipped in +`@adcp/client` 5.12): + +- Single-storyboard diagnostic run → + `npx adcp storyboard run --allow-http --auth --json` +- Single-step debug → + `npx adcp storyboard step --json` +- Capability-driven filter + JUnit output → supported natively on the + CLI via `--format junit` and capability resolution. + +`run-storyboards.ts` stays — the dual-mode (legacy + framework) CI +matrix and per-storyboard `test_kit` / `/mcp-strict` routing it encodes +doesn't fit the helper's single-agent-one-config shape yet. Full +retirement is tracked upstream as the follow-up RFC on +`runAgainstLocalAgent` (per-storyboard config support). + +Also removes the `run-one-storyboard.ts` path filter from +`.github/workflows/training-agent-storyboards.yml`. diff --git a/.github/workflows/training-agent-storyboards.yml b/.github/workflows/training-agent-storyboards.yml index 5139b5543b..aea8aa118f 100644 --- a/.github/workflows/training-agent-storyboards.yml +++ b/.github/workflows/training-agent-storyboards.yml @@ -6,7 +6,6 @@ on: paths: - 'server/src/training-agent/**' - 'server/tests/manual/run-storyboards.ts' - - 'server/tests/manual/run-one-storyboard.ts' - 'package.json' - 'package-lock.json' - 'static/compliance/source/**' @@ -15,7 +14,6 @@ on: paths: - 'server/src/training-agent/**' - 'server/tests/manual/run-storyboards.ts' - - 'server/tests/manual/run-one-storyboard.ts' - 'package.json' - 'package-lock.json' - 'static/compliance/source/**' diff --git a/server/tests/manual/run-one-storyboard.ts b/server/tests/manual/run-one-storyboard.ts deleted file mode 100644 index 8594281770..0000000000 --- a/server/tests/manual/run-one-storyboard.ts +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Diagnostic: dump the raw runStoryboard() result for a single storyboard. - * npx tsx server/tests/manual/run-one-storyboard.ts capability_discovery - */ - -import express from 'express'; -import http from 'node:http'; -import { readFileSync, existsSync } from 'node:fs'; -import { join } from 'node:path'; -import YAML from 'yaml'; -import { listAllComplianceStoryboards, runStoryboard, getComplianceCacheDir } from '@adcp/client/testing'; -import type { Storyboard, StoryboardRunOptions } from '@adcp/client/testing'; -import { StaticJwksResolver, InMemoryReplayStore, InMemoryRevocationStore } from '@adcp/client/signing'; -import type { AdcpJsonWebKey } from '@adcp/client/signing'; - -const AUTH_TOKEN = process.env.PUBLIC_TEST_AGENT_TOKEN ?? 'storyboard-diag-token'; -process.env.PUBLIC_TEST_AGENT_TOKEN = AUTH_TOKEN; -if (!process.env.LOG_STORYBOARDS) process.env.LOG_LEVEL = 'silent'; - -const id = process.argv[2]; -if (!id) { console.error('usage: run-one-storyboard.ts '); process.exit(2); } - -const { createTrainingAgentRouter } = await import('../../src/training-agent/index.js'); -const { stopSessionCleanup } = await import('../../src/training-agent/state.js'); -const { getPublicJwks } = await import('../../src/training-agent/webhooks.js'); - -const sb = listAllComplianceStoryboards().find(s => s.id === id); -if (!sb) { console.error(`storyboard ${id} not found`); process.exit(2); } - -interface LoadedKit { - brand?: { house?: { domain?: string } }; - auth?: { api_key?: string; probe_task?: string }; -} - -function loadKit(s: Storyboard): LoadedKit | undefined { - const kitRef = s.prerequisites?.test_kit; - if (!kitRef) return undefined; - const path = join(getComplianceCacheDir(), kitRef); - if (!existsSync(path)) return undefined; - return YAML.parse(readFileSync(path, 'utf-8')) as LoadedKit; -} - -const app = express(); -app.use(express.json({ - limit: '5mb', - verify: (req, _res, buf) => { - (req as unknown as { rawBody?: string }).rawBody = buf.toString('utf8'); - }, -})); -// API-key-only agent: MUST NOT serve RFC 9728 PRM. See -// server/tests/manual/run-storyboards.ts and -// static/compliance/source/universal/security.yaml lines 37–47. -app.use('/api/training-agent', createTrainingAgentRouter()); -const server = http.createServer(app); -server.listen(0, '127.0.0.1', async () => { - const port = (server.address() as { port: number }).port; - const url = `http://127.0.0.1:${port}/api/training-agent/mcp`; - // Intentionally do not log agent URL to stdout — this script's stdout is - // piped through `jq` / `python -c` by the storyboard debugging workflow. - const kit = loadKit(sb); - const domain = kit?.brand?.house?.domain; - const brand: StoryboardRunOptions['brand'] | undefined = domain ? { domain } : undefined; - const testKit: StoryboardRunOptions['test_kit'] | undefined = (() => { - const a = kit?.auth; - if (!a?.api_key && !a?.probe_task) return undefined; - if (!a.probe_task) throw new Error('test kit declares auth.api_key without auth.probe_task'); - return { - auth: { - ...(a.api_key !== undefined && { api_key: a.api_key }), - probe_task: a.probe_task, - }, - }; - })(); - try { - const result = await runStoryboard(url, sb, { - auth: { type: 'bearer', token: AUTH_TOKEN }, - allow_http: true, - contracts: ['webhook_receiver_runner'], - webhook_receiver: { mode: 'loopback_mock' }, - webhook_signing: { - jwks: new StaticJwksResolver(getPublicJwks().keys as AdcpJsonWebKey[]), - replayStore: new InMemoryReplayStore(), - revocationStore: new InMemoryRevocationStore(), - }, - request_signing: { transport: 'mcp' }, - ...(brand && { brand }), - ...(testKit && { test_kit: testKit }), - }); - console.log(JSON.stringify(result, null, 2)); - } finally { - stopSessionCleanup(); - server.close(); - } -}); From e9c1050d717854bc4038e6b143571a946f4e21ee Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Wed, 22 Apr 2026 05:27:00 -0400 Subject: [PATCH 5/8] ci(storyboards): overlay in-repo compliance source onto SDK cache Storyboards load from @adcp/client's bundled compliance cache (a snapshot synced from this repo on SDK publish). Spec changes in this repo are invisible to the runner until the SDK republishes. Overlay the source tree onto the cache before running so CI validates in-repo spec changes immediately. Fixes CI on fixture-seeding PRs whose baselines assumed the overlay (which previously only existed in local dev). --- .../workflows/training-agent-storyboards.yml | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/training-agent-storyboards.yml b/.github/workflows/training-agent-storyboards.yml index 5139b5543b..d2c1af7684 100644 --- a/.github/workflows/training-agent-storyboards.yml +++ b/.github/workflows/training-agent-storyboards.yml @@ -74,6 +74,31 @@ jobs: env: PUPPETEER_SKIP_DOWNLOAD: 'true' + - name: Overlay in-repo compliance source onto SDK cache + # The storyboard runner loads from @adcp/client's bundled + # compliance cache, which is a snapshot synced from this repo's + # static/compliance/source/ on each SDK publish. To validate + # spec changes in this PR before waiting for an SDK republish, + # overlay the current source tree onto the cache. Overlays only + # files that exist in both trees; new top-level additions to + # source/ still require a sync-schemas cycle on the SDK side. + run: | + SRC="static/compliance/source" + DST="node_modules/@adcp/client/compliance/cache/latest" + if [ ! -d "$DST" ]; then + echo "::warning::SDK compliance cache not found at $DST — skipping overlay" + exit 0 + fi + # Copy every file that already exists in the cache. `cp -u` is + # not used because we want the overlay to always win for the + # files we maintain here. + (cd "$SRC" && find . -type f) | while read -r rel; do + if [ -f "$DST/${rel#./}" ]; then + cp "$SRC/${rel#./}" "$DST/${rel#./}" + fi + done + echo "Overlay complete." + - name: Run storyboards (${{ matrix.mode }}) id: run env: From 74eceb2e628a9c0b5cacec4806d1c63ebd94c805 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Wed, 22 Apr 2026 05:29:06 -0400 Subject: [PATCH 6/8] ci(storyboards): lower baselines to match 5.12 bump without local overlay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My original baselines (42/37) were taken from a local run where I'd overlaid static/compliance/source/ onto the SDK cache. Without that overlay, CI sees 38/32 clean on the bump — matching the fixture-seeding regression documented in the PR. Lower the floor here; the follow-up fixtures PR (adcp#2894) lifts it back up. --- .github/workflows/training-agent-storyboards.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/training-agent-storyboards.yml b/.github/workflows/training-agent-storyboards.yml index aea8aa118f..04509657bc 100644 --- a/.github/workflows/training-agent-storyboards.yml +++ b/.github/workflows/training-agent-storyboards.yml @@ -36,7 +36,7 @@ jobs: flag_value: '0' # Baseline on main — keep in sync with the last clean run reported # in the PR description. Rising is fine; regressing fails CI. - # 46→42 on the @adcp/client 5.12.0 bump: the storyboard runner + # 46→38 on the @adcp/client 5.12.0 bump: the storyboard runner # now emits every authored package on create_media_buy # (adcp-client#794), which exposes that several storyboards # reference per-package products our catalog does not seed @@ -45,18 +45,21 @@ jobs: # task — wiring `controller_seeding: true` + `fixtures:` blocks # on the affected storyboards (adcp-client#790 SDK side already # shipped). Cascades downstream get_delivery on the same buys. - min_clean_storyboards: 42 + # Passing-step count is 321 (↑2 vs. main's 319) because the + # log_event builder now emits spec-shaped payloads so + # sales_social / sales_catalog_driven log_event no longer -32602s. + min_clean_storyboards: 38 min_passing_steps: 319 - mode: framework flag_value: '1' - # 42→37 on the @adcp/client 5.12.0 bump: same root cause as + # 42→32 on the @adcp/client 5.12.0 bump: same root cause as # legacy (multi-package authoring exposes missing seeds). The - # framework-only delta vs. legacy is now 5 storyboards / 7 step + # framework-only delta vs. legacy is 5 storyboards / 7 step # failures (acquire_rights_denied error code, calibrate_content # verdict, log_event/provide_feedback session lookups, # report_usage `accepted` field type) — tracked as the residual # zod-parity follow-up. See PR description for the diff. - min_clean_storyboards: 37 + min_clean_storyboards: 32 min_passing_steps: 319 steps: - uses: actions/checkout@v6 From 4d1a669ed4029bc273d91a02ffefcbdf841d536b Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Wed, 22 Apr 2026 05:31:29 -0400 Subject: [PATCH 7/8] ci(storyboards): raise baselines reflecting fixture-seeding lift --- .../workflows/training-agent-storyboards.yml | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/.github/workflows/training-agent-storyboards.yml b/.github/workflows/training-agent-storyboards.yml index 2db245b0f9..a715ed7514 100644 --- a/.github/workflows/training-agent-storyboards.yml +++ b/.github/workflows/training-agent-storyboards.yml @@ -36,30 +36,23 @@ jobs: flag_value: '0' # Baseline on main — keep in sync with the last clean run reported # in the PR description. Rising is fine; regressing fails CI. - # 46→38 on the @adcp/client 5.12.0 bump: the storyboard runner - # now emits every authored package on create_media_buy - # (adcp-client#794), which exposes that several storyboards - # reference per-package products our catalog does not seed - # (outdoor_video_q2, late_fringe_15s_mf, outdoor_ctv_q2_guaranteed, - # lifestyle_display_q2). Tracked as a separate fixture-seeding - # task — wiring `controller_seeding: true` + `fixtures:` blocks - # on the affected storyboards (adcp-client#790 SDK side already - # shipped). Cascades downstream get_delivery on the same buys. - # Passing-step count is 321 (↑2 vs. main's 319) because the - # log_event builder now emits spec-shaped payloads so - # sales_social / sales_catalog_driven log_event no longer -32602s. - min_clean_storyboards: 38 + # 38→42 after wiring `controller_seeding: true` + `fixtures:` + # blocks on the six multi-package storyboards exposed by + # adcp-client#794 (media_buy_seller, delivery_reporting, + # governance_approved, creative_generative, sales_broadcast_tv, + # sales_guaranteed). The in-repo compliance source overlay step + # ensures CI sees these fixture adds before an SDK republish. + min_clean_storyboards: 42 min_passing_steps: 319 - mode: framework flag_value: '1' - # 42→32 on the @adcp/client 5.12.0 bump: same root cause as - # legacy (multi-package authoring exposes missing seeds). The - # framework-only delta vs. legacy is 5 storyboards / 7 step - # failures (acquire_rights_denied error code, calibrate_content - # verdict, log_event/provide_feedback session lookups, - # report_usage `accepted` field type) — tracked as the residual - # zod-parity follow-up. See PR description for the diff. - min_clean_storyboards: 32 + # 32→37 after the same fixture-seeding work. The framework-only + # delta vs. legacy is 5 storyboards / 7 step failures + # (acquire_rights_denied error code, calibrate_content verdict, + # log_event/provide_feedback session lookups, report_usage + # `accepted` field type) — tracked as the residual zod-parity + # follow-up. See PR description for the diff. + min_clean_storyboards: 37 min_passing_steps: 319 steps: - uses: actions/checkout@v6 From 13106205370e11211b38d5db691181e6ad01cebf Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Wed, 22 Apr 2026 05:45:13 -0400 Subject: [PATCH 8/8] fix(storyboards): align outdoor_video_q2 seed with canonical fixture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit protocols/governance/index.yaml pre-existed this PR and seeds outdoor_video_q2 as a guaranteed/video_15s/\$12 product. Three new fixtures in this PR (delivery_reporting, governance_approved, creative-generative) diverged (non_guaranteed/video_30s/\$15), so the SDK's seed store refused the replay with seed_product.outdoor_video_q2: INVALID_PARAMS ... diverges from the previously seeded fixture on every storyboard reached after governance/index.yaml had run. Match the canonical seed shape (guaranteed / video_15s / fixed_price 12.0) across the three PR-added fixtures. The storyboards' create_media_buy packages only reference pricing_option_id, so delivery_type + format_id don't affect the flow — this is purely a seed-body alignment. --- .../protocols/media-buy/scenarios/delivery_reporting.yaml | 6 +++--- .../protocols/media-buy/scenarios/governance_approved.yaml | 6 +++--- .../specialisms/creative-generative/generative-seller.yaml | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/static/compliance/source/protocols/media-buy/scenarios/delivery_reporting.yaml b/static/compliance/source/protocols/media-buy/scenarios/delivery_reporting.yaml index 0e9109541e..75596a2306 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/delivery_reporting.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/delivery_reporting.yaml @@ -47,10 +47,10 @@ fixtures: format_ids: - id: "display_300x250" - product_id: "outdoor_video_q2" - delivery_type: "non_guaranteed" + delivery_type: "guaranteed" channels: ["video"] format_ids: - - id: "video_30s" + - id: "video_15s" pricing_options: - product_id: "outdoor_display_q2" pricing_option_id: "cpm_standard" @@ -61,7 +61,7 @@ fixtures: pricing_option_id: "cpm_standard" pricing_model: "cpm" currency: "USD" - fixed_price: 15.0 + fixed_price: 12.0 phases: - id: setup diff --git a/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml b/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml index 57ad880d79..1d7a43b041 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml @@ -46,10 +46,10 @@ fixtures: format_ids: - id: "display_300x250" - product_id: "outdoor_video_q2" - delivery_type: "non_guaranteed" + delivery_type: "guaranteed" channels: ["video"] format_ids: - - id: "video_30s" + - id: "video_15s" pricing_options: - product_id: "outdoor_display_q2" pricing_option_id: "cpm_standard" @@ -60,7 +60,7 @@ fixtures: pricing_option_id: "cpm_standard" pricing_model: "cpm" currency: "USD" - fixed_price: 15.0 + fixed_price: 12.0 phases: - id: governance_plan_setup diff --git a/static/compliance/source/specialisms/creative-generative/generative-seller.yaml b/static/compliance/source/specialisms/creative-generative/generative-seller.yaml index 89301370a7..d0a4cf69db 100644 --- a/static/compliance/source/specialisms/creative-generative/generative-seller.yaml +++ b/static/compliance/source/specialisms/creative-generative/generative-seller.yaml @@ -75,10 +75,10 @@ fixtures: format_ids: - id: "display_300x250" - product_id: "outdoor_video_q2" - delivery_type: "non_guaranteed" + delivery_type: "guaranteed" channels: ["video"] format_ids: - - id: "video_30s" + - id: "video_15s" pricing_options: - product_id: "outdoor_display_q2" pricing_option_id: "cpm_guaranteed" @@ -89,7 +89,7 @@ fixtures: pricing_option_id: "cpm_standard" pricing_model: "cpm" currency: "USD" - fixed_price: 15.0 + fixed_price: 12.0 phases: - id: capability_discovery