@adcp/sdk 6.9.0 → 6.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/adcp.js +285 -5
- package/compliance/cache/3.0.6.previous/domains/brand/index.yaml +163 -0
- package/compliance/cache/3.0.6.previous/domains/creative/index.yaml +412 -0
- package/compliance/cache/3.0.6.previous/domains/governance/index.yaml +683 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/creative-reception.yaml +247 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/index.yaml +769 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/create_media_buy_async.yaml +232 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/creative_fate_after_cancellation.yaml +414 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/delivery_reporting.yaml +205 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/governance_approved.yaml +211 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/governance_conditions.yaml +196 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/governance_denied.yaml +192 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/governance_denied_recovery.yaml +244 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/invalid_transitions.yaml +284 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/inventory_list_no_match.yaml +143 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/inventory_list_targeting.yaml +271 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/measurement_terms_rejected.yaml +195 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/pending_creatives_to_start.yaml +250 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/proposal_finalize.yaml +243 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/refine_products.yaml +148 -0
- package/compliance/cache/3.0.6.previous/domains/media-buy/state-machine.yaml +442 -0
- package/compliance/cache/3.0.6.previous/domains/signals/index.yaml +266 -0
- package/compliance/cache/3.0.6.previous/domains/sponsored-intelligence/index.yaml +256 -0
- package/compliance/cache/3.0.6.previous/index.json +324 -0
- package/compliance/cache/3.0.6.previous/protocols/brand/index.yaml +163 -0
- package/compliance/cache/3.0.6.previous/protocols/creative/index.yaml +412 -0
- package/compliance/cache/3.0.6.previous/protocols/governance/index.yaml +683 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/creative-reception.yaml +247 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/index.yaml +769 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/create_media_buy_async.yaml +232 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/creative_fate_after_cancellation.yaml +414 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/delivery_reporting.yaml +205 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/governance_approved.yaml +211 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/governance_conditions.yaml +196 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/governance_denied.yaml +192 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/governance_denied_recovery.yaml +244 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/invalid_transitions.yaml +284 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/inventory_list_no_match.yaml +143 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/inventory_list_targeting.yaml +271 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/measurement_terms_rejected.yaml +195 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/pending_creatives_to_start.yaml +250 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/proposal_finalize.yaml +243 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/refine_products.yaml +148 -0
- package/compliance/cache/3.0.6.previous/protocols/media-buy/state-machine.yaml +442 -0
- package/compliance/cache/3.0.6.previous/protocols/signals/index.yaml +266 -0
- package/compliance/cache/3.0.6.previous/protocols/sponsored-intelligence/index.yaml +256 -0
- package/compliance/cache/3.0.6.previous/specialisms/audience-sync/index.yaml +280 -0
- package/compliance/cache/3.0.6.previous/specialisms/brand-rights/index.yaml +350 -0
- package/compliance/cache/3.0.6.previous/specialisms/brand-rights/scenarios/governance_denied.yaml +204 -0
- package/compliance/cache/3.0.6.previous/specialisms/collection-lists/index.yaml +359 -0
- package/compliance/cache/3.0.6.previous/specialisms/content-standards/index.yaml +572 -0
- package/compliance/cache/3.0.6.previous/specialisms/creative-ad-server/index.yaml +383 -0
- package/compliance/cache/3.0.6.previous/specialisms/creative-generative/generative-seller.yaml +758 -0
- package/compliance/cache/3.0.6.previous/specialisms/creative-generative/index.yaml +746 -0
- package/compliance/cache/3.0.6.previous/specialisms/creative-template/index.yaml +413 -0
- package/compliance/cache/3.0.6.previous/specialisms/governance-aware-seller/index.yaml +136 -0
- package/compliance/cache/3.0.6.previous/specialisms/governance-delivery-monitor/index.yaml +441 -0
- package/compliance/cache/3.0.6.previous/specialisms/governance-spend-authority/denied.yaml +221 -0
- package/compliance/cache/3.0.6.previous/specialisms/governance-spend-authority/index.yaml +330 -0
- package/compliance/cache/3.0.6.previous/specialisms/property-lists/index.yaml +482 -0
- package/compliance/cache/3.0.6.previous/specialisms/sales-broadcast-tv/index.yaml +689 -0
- package/compliance/cache/3.0.6.previous/specialisms/sales-catalog-driven/index.yaml +779 -0
- package/compliance/cache/3.0.6.previous/specialisms/sales-guaranteed/index.yaml +504 -0
- package/compliance/cache/3.0.6.previous/specialisms/sales-non-guaranteed/index.yaml +428 -0
- package/compliance/cache/3.0.6.previous/specialisms/sales-proposal-mode/index.yaml +520 -0
- package/compliance/cache/3.0.6.previous/specialisms/sales-social/index.yaml +584 -0
- package/compliance/cache/3.0.6.previous/specialisms/signal-marketplace/index.yaml +415 -0
- package/compliance/cache/3.0.6.previous/specialisms/signal-marketplace/scenarios/governance_denied.yaml +207 -0
- package/compliance/cache/3.0.6.previous/specialisms/signal-owned/index.yaml +316 -0
- package/compliance/cache/3.0.6.previous/test-kits/acme-outdoor.yaml +210 -0
- package/compliance/cache/3.0.6.previous/test-kits/bistro-oranje.yaml +126 -0
- package/compliance/cache/3.0.6.previous/test-kits/nova-motors.yaml +262 -0
- package/compliance/cache/3.0.6.previous/test-kits/osei-natural.yaml +126 -0
- package/compliance/cache/3.0.6.previous/test-kits/signed-requests-runner.yaml +155 -0
- package/compliance/cache/3.0.6.previous/test-kits/substitution-observer-runner.yaml +690 -0
- package/compliance/cache/3.0.6.previous/test-kits/summit-foods.yaml +125 -0
- package/compliance/cache/3.0.6.previous/test-kits/webhook-receiver-runner.yaml +265 -0
- package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/001-minimal-plan.json +43 -0
- package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/002-full-plan.json +217 -0
- package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/003-bookkeeping-stripped.json +60 -0
- package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/004a-human-review-omitted.json +43 -0
- package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/004b-human-review-explicit-null.json +49 -0
- package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/005a-policy-categories-order-1.json +53 -0
- package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/005b-policy-categories-order-2.json +57 -0
- package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/006a-ext-trace-v1.json +49 -0
- package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/006b-ext-trace-v2.json +53 -0
- package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/007-unicode-objectives.json +43 -0
- package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/008-numeric-canonicalization.json +65 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/README.md +219 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/canonicalization.json +241 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/keys.json +60 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/001-no-signature-header.json +24 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/002-wrong-tag.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/003-expired-signature.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/004-window-too-long.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/005-alg-not-allowed.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/006-missing-covered-component.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/007-missing-content-digest.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/008-unknown-keyid.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/009-key-ops-missing-verify.json +27 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/010-content-digest-mismatch.json +33 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/011-malformed-header.json +27 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/012-missing-expires-param.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/013-expires-le-created.json +27 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/014-missing-nonce-param.json +27 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/015-signature-invalid.json +28 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/016-replayed-nonce.json +35 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/017-key-revoked.json +38 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/018-digest-covered-when-forbidden.json +28 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/019-signature-without-signature-input.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/020-rate-abuse.json +34 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/021-duplicate-signature-input-label.json +31 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/022-multi-valued-content-type.json +31 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/023-multi-valued-content-digest.json +32 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/024-unquoted-string-param.json +31 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/025-jwk-alg-crv-mismatch.json +43 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/026-non-ascii-host.json +31 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/027-webhook-registration-authentication-unsigned.json +25 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/001-basic-post.json +30 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/002-post-with-content-digest.json +31 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/003-es256-post.json +30 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/004-multiple-signature-labels.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/005-default-port-stripped.json +30 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/006-dot-segment-path.json +30 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/007-query-byte-preserved.json +30 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/008-percent-encoded-path.json +30 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/009-percent-encoded-unreserved-decoded.json +30 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/010-percent-encoded-slash-preserved.json +30 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/011-ipv6-authority.json +30 -0
- package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/012-ipv6-authority-default-port-stripped.json +30 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/README.md +211 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/keys.json +61 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/001-wrong-tag.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/002-expired-signature.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/003-window-too-long.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/004-alg-not-allowed.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/005-missing-authority-component.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/006-missing-content-digest.json +25 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/007-unknown-keyid.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/008-wrong-adcp-use.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/009-content-digest-mismatch.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/010-malformed-signature-input.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/011-signature-without-input.json +25 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/012-missing-expires-param.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/013-expires-le-created.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/014-missing-nonce-param.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/015-signature-invalid.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/016-replayed-nonce.json +37 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/017-key-revoked.json +32 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/018-rate-abuse.json +33 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/019-revocation-stale.json +32 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/020-key-ops-missing-verify.json +41 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/021-base64-alphabet-mixing.json +26 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/001-basic-post.json +24 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/002-es256-post.json +24 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/003-multiple-signature-labels.json +24 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/004-default-port-stripped.json +24 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/005-percent-encoded-path.json +24 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/006-query-byte-preserved.json +24 -0
- package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/007-body-without-idempotency-key.json +25 -0
- package/compliance/cache/3.0.6.previous/universal/capability-discovery.yaml +125 -0
- package/compliance/cache/3.0.6.previous/universal/collection-lists-pagination-integrity.yaml +306 -0
- package/compliance/cache/3.0.6.previous/universal/content-standards-pagination-integrity.yaml +326 -0
- package/compliance/cache/3.0.6.previous/universal/deterministic-testing.yaml +1343 -0
- package/compliance/cache/3.0.6.previous/universal/error-compliance.yaml +474 -0
- package/compliance/cache/3.0.6.previous/universal/fictional-entities.yaml +307 -0
- package/compliance/cache/3.0.6.previous/universal/get-media-buys-pagination-integrity.yaml +160 -0
- package/compliance/cache/3.0.6.previous/universal/get-signals-pagination-integrity.yaml +211 -0
- package/compliance/cache/3.0.6.previous/universal/idempotency.yaml +593 -0
- package/compliance/cache/3.0.6.previous/universal/pagination-integrity-creative-formats.yaml +258 -0
- package/compliance/cache/3.0.6.previous/universal/pagination-integrity-list-accounts.yaml +262 -0
- package/compliance/cache/3.0.6.previous/universal/pagination-integrity.yaml +263 -0
- package/compliance/cache/3.0.6.previous/universal/property-lists-pagination-integrity.yaml +307 -0
- package/compliance/cache/3.0.6.previous/universal/runner-output-contract.yaml +358 -0
- package/compliance/cache/3.0.6.previous/universal/schema-validation.yaml +526 -0
- package/compliance/cache/3.0.6.previous/universal/security.yaml +431 -0
- package/compliance/cache/3.0.6.previous/universal/signed-requests.yaml +205 -0
- package/compliance/cache/3.0.6.previous/universal/storyboard-schema.yaml +1176 -0
- package/compliance/cache/3.0.6.previous/universal/v3-envelope-integrity.yaml +106 -0
- package/compliance/cache/3.0.6.previous/universal/webhook-emission.yaml +337 -0
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/create-adcp-server.d.ts +33 -0
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +127 -1
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/credential-policy.d.ts +221 -0
- package/dist/lib/server/credential-policy.d.ts.map +1 -0
- package/dist/lib/server/credential-policy.js +260 -0
- package/dist/lib/server/credential-policy.js.map +1 -0
- package/dist/lib/server/dynamic-registry.d.ts +219 -0
- package/dist/lib/server/dynamic-registry.d.ts.map +1 -0
- package/dist/lib/server/dynamic-registry.js +245 -0
- package/dist/lib/server/dynamic-registry.js.map +1 -0
- package/dist/lib/server/index.d.ts +8 -0
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +15 -4
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/operational-platform.d.ts +239 -0
- package/dist/lib/server/operational-platform.d.ts.map +1 -0
- package/dist/lib/server/operational-platform.js +94 -0
- package/dist/lib/server/operational-platform.js.map +1 -0
- package/dist/lib/server/wire-safe.d.ts +211 -0
- package/dist/lib/server/wire-safe.d.ts.map +1 -0
- package/dist/lib/server/wire-safe.js +231 -0
- package/dist/lib/server/wire-safe.js.map +1 -0
- package/dist/lib/server/wire-spec-fields.generated.d.ts +168 -0
- package/dist/lib/server/wire-spec-fields.generated.d.ts.map +1 -0
- package/dist/lib/server/wire-spec-fields.generated.js +172 -0
- package/dist/lib/server/wire-spec-fields.generated.js.map +1 -0
- package/dist/lib/testing/compliance/index.d.ts +2 -0
- package/dist/lib/testing/compliance/index.d.ts.map +1 -1
- package/dist/lib/testing/compliance/index.js +6 -1
- package/dist/lib/testing/compliance/index.js.map +1 -1
- package/dist/lib/testing/compliance/summary.d.ts +77 -0
- package/dist/lib/testing/compliance/summary.d.ts.map +1 -0
- package/dist/lib/testing/compliance/summary.js +176 -0
- package/dist/lib/testing/compliance/summary.js.map +1 -0
- package/dist/lib/testing/storyboard/compliance.d.ts +26 -0
- package/dist/lib/testing/storyboard/compliance.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/compliance.js +51 -0
- package/dist/lib/testing/storyboard/compliance.js.map +1 -1
- package/dist/lib/testing/storyboard/index.d.ts +2 -2
- package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/index.js +4 -2
- package/dist/lib/testing/storyboard/index.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +58 -5
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.d.ts.map +1 -1
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,1176 @@
|
|
|
1
|
+
# Storyboard Definition Schema
|
|
2
|
+
#
|
|
3
|
+
# Storyboards are narrative test workflows that walk agent builders through
|
|
4
|
+
# the sequence of calls their agent will receive, with context at each step.
|
|
5
|
+
#
|
|
6
|
+
# Each storyboard targets a specific agent interaction model and
|
|
7
|
+
# describes the flow from a caller's perspective.
|
|
8
|
+
|
|
9
|
+
# --- Schema definition ---
|
|
10
|
+
|
|
11
|
+
# A storyboard file must conform to this structure:
|
|
12
|
+
#
|
|
13
|
+
# id: string (unique identifier, e.g., "creative_template")
|
|
14
|
+
# version: string (semver, e.g., "1.0.0")
|
|
15
|
+
# title: string (human-readable title)
|
|
16
|
+
# category: enum — matches the specialism ID with hyphens replaced by underscores.
|
|
17
|
+
# Sales: sales_guaranteed | sales_non_guaranteed | sales_proposal_mode | sales_catalog_driven | sales_broadcast_tv | sales_streaming_tv | sales_social | sales_exchange | sales_retail_media
|
|
18
|
+
# Creative: creative_ad_server | creative_generative | creative_template
|
|
19
|
+
# Signals: signal_marketplace | signal_owned
|
|
20
|
+
# Governance: content_standards | property_lists | collection_lists | governance_delivery_monitor | governance_spend_authority | measurement_verification
|
|
21
|
+
# Brand: brand_rights
|
|
22
|
+
# Audiences: audience_sync
|
|
23
|
+
# Universal / domain-level: capability_discovery | schema_validation | behavioral_analysis | error_compliance | security | media_buy_seller | media_buy_governance_escalation | si_session
|
|
24
|
+
# Scenario variants use <category>/<variant> form, e.g. governance_spend_authority/denied, creative_generative/seller, brand_rights/governance_denied
|
|
25
|
+
# summary: string (one-line description for listings)
|
|
26
|
+
# track: enum (optional — compliance track this storyboard contributes to:
|
|
27
|
+
# core | products | media_buy | creative | reporting | governance |
|
|
28
|
+
# campaign_governance | signals | si | audiences | error_handling | brand | security)
|
|
29
|
+
# required_tools: string[] (optional — tool names the storyboard uses; empty for protocol-level tests)
|
|
30
|
+
# narrative: string (paragraph explaining the overall flow)
|
|
31
|
+
#
|
|
32
|
+
# requires_scenarios: string[] (optional — scenario IDs from storyboards/scenarios/ that must pass alongside this storyboard.
|
|
33
|
+
# Scenarios are small, focused behavior tests (e.g., "media_buy_seller/accepts_governance").
|
|
34
|
+
# The compliance engine resolves and runs them alongside the main storyboard.
|
|
35
|
+
#
|
|
36
|
+
# Flag flow across `requires_scenarios`:
|
|
37
|
+
# A `branch_set` flag contributed in a step of storyboard X is
|
|
38
|
+
# considered asserted if EITHER (a) a step in X's own phases asserts
|
|
39
|
+
# it via `assert_contribution` with `check: any_of, allowed_values:
|
|
40
|
+
# [<flag>]`, OR (b) a scenario Y listed in `X.requires_scenarios`
|
|
41
|
+
# asserts it via the same check. The `orphan_contribution` lint
|
|
42
|
+
# honors this flow.
|
|
43
|
+
#
|
|
44
|
+
# Reverse flow (a contribution in scenario Y asserted in parent X)
|
|
45
|
+
# is NOT supported — scenarios MUST be internally self-grading.
|
|
46
|
+
# The restriction preserves standalone lintability of scenarios
|
|
47
|
+
# and prevents cycles in the composition graph. Scenarios that
|
|
48
|
+
# contribute a flag without asserting it in their own phases will
|
|
49
|
+
# surface as `orphan_contribution` when linted standalone.
|
|
50
|
+
#
|
|
51
|
+
# Scenario IDs in `requires_scenarios` MUST match the referenced
|
|
52
|
+
# file's top-level `id:` exactly, and the referenced scenario file
|
|
53
|
+
# MUST exist in the source tree at build time. Duplicate IDs
|
|
54
|
+
# across files are a build-time error.)
|
|
55
|
+
#
|
|
56
|
+
# default_agent: string (optional — logical agent key used by multi-agent
|
|
57
|
+
# storyboard runners to route steps that do not have a unique specialism
|
|
58
|
+
# claimant in the runtime agents map. Resolved by the runner against the
|
|
59
|
+
# `agents` option passed to `runStoryboard({ agents: { sales: …, governance: …, … } })`.
|
|
60
|
+
#
|
|
61
|
+
# Key shape: free-form non-empty string, matched verbatim against the
|
|
62
|
+
# runtime `agents` map's keys. The spec does NOT constrain the key to the
|
|
63
|
+
# specialism enum (`sales`, `signals`, `governance`, `creative`, `brand`)
|
|
64
|
+
# because production multi-agent topologies legitimately fan out per-property
|
|
65
|
+
# (`nyt_sales`, `wsj_sales`), per-region (`sales_eu`, `sales_us`), or
|
|
66
|
+
# per-brand-rights-holder. Authors choose the convention that matches their
|
|
67
|
+
# operator's CI invocation; portability across operators is the author's
|
|
68
|
+
# concern, not the spec's.
|
|
69
|
+
#
|
|
70
|
+
# When to set it. Storyboards that exercise cross-domain tools — e.g.,
|
|
71
|
+
# `sync_creatives`, `list_creative_formats` — do not name a single specialism
|
|
72
|
+
# the runner can match. The author has the most context for which logical
|
|
73
|
+
# tenant should receive these calls (usually "wherever the seller is":
|
|
74
|
+
# `sales`). Encoding `default_agent: sales` once in the YAML beats
|
|
75
|
+
# re-asserting it on every CI invocation. (Note: `comply_test_controller`
|
|
76
|
+
# is routed via `prerequisites.controller_seeding`, not this field — the
|
|
77
|
+
# controller is a back-channel, not a specialism claimant.)
|
|
78
|
+
#
|
|
79
|
+
# Resolution order (runner contract — see adcp-client#1066, adcp-client#1355):
|
|
80
|
+
# 1. Step-level `agent:` override (if declared on the step).
|
|
81
|
+
# 2. Specialism-claimant match against the runtime agents map (matched
|
|
82
|
+
# via each agent's `get_adcp_capabilities.supported_protocols`):
|
|
83
|
+
# - Exactly one agent claims the step's task's specialism → route there.
|
|
84
|
+
# - Zero claimants → fall through to slot 3.
|
|
85
|
+
# - Two or more claimants → runner MUST grade the step `unrouted_step`
|
|
86
|
+
# rather than picking arbitrarily. Multi-claim is an operator-config
|
|
87
|
+
# error; the storyboard cannot disambiguate it. Slot 3/4 do NOT
|
|
88
|
+
# rescue this case — silently picking one would mask the misconfig.
|
|
89
|
+
# 3. Storyboard-level `default_agent` (THIS field) resolved against the
|
|
90
|
+
# runtime agents map. When the field is set but the key is absent from
|
|
91
|
+
# the map, the runner MUST grade the step `default_agent_unresolved`
|
|
92
|
+
# and MUST NOT fall through to slot 4 — silent fallback would invisibly
|
|
93
|
+
# override the storyboard author's encoded intent. Slot 4 fires only
|
|
94
|
+
# when the storyboard does NOT declare this field.
|
|
95
|
+
# 4. Run-options `default_agent` passed to `runStoryboard({ default_agent })`.
|
|
96
|
+
# Same key-resolution rule as slot 3: set-but-unmatched grades
|
|
97
|
+
# `default_agent_unresolved`; unset falls through to slot 5.
|
|
98
|
+
# 5. Fail-fast — runner raises `unrouted_step` and grades the step failed.
|
|
99
|
+
#
|
|
100
|
+
# Single-agent runs ignore this field entirely — there is no map to resolve
|
|
101
|
+
# against, every step routes to the only configured agent. Authors SHOULD
|
|
102
|
+
# still declare it for clarity; multi-agent runners treat it as advisory and
|
|
103
|
+
# single-agent runners as a no-op.
|
|
104
|
+
#
|
|
105
|
+
# Validation. The `<key>` MUST be a non-empty string. Validation that the
|
|
106
|
+
# key resolves to a configured tenant is a runtime concern (per the slot 3
|
|
107
|
+
# rule above), not a schema concern — the same storyboard runs against
|
|
108
|
+
# different topologies.)
|
|
109
|
+
#
|
|
110
|
+
# agent:
|
|
111
|
+
# interaction_model: enum (stateless_transform | stateful_preloaded | stateful_push | stateless_generate | media_buy_seller | marketplace_catalog | owned_signals | si_platform | brand_rights_holder | governance_agent)
|
|
112
|
+
# capabilities: string[] (AdCP capability flags: supports_transformation, has_creative_library, supports_generation, sells_media, accepts_briefs, supports_guaranteed, supports_non_guaranteed, catalog_signals)
|
|
113
|
+
# examples: string[] (real-world examples: "Celtra", "Innovid")
|
|
114
|
+
#
|
|
115
|
+
# caller:
|
|
116
|
+
# role: string (who initiates the calls: "buyer_agent", "orchestrator", "dsp")
|
|
117
|
+
# example: string (e.g., "Scope3", "Pinnacle Agency")
|
|
118
|
+
#
|
|
119
|
+
# prerequisites:
|
|
120
|
+
# description: string (what must be true before running this storyboard)
|
|
121
|
+
# test_kit: string (reference to a test kit file, e.g., "test-kits/acme-outdoor.yaml").
|
|
122
|
+
# Always a single path — test kits are not composed through this field. See
|
|
123
|
+
# "Test kit flavors" below for the two shapes a test kit may take and how
|
|
124
|
+
# storyboards compose brand identity with harness coordination when they need
|
|
125
|
+
# both.
|
|
126
|
+
# controller_seeding: boolean (optional — when true, the runner auto-injects a
|
|
127
|
+
# fixtures phase that seeds every entry in the storyboard's top-level
|
|
128
|
+
# `fixtures:` block via `comply_test_controller` before the main phases run.
|
|
129
|
+
# Storyboards that hardcode fixture IDs in sample_request payloads SHOULD
|
|
130
|
+
# declare this and populate `fixtures:`. Missing `seed_*` support on the
|
|
131
|
+
# agent grades the storyboard `not_applicable`, not failed.)
|
|
132
|
+
#
|
|
133
|
+
# fixtures: object (optional — declarative prerequisite state the runner seeds via
|
|
134
|
+
# comply_test_controller before executing phases. Structure:
|
|
135
|
+
# fixtures:
|
|
136
|
+
# products:
|
|
137
|
+
# - product_id: "test-product"
|
|
138
|
+
# delivery_type: "non_guaranteed"
|
|
139
|
+
# pricing_options:
|
|
140
|
+
# - pricing_option_id: "test-pricing"
|
|
141
|
+
# pricing_model: "cpm"
|
|
142
|
+
# creatives:
|
|
143
|
+
# - creative_id: "campaign_hero_video"
|
|
144
|
+
# status: "approved"
|
|
145
|
+
# format_id: { id: "video_30s" }
|
|
146
|
+
# plans:
|
|
147
|
+
# - plan_id: "gov_acme_q2_2027"
|
|
148
|
+
# budget: { total: 30000, currency: "USD" }
|
|
149
|
+
# media_buys:
|
|
150
|
+
# - media_buy_id: "mb_acme_q2_2026_auction"
|
|
151
|
+
# status: "active"
|
|
152
|
+
# Each top-level key maps to a seed_* scenario (products → seed_product,
|
|
153
|
+
# pricing_options → seed_pricing_option, creatives → seed_creative,
|
|
154
|
+
# plans → seed_plan, media_buys → seed_media_buy). Foreign-key dependency DAG
|
|
155
|
+
# the runner MUST honor when auto-seeding:
|
|
156
|
+
#
|
|
157
|
+
# product ──┬─→ pricing_option
|
|
158
|
+
# ├─→ plan
|
|
159
|
+
# └─→ media_buy
|
|
160
|
+
# creative ────→ media_buy
|
|
161
|
+
# plan ────────→ media_buy
|
|
162
|
+
#
|
|
163
|
+
# Products seed before pricing_options and plans that reference them;
|
|
164
|
+
# products, creatives, and plans all seed before media_buys that reference
|
|
165
|
+
# them. See docs/building/implementation/comply-test-controller.mdx for the
|
|
166
|
+
# full seeding semantics and per-scenario param shapes.
|
|
167
|
+
#
|
|
168
|
+
# Storyboards SHOULD prefer the `fixtures:` block over hardcoded sample_request
|
|
169
|
+
# IDs whose existence the runner cannot verify.
|
|
170
|
+
#
|
|
171
|
+
# Hardcoded-literal fixtures in sample_request (e.g., product_id: "test-product"
|
|
172
|
+
# without a matching `fixtures:` entry) are permitted through 3.x for existing
|
|
173
|
+
# storyboards that pre-seed matching IDs out of band, and SHOULD migrate to the
|
|
174
|
+
# `fixtures:` block before 4.0. New storyboards authored after this spec edit
|
|
175
|
+
# MUST NOT hardcode fixture IDs without a corresponding `fixtures:` block or a
|
|
176
|
+
# test-kit substitution.)
|
|
177
|
+
#
|
|
178
|
+
# --- Test kit flavors ---
|
|
179
|
+
#
|
|
180
|
+
# Files under `static/compliance/source/test-kits/` come in two shapes. The
|
|
181
|
+
# distinction is not enforced by a separate field today — it's identified by
|
|
182
|
+
# which fixture data the kit carries.
|
|
183
|
+
#
|
|
184
|
+
# Brand kit — carries a brand identity and authentication fixture. Declares:
|
|
185
|
+
# id string (short identifier)
|
|
186
|
+
# auth.api_key string (`demo-<kit>-v1` pattern; the demo Bearer the
|
|
187
|
+
# runner sends on positive api-key probes)
|
|
188
|
+
# auth.probe_task string (the protected read the runner calls under
|
|
189
|
+
# `auth: none` and with a random-invalid key)
|
|
190
|
+
# brand object (house + brand_id + names + logos + colors +
|
|
191
|
+
# fonts + tone — full brand.json payload)
|
|
192
|
+
# + optional products, creatives, pricing_options, destinations, etc.
|
|
193
|
+
#
|
|
194
|
+
# Today's brand kits: acme-outdoor, bistro-oranje, nova-motors, osei-natural,
|
|
195
|
+
# summit-foods. Used by storyboards that exercise brand-specific AdCP protocol
|
|
196
|
+
# flows (creative, media-buy, signals, governance, etc.).
|
|
197
|
+
#
|
|
198
|
+
# Runner contract — carries a harness coordination contract. Declares:
|
|
199
|
+
# id string (short identifier)
|
|
200
|
+
# applies_to object or list (which storyboards/specialisms consume
|
|
201
|
+
# this contract, e.g., `{ universal_storyboard: signed-requests }`
|
|
202
|
+
# or `{ specialism: sales-catalog-driven }`)
|
|
203
|
+
# + contract-specific fields describing how the runner should pre-configure
|
|
204
|
+
# the agent under test (e.g., `receiver_urls:`, `retry_replay_contract:`
|
|
205
|
+
# for webhook-receiver-runner; `endpoint_modes:` for signed-requests)
|
|
206
|
+
#
|
|
207
|
+
# Today's runner contracts: signed-requests-runner, substitution-observer-
|
|
208
|
+
# runner, webhook-receiver-runner. They carry no credentials today — the
|
|
209
|
+
# runner injects no auth for these storyboards because the storyboards test
|
|
210
|
+
# harness infrastructure (signature verification, webhook receiver
|
|
211
|
+
# coordination, URL substitution observability), not brand-specific AdCP
|
|
212
|
+
# flows. A future runner contract could legitimately carry its own test-
|
|
213
|
+
# coordination principal (e.g., a callback-authenticating receiver), so the
|
|
214
|
+
# "no credentials" property is temporal, not structural.
|
|
215
|
+
#
|
|
216
|
+
# Enforcement. `scripts/lint-storyboard-test-kits.cjs` fails the build if a
|
|
217
|
+
# kit under `test-kits/` declares neither `auth.api_key` nor `applies_to` —
|
|
218
|
+
# that's the bimodal partition this section describes. The lint tolerates
|
|
219
|
+
# kits that declare both (the future-branded-runner case above).
|
|
220
|
+
#
|
|
221
|
+
# Composition. Storyboards that need BOTH a brand identity AND a harness
|
|
222
|
+
# contract declare the brand kit in `prerequisites.test_kit` and opt into the
|
|
223
|
+
# runner contract through the `requires_contract: <runner_id>` field on
|
|
224
|
+
# specific assertion tasks that need it — today that's `expect_substitution_safe`
|
|
225
|
+
# and `expect_webhook*` (see their task definitions below for the per-task
|
|
226
|
+
# field shape). When the named contract is not in scope for the runner, the
|
|
227
|
+
# task grades as `not_applicable` rather than failing. Example:
|
|
228
|
+
# `specialisms/sales-catalog-driven/index.yaml` declares
|
|
229
|
+
# `prerequisites.test_kit: "test-kits/acme-outdoor.yaml"` and its
|
|
230
|
+
# `expect_substitution_safe` step carries `requires_contract:
|
|
231
|
+
# substitution_observer_runner`.
|
|
232
|
+
#
|
|
233
|
+
# Storyboards that test pure harness infrastructure without a specific brand
|
|
234
|
+
# (e.g., `universal/signed-requests.yaml`,
|
|
235
|
+
# `universal/webhook-emission.yaml`) point `prerequisites.test_kit` directly
|
|
236
|
+
# at the runner contract and fall back on `task_default:` when any
|
|
237
|
+
# `$test_kit.operations.<name>` reference resolves to null (see Step field
|
|
238
|
+
# docs).
|
|
239
|
+
#
|
|
240
|
+
# `test_kit=<path>` in the contradiction-lint fingerprint disambiguates the
|
|
241
|
+
# two flavors — `auth=kit_default` resolves to the kit's api_key for brand
|
|
242
|
+
# kits and to no-credential for runner contracts, but the two never collide
|
|
243
|
+
# because the `test_kit=` component separates them.
|
|
244
|
+
#
|
|
245
|
+
# Future: if a storyboard ever needs to compose TWO runner contracts
|
|
246
|
+
# simultaneously (e.g., signed-requests + substitution-observer), the step-
|
|
247
|
+
# level `requires_contract:` field will need to accept a list. Not reachable
|
|
248
|
+
# today.
|
|
249
|
+
#
|
|
250
|
+
# See `docs/contributing/storyboard-authoring.md` for the author-workflow
|
|
251
|
+
# side of kit selection.
|
|
252
|
+
#
|
|
253
|
+
# phases: array of Phase objects
|
|
254
|
+
#
|
|
255
|
+
# --- Phase ---
|
|
256
|
+
#
|
|
257
|
+
# id: string (unique within storyboard)
|
|
258
|
+
# title: string (human-readable phase title)
|
|
259
|
+
# narrative: string (paragraph explaining this phase from the caller's perspective)
|
|
260
|
+
#
|
|
261
|
+
# steps: array of Step objects
|
|
262
|
+
#
|
|
263
|
+
# --- Step ---
|
|
264
|
+
#
|
|
265
|
+
# id: string (unique within phase)
|
|
266
|
+
# title: string (human-readable step title)
|
|
267
|
+
# narrative: string (what's happening and why)
|
|
268
|
+
# task: string (AdCP task name: list_creative_formats, preview_creative, build_creative, get_products,
|
|
269
|
+
# create_media_buy, sync_accounts, etc. May reference a test-kit field, e.g.
|
|
270
|
+
# "$test_kit.auth.probe_task", in which case `task_default` provides the fallback.)
|
|
271
|
+
# task_default: string (optional — default task name when `task` is a test-kit reference that resolves to null)
|
|
272
|
+
# schema_ref: string (path to request schema, e.g., "creative/list-creative-formats-request.json" or "media-buy/get-products-request.json")
|
|
273
|
+
# response_schema_ref: string (path to response schema)
|
|
274
|
+
# doc_ref: string (path to documentation page)
|
|
275
|
+
# comply_scenario: string (maps to @adcp/client testing scenario, e.g., "creative_sync")
|
|
276
|
+
# expected: string (human-readable description of expected behavior)
|
|
277
|
+
# stateful: boolean (does this step depend on state from a previous step?)
|
|
278
|
+
#
|
|
279
|
+
# sample_request: object (optional — example request payload for this step)
|
|
280
|
+
# Placeholders: `$context.<name>` and `{{prior_step.<id>.<field>}}`. See
|
|
281
|
+
# the "Substitution" section below for the full semantics. For the narrow
|
|
282
|
+
# case where the SDK's per-task request builder discards a body field
|
|
283
|
+
# from `sample_request`, use `context_inputs:` (above) as the
|
|
284
|
+
# post-builder injection path.
|
|
285
|
+
# sample_response: object (optional — example expected response; also surfaces in published docs)
|
|
286
|
+
#
|
|
287
|
+
# auth: "none" | object (optional — overrides the transport's default credentials for this step)
|
|
288
|
+
# auth: none → strip any configured credentials before sending
|
|
289
|
+
# auth: { type: api_key, from_test_kit: true } → use the API key at `auth.api_key` in the test kit
|
|
290
|
+
# auth: { type: api_key, from_test_kit: "auth.principals.<name>.api_key" }
|
|
291
|
+
# → select a named principal within a multi-principal kit
|
|
292
|
+
# (forward-compatible shape; no kit exposes multiple
|
|
293
|
+
# principals today)
|
|
294
|
+
# auth: { type: api_key, value_strategy: random_invalid }
|
|
295
|
+
# → use a per-run random bogus API key (for invalid-key probes).
|
|
296
|
+
# Runner generates `invalid-<32 random bytes>` per run.
|
|
297
|
+
# auth: { type: oauth_bearer, value_strategy: random_invalid_jwt }
|
|
298
|
+
# → use a per-run random JWT-shaped bogus Bearer token.
|
|
299
|
+
# Runner generates `<base64url random>.<base64url random>.<base64url random>` per run.
|
|
300
|
+
#
|
|
301
|
+
# DISALLOWED: `auth: { type: ..., value: "<literal>" }` — literal credentials in
|
|
302
|
+
# storyboard YAML are a code smell (they bind the storyboard to a specific value,
|
|
303
|
+
# can't rotate without rewriting, and leak plaintext identity into source).
|
|
304
|
+
# `lint-storyboard-auth-shape.cjs` (rule `literal_value`) fails the build on this
|
|
305
|
+
# shape. Use `from_test_kit` or `value_strategy` instead. See #2720.
|
|
306
|
+
#
|
|
307
|
+
# omit_idempotency_key: boolean (optional — when true on a mutating-task step,
|
|
308
|
+
# the runner skips `applyIdempotencyInvariant` and signals the SDK client via
|
|
309
|
+
# `skipIdempotencyAutoInject` so the request reaches the agent WITHOUT an
|
|
310
|
+
# `idempotency_key`. Use for missing-key rejection vectors — without this flag
|
|
311
|
+
# the SDK auto-injects a key and the agent never sees the missing-key path.
|
|
312
|
+
# Applies only to mutating tasks (create/update/cancel); ignored on reads.)
|
|
313
|
+
#
|
|
314
|
+
# contributes_to: string (optional — marks this step as contributing a named flag
|
|
315
|
+
# that a later assert_contribution step can require)
|
|
316
|
+
# contributes: boolean (optional — shorthand for `contributes_to: <enclosing phase's branch_set.id>`
|
|
317
|
+
# introduced alongside first-class `branch_set:` declarations.
|
|
318
|
+
# Legal only inside a phase that declares `branch_set:`; the runner's
|
|
319
|
+
# loader resolves `contributes: true` to the phase's branch_set.id
|
|
320
|
+
# and raises a storyboard-load error on `contributes: true` outside
|
|
321
|
+
# a branch_set phase or on a step that also declares `contributes_to`.
|
|
322
|
+
# See adcp-client#693 and `lint:storyboard-branch-sets` for the
|
|
323
|
+
# full rule set.)
|
|
324
|
+
# contributes_if: string (optional — runner-evaluated expression gating the contribution)
|
|
325
|
+
# Supported grammar (single form; not a general-purpose DSL):
|
|
326
|
+
# "prior_step.<step_id>.passed" → true if the named prior step in the same storyboard passed
|
|
327
|
+
# Storyboard authors should prefer expressing gating through step ordering when possible.
|
|
328
|
+
#
|
|
329
|
+
# provides_state_for: string | string[] (optional — declares that this step's pass
|
|
330
|
+
# establishes equivalent state for the named peer step(s) in the same phase. Used
|
|
331
|
+
# to rescue the cascade-skip default when one of two interchangeable stateful
|
|
332
|
+
# steps is missing on the agent: e.g., explicit-mode sellers that pre-provision
|
|
333
|
+
# accounts out-of-band declare `sync_accounts` as missing_tool but expose
|
|
334
|
+
# `list_accounts` as the canonical alternative — declaring
|
|
335
|
+
# `provides_state_for: sync_accounts` on `list_accounts` tells the runner that
|
|
336
|
+
# the substitute's pass satisfies the same downstream state contract.
|
|
337
|
+
#
|
|
338
|
+
# Grammar parallels `contributes_to`: declared on the substitute step, names
|
|
339
|
+
# the target peer(s) whose state it provides. The two fields are independent —
|
|
340
|
+
# `contributes_to` flows branch-set flags into a later `assert_contribution`;
|
|
341
|
+
# `provides_state_for` waives a peer's missing-tool / missing-test-controller
|
|
342
|
+
# cascade so downstream stateful steps still run.
|
|
343
|
+
#
|
|
344
|
+
# Array semantics: `provides_state_for: [A, B]` is ALL-OF — one substitute pass
|
|
345
|
+
# establishes state for both A and B simultaneously. ANY-OF semantics are not
|
|
346
|
+
# supported; if a substitute only conditionally satisfies a peer, model it as
|
|
347
|
+
# two separate substitute steps.
|
|
348
|
+
#
|
|
349
|
+
# Validation rules (enforced by lint:storyboard-provides-state-for, fail at
|
|
350
|
+
# parse time):
|
|
351
|
+
# - Same-phase only. The target step MUST live in the same phase as the
|
|
352
|
+
# substitute step. Cross-phase substitution is rejected; a cross-phase
|
|
353
|
+
# state contract belongs in `context_outputs` / `context_inputs`, not
|
|
354
|
+
# this field.
|
|
355
|
+
# - Target step must exist in the same phase by `id`.
|
|
356
|
+
# - Target step MUST declare `stateful: true`. Stateless peers don't
|
|
357
|
+
# carry a state contract to substitute for.
|
|
358
|
+
# - Substitute step MUST declare `stateful: true`. A stateless step
|
|
359
|
+
# cannot establish equivalent state on the agent side.
|
|
360
|
+
# - Self-references (`provides_state_for: <own_id>`) are rejected.
|
|
361
|
+
# - Cycles (A→B and B→A in the same phase) are rejected. The peer-graph
|
|
362
|
+
# per phase MUST be acyclic.
|
|
363
|
+
#
|
|
364
|
+
# Runner behavior (see runner-output-contract.yaml > skip_result.reasons.peer_substituted):
|
|
365
|
+
# - When the substitute step passes AND the target peer would otherwise
|
|
366
|
+
# grade `missing_tool` / `missing_test_controller`, the runner MUST
|
|
367
|
+
# grade the target peer with skip reason `peer_substituted` (not
|
|
368
|
+
# `missing_tool`) and MUST NOT cascade `prerequisite_failed` to
|
|
369
|
+
# downstream stateful steps in subsequent phases.
|
|
370
|
+
# - When the substitute step itself fails or is skipped, the cascade
|
|
371
|
+
# proceeds as if `provides_state_for` were absent — the substitute
|
|
372
|
+
# did not establish the state it claimed.
|
|
373
|
+
#
|
|
374
|
+
# See adcontextprotocol/adcp#3734 (mechanism rationale) and
|
|
375
|
+
# adcp-client#1130 (the cascade-skip default this field rescues).
|
|
376
|
+
#
|
|
377
|
+
# context_outputs: array of ContextOutput objects (optional — capture values from
|
|
378
|
+
# this step's response into the runner's context accumulator so later steps can
|
|
379
|
+
# reference them via $context.<name>). Each entry:
|
|
380
|
+
# - name: string (the context variable to populate; MUST be unique within the
|
|
381
|
+
# storyboard run)
|
|
382
|
+
# path: string (JSON path against this step's response body, e.g.
|
|
383
|
+
# "media_buy_id", "accounts[0].account_id", "plans[0].plan_id")
|
|
384
|
+
#
|
|
385
|
+
# Special path prefix `task_completion.<inner>`: when the immediate response
|
|
386
|
+
# is a non-terminal task envelope (status `submitted` / `working` /
|
|
387
|
+
# `input-required`, carrying a `task_id`), the runner polls `tasks/get`
|
|
388
|
+
# until the task reaches a terminal state and resolves `<inner>` against
|
|
389
|
+
# the completion artifact's `data` instead of the immediate response. Use
|
|
390
|
+
# for captures whose value only exists on the completion artifact — e.g.
|
|
391
|
+
# the seller-assigned `media_buy_id` on an IO-signing / async-signed HITL
|
|
392
|
+
# flow where `create_media_buy` returns `submitted` and the ID lands on
|
|
393
|
+
# the completion artifact. Without the prefix, the literal key
|
|
394
|
+
# `task_completion` is looked up on the immediate response, which fails
|
|
395
|
+
# as `capture_path_not_resolvable`. Requires runner >= adcp-client v6.7;
|
|
396
|
+
# older runners treat the prefix as a literal key. See adcp-client#1417
|
|
397
|
+
# (rationale) and adcp-client#1426 (implementation).
|
|
398
|
+
#
|
|
399
|
+
# Runner behavior:
|
|
400
|
+
# - Captures occur AFTER the step's validations pass. A failed step MUST NOT
|
|
401
|
+
# populate the context accumulator — downstream $context.<name> references
|
|
402
|
+
# resolve to an unresolved_substitution error, which grades the dependent
|
|
403
|
+
# step as failed rather than the storyboard passing on fabricated state.
|
|
404
|
+
# - Paths that do not resolve in the response body are a runner-side grading
|
|
405
|
+
# failure on THIS step (capture_path_not_resolvable), not the downstream
|
|
406
|
+
# reader — the capture declared a contract that the response did not meet.
|
|
407
|
+
# - The accumulator is storyboard-run-scoped; values do not leak across runs.
|
|
408
|
+
#
|
|
409
|
+
# context_inputs: array of ContextInput objects (optional — inject captured
|
|
410
|
+
# context values into the request at a specific path AFTER the per-task
|
|
411
|
+
# request builder runs).
|
|
412
|
+
#
|
|
413
|
+
# Shape:
|
|
414
|
+
# context_inputs:
|
|
415
|
+
# - key: product_format_id # name populated by a prior context_outputs capture
|
|
416
|
+
# inject_at: "format_ids[0]" # JSON path inside the request body
|
|
417
|
+
#
|
|
418
|
+
# Do NOT use `context_inputs` unless `$context.<name>` inside
|
|
419
|
+
# `sample_request:` has been tried and verified not to reach the wire.
|
|
420
|
+
# The default substitution path is `$context.<name>` placeholders inside
|
|
421
|
+
# `sample_request:` — keep the request body honest about what the agent
|
|
422
|
+
# will receive. Reasons the default is preferred:
|
|
423
|
+
# - The published sample_response/sample_request pair is what seller
|
|
424
|
+
# engineers read to implement against the storyboard; anything the
|
|
425
|
+
# runner injects out-of-band is invisible there.
|
|
426
|
+
# - `$context.<name>` produces a pointed `unresolved_substitution` failure
|
|
427
|
+
# when the referenced capture is missing. `context_inputs` is
|
|
428
|
+
# silently a no-op on a missing `key` (runner:
|
|
429
|
+
# `if (input.key in context) setPath(...)`) — a typo in `key` or a
|
|
430
|
+
# failed capture produces an empty field rather than a loud error.
|
|
431
|
+
#
|
|
432
|
+
# Legitimate uses:
|
|
433
|
+
# - The @adcp/client per-task request builder for a given tool discards
|
|
434
|
+
# body fields from `sample_request` (the runner's post-builder merge
|
|
435
|
+
# only forwards envelope fields: `context`, `ext`, `idempotency_key`,
|
|
436
|
+
# `push_notification_config`). The observable symptom is that the
|
|
437
|
+
# agent answers with an unfiltered/default result and a round-trip
|
|
438
|
+
# or substitution-observer validation grades on fabricated state.
|
|
439
|
+
# `context_inputs` runs AFTER the builder, so it round-trips the
|
|
440
|
+
# captured value to the wire regardless.
|
|
441
|
+
# - Dependency-aware multi-instance dispatch, where the runner needs to
|
|
442
|
+
# route a step to a specific replica URL derived from a prior step's
|
|
443
|
+
# response (not applicable to single-instance storyboards).
|
|
444
|
+
#
|
|
445
|
+
# Value types are preserved. The captured value is written to `inject_at`
|
|
446
|
+
# as-is — object and numeric captures are NOT stringified, matching the
|
|
447
|
+
# `$context.<name>` behavior described below.
|
|
448
|
+
#
|
|
449
|
+
# Document every SDK-gap-workaround entry inline. The comment MUST name
|
|
450
|
+
# the SDK version that carries the gap and the upstream PR that closes
|
|
451
|
+
# it, so the entry can be removed when we bump past that release. See
|
|
452
|
+
# `static/compliance/source/protocols/media-buy/index.yaml`
|
|
453
|
+
# (`list_formats_integrity` step) for a worked example tied to
|
|
454
|
+
# adcontextprotocol/adcp-client#789.
|
|
455
|
+
#
|
|
456
|
+
# validations: array of Validation objects (optional)
|
|
457
|
+
#
|
|
458
|
+
# expect_error: boolean (optional, default false — when true, the runner expects
|
|
459
|
+
# the agent to return an error response for this step. Paired with `negative_path`
|
|
460
|
+
# to control whether the step's `sample_request` is schema-validated.)
|
|
461
|
+
#
|
|
462
|
+
# negative_path: "schema_invalid" | "payload_well_formed" (optional, default "schema_invalid")
|
|
463
|
+
# Disambiguates two categories of negative-path steps when `expect_error: true`:
|
|
464
|
+
# - schema_invalid (default): the `sample_request` payload is intentionally malformed
|
|
465
|
+
# (missing required field, wrong type, bad enum value, etc.) to verify the agent's
|
|
466
|
+
# validation response. Schema validation is SKIPPED on this step.
|
|
467
|
+
# - payload_well_formed: the `sample_request` payload is schema-valid but the agent
|
|
468
|
+
# rejects it at runtime — wrong state-machine transition, resource not found,
|
|
469
|
+
# governance denial, auth failure, temporal constraint, etc. Schema validation
|
|
470
|
+
# RUNS even though the agent is expected to return an error. Shape drift on these
|
|
471
|
+
# steps is caught statically.
|
|
472
|
+
# Omitting `negative_path` when `expect_error: true` behaves as `schema_invalid`
|
|
473
|
+
# (backwards-compatible default).
|
|
474
|
+
#
|
|
475
|
+
# sample_request_skip_schema: boolean (optional — explicit opt-out of schema
|
|
476
|
+
# validation for a step regardless of `expect_error` / `negative_path`. Use
|
|
477
|
+
# sparingly; prefer `negative_path: schema_invalid` for intentionally malformed
|
|
478
|
+
# negative-path fixtures.)
|
|
479
|
+
#
|
|
480
|
+
# --- Phase (optional fields for conditional execution) ---
|
|
481
|
+
#
|
|
482
|
+
# optional: boolean (default false — see semantics below)
|
|
483
|
+
# skip_if: string (expression — phase is skipped when the expression evaluates to true,
|
|
484
|
+
# e.g., "!test_kit.auth.api_key")
|
|
485
|
+
# branch_set: object (optional — first-class declaration of branch-set membership;
|
|
486
|
+
# see "Branch sets" section below)
|
|
487
|
+
#
|
|
488
|
+
# Optional phase semantics:
|
|
489
|
+
# - An `optional: true` phase always runs unless `skip_if` evaluates to true.
|
|
490
|
+
# `skip_if` (e.g., "!test_kit.auth.api_key") means "skip this phase when the
|
|
491
|
+
# expression is true"; without `skip_if`, the phase runs every time.
|
|
492
|
+
# - Failures inside an optional phase do NOT fail the overall storyboard on
|
|
493
|
+
# their own — the phase is for accumulating contributions toward a later
|
|
494
|
+
# `assert_contribution` check. The final assert is what fails the storyboard
|
|
495
|
+
# when no optional path succeeded.
|
|
496
|
+
# - A non-optional phase that fails fails the storyboard unconditionally.
|
|
497
|
+
#
|
|
498
|
+
# Branch sets:
|
|
499
|
+
# A branch set is a group of peer optional phases exercising mutually
|
|
500
|
+
# exclusive agent behaviors for the same trigger — e.g., an agent that
|
|
501
|
+
# rejects a past start_time vs. an agent that accepts-and-adjusts. A
|
|
502
|
+
# later `assert_contribution` step over the set's `branch_set.id` fails
|
|
503
|
+
# the storyboard only when NO branch contributed.
|
|
504
|
+
#
|
|
505
|
+
# Minimal example — two peer phases and the assertion that grades them:
|
|
506
|
+
#
|
|
507
|
+
# phases:
|
|
508
|
+
# - id: past_start_reject_path
|
|
509
|
+
# optional: true
|
|
510
|
+
# branch_set:
|
|
511
|
+
# id: past_start_handled
|
|
512
|
+
# semantics: any_of
|
|
513
|
+
# steps:
|
|
514
|
+
# - id: create_buy_past_start_reject
|
|
515
|
+
# task: create_media_buy
|
|
516
|
+
# contributes_to: past_start_handled
|
|
517
|
+
# # ... expect INVALID_REQUEST ...
|
|
518
|
+
#
|
|
519
|
+
# - id: past_start_adjust_path
|
|
520
|
+
# optional: true
|
|
521
|
+
# branch_set:
|
|
522
|
+
# id: past_start_handled
|
|
523
|
+
# semantics: any_of
|
|
524
|
+
# steps:
|
|
525
|
+
# - id: create_buy_past_start_adjust
|
|
526
|
+
# task: create_media_buy
|
|
527
|
+
# contributes_to: past_start_handled
|
|
528
|
+
# # ... expect media_buy_id ...
|
|
529
|
+
#
|
|
530
|
+
# - id: past_start_enforcement
|
|
531
|
+
# steps:
|
|
532
|
+
# - task: assert_contribution
|
|
533
|
+
# validations:
|
|
534
|
+
# - check: any_of
|
|
535
|
+
# allowed_values: [past_start_handled]
|
|
536
|
+
#
|
|
537
|
+
# `@adcp/client` 5.8.0+ also accepts the boolean shorthand
|
|
538
|
+
# `contributes: true` on a step inside a branch_set phase — the runner's
|
|
539
|
+
# loader resolves it to the enclosing phase's `branch_set.id`. The two
|
|
540
|
+
# forms are semantically identical; `contributes: true` is preferred
|
|
541
|
+
# inside a branch_set phase because it eliminates the typo surface (the
|
|
542
|
+
# string form must equal `branch_set.id` exactly). Authors may use
|
|
543
|
+
# either; the lint enforces both.
|
|
544
|
+
#
|
|
545
|
+
# New storyboards MUST declare branch-set membership with a `branch_set:`
|
|
546
|
+
# object. The implicit-detection fallback below exists only so pre-#2633
|
|
547
|
+
# storyboards keep running unchanged.
|
|
548
|
+
#
|
|
549
|
+
# `branch_set:` field shape:
|
|
550
|
+
# - id: non-empty string. MUST equal the `contributes_to` value
|
|
551
|
+
# on every contributing step inside the phase and MUST
|
|
552
|
+
# appear in the `assert_contribution` step's any_of
|
|
553
|
+
# allowed_values.
|
|
554
|
+
# - semantics: `any_of`. No other value is supported today.
|
|
555
|
+
#
|
|
556
|
+
# Per-step contribution signalling is REQUIRED on each contributing step —
|
|
557
|
+
# the phase-level `branch_set:` declares membership but does not replace
|
|
558
|
+
# the step-level flag. Runners accumulate contributions at the step level.
|
|
559
|
+
# Two equivalent forms:
|
|
560
|
+
# - `contributes: true` (preferred inside a branch_set phase; resolves
|
|
561
|
+
# to the enclosing phase's `branch_set.id`)
|
|
562
|
+
# - `contributes_to: <branch_set.id>` (string form; required outside a
|
|
563
|
+
# branch_set phase and must equal `branch_set.id` inside one)
|
|
564
|
+
#
|
|
565
|
+
# Authoring rules (enforced by `lint:storyboard-branch-sets`):
|
|
566
|
+
# - A phase with `branch_set:` MUST set `optional: true`. A
|
|
567
|
+
# non-optional phase's failure would fail the storyboard
|
|
568
|
+
# unconditionally and defeat the any_of semantics.
|
|
569
|
+
# - All phases in a storyboard sharing the same `branch_set.id` MUST
|
|
570
|
+
# share the same `branch_set.semantics`.
|
|
571
|
+
# - A storyboard with a `branch_set:` declaration MUST contain an
|
|
572
|
+
# `assert_contribution` step whose `validations[].check: any_of`
|
|
573
|
+
# includes the `branch_set.id` in `allowed_values`. Any
|
|
574
|
+
# `contributes_to:` / `contributes: true` whose resolved flag has no
|
|
575
|
+
# matching assert_contribution is flagged as `orphan_contribution` —
|
|
576
|
+
# nothing grades it.
|
|
577
|
+
# - Any step inside a branch-set phase that declares `contributes_to`
|
|
578
|
+
# MUST use the value `<branch_set.id>`.
|
|
579
|
+
# - A step MUST NOT declare both `contributes` and `contributes_to`
|
|
580
|
+
# (ambiguous). `contributes: true` is legal only inside a phase
|
|
581
|
+
# that declares `branch_set:`.
|
|
582
|
+
# - If any phase declares `branch_set: { id: X, ... }`, every optional
|
|
583
|
+
# phase in the same storyboard whose steps contribute to X MUST also
|
|
584
|
+
# declare `branch_set: { id: X, ... }`. A mixed-mode storyboard (one
|
|
585
|
+
# peer declared, one peer relying on implicit detection) would be
|
|
586
|
+
# graded as a single-member set by a runner preferring the explicit
|
|
587
|
+
# declaration.
|
|
588
|
+
#
|
|
589
|
+
# Runner grading:
|
|
590
|
+
# Runners MUST prefer the explicit `branch_set:` declaration when
|
|
591
|
+
# present and fall back to implicit detection otherwise (see below).
|
|
592
|
+
# - If at least one peer branch contributed the `branch_set.id`, the
|
|
593
|
+
# non-contributing branch's failing steps MUST be graded with skip
|
|
594
|
+
# reason `peer_branch_taken` (see
|
|
595
|
+
# runner-output-contract.yaml > skip_result.reasons.peer_branch_taken).
|
|
596
|
+
# They MUST NOT surface as `failed` in the storyboard summary, and
|
|
597
|
+
# MUST NOT be conflated with `not_applicable` (reserved for coverage
|
|
598
|
+
# gaps — agent didn't declare the protocol).
|
|
599
|
+
# - If no branch contributed, the final `assert_contribution` step
|
|
600
|
+
# fails the storyboard. Individual branch step failures still grade
|
|
601
|
+
# `failed` so the implementor sees what each branch observed.
|
|
602
|
+
#
|
|
603
|
+
# When to use a branch set:
|
|
604
|
+
# Only when the AdCP spec permits multiple conformant behaviors for
|
|
605
|
+
# the same trigger. If the spec mandates one behavior, write a
|
|
606
|
+
# required phase — a branch set where only one branch is legal
|
|
607
|
+
# weakens the test to any_of and hides regressions. Branch sets are
|
|
608
|
+
# not for optional features (use `skip_if` and coverage flags) or
|
|
609
|
+
# for progressive disclosure (use sequential phases).
|
|
610
|
+
#
|
|
611
|
+
# Authoring guidance: keep branch sets flat — two or three peer phases
|
|
612
|
+
# is typical. Nested branch sets (a branch set whose member is itself a
|
|
613
|
+
# branch set) are out of scope for runners. If a scenario needs deeper
|
|
614
|
+
# branching, restructure into separate storyboards or a linear sequence.
|
|
615
|
+
#
|
|
616
|
+
# Implicit branch-set detection (DEPRECATED — pre-#2633 storyboards only):
|
|
617
|
+
# When no phase in a storyboard declares `branch_set:`, runners fall
|
|
618
|
+
# back to the legacy rule: a branch set is the set of `optional: true`
|
|
619
|
+
# phases whose steps declare the same `contributes_to: <flag>` value
|
|
620
|
+
# as a later `assert_contribution` step's
|
|
621
|
+
# `validations[].check: any_of, allowed_values: [<flag>]` target.
|
|
622
|
+
# Fragile against typos in `contributes_to:` — new storyboards MUST
|
|
623
|
+
# use the explicit form above. This fallback exists only so
|
|
624
|
+
# pre-#2633 storyboards keep running during migration.
|
|
625
|
+
#
|
|
626
|
+
# Cross-storyboard contradiction lint:
|
|
627
|
+
# The `lint:storyboard-contradictions` script groups every step-with-
|
|
628
|
+
# assertions across every storyboard by (task, canonicalized request
|
|
629
|
+
# fingerprint, prior-state fingerprint, env fingerprint) and flags
|
|
630
|
+
# groups whose asserted outcomes disagree in a way no conformant agent
|
|
631
|
+
# can satisfy (success ↔ error, or disjoint error-code sets).
|
|
632
|
+
#
|
|
633
|
+
# Env fingerprint includes the storyboard's top-level `id:`, so two
|
|
634
|
+
# separate storyboards exercising different controller-seeded states
|
|
635
|
+
# are legitimately independent test suites and do not collide. To make
|
|
636
|
+
# two different storyboards' assertions collide — e.g., to catch
|
|
637
|
+
# "storyboard A assumes media_buy_id X is active, storyboard B
|
|
638
|
+
# assumes it is canceled" — use matching `comply_scenario:` values or
|
|
639
|
+
# shared `prerequisites.controller_seeding`.
|
|
640
|
+
#
|
|
641
|
+
# Branch-set peers (two optional phases in the same storyboard sharing
|
|
642
|
+
# a `branch_set.id`) are exempt from contradiction flagging by design:
|
|
643
|
+
# any_of semantics intentionally asserts mutually exclusive outcomes.
|
|
644
|
+
#
|
|
645
|
+
# Mutating-task detection: the lint's prior-state fingerprint partitions
|
|
646
|
+
# steps by the ordered list of prior MUTATING tasks. A task is mutating
|
|
647
|
+
# iff its request schema declares `"x-mutates-state": true` at the top
|
|
648
|
+
# level. The annotation is consumed by
|
|
649
|
+
# `scripts/lint-storyboard-contradictions.cjs` (which builds the
|
|
650
|
+
# mutating-task set at lint time) and is the single source of truth for
|
|
651
|
+
# this concern.
|
|
652
|
+
#
|
|
653
|
+
# Decidability rule (apply to any new task):
|
|
654
|
+
# A task declares `"x-mutates-state": true` iff a conformant compliance
|
|
655
|
+
# suite could write an assertion — on any later `get_*`, `list_*`, or
|
|
656
|
+
# follow-up call — whose outcome depends on this request having run.
|
|
657
|
+
# Equivalently: "this task changes observable server state a later
|
|
658
|
+
# conformant call may assert against."
|
|
659
|
+
#
|
|
660
|
+
# Mutating task classes (illustrative; the decidability rule above is
|
|
661
|
+
# the actual test):
|
|
662
|
+
# - Writes (create/update/delete/sync/build) — resource lifecycle.
|
|
663
|
+
# - State transitions (cancel, approve, reject) — status moves.
|
|
664
|
+
# - Session-scoped primitives (si_initiate_session, si_send_message,
|
|
665
|
+
# si_terminate_session) — session lifecycle.
|
|
666
|
+
# - Reporting/audit writes (log_event, report_usage,
|
|
667
|
+
# report_plan_outcome, provide_performance_feedback) — mutating
|
|
668
|
+
# because `get_plan_audit_logs` and billing-summary tasks can assert
|
|
669
|
+
# against their recorded outputs.
|
|
670
|
+
#
|
|
671
|
+
# Non-mutating task classes:
|
|
672
|
+
# - Read-only tasks (get_*, list_*, check_*, validate_*, preview_*) do
|
|
673
|
+
# not declare `x-mutates-state` UNLESS the read itself produces
|
|
674
|
+
# observable state a later task can assert against (e.g., a view
|
|
675
|
+
# counter that `get_analytics` can read) — those are mutating
|
|
676
|
+
# regardless of prefix.
|
|
677
|
+
# - Pure-compute tasks (no state read or write — estimates,
|
|
678
|
+
# calculations) MUST NOT declare `x-mutates-state`.
|
|
679
|
+
#
|
|
680
|
+
# Relationship to idempotency_key (decoupled, do not unify):
|
|
681
|
+
# `x-mutates-state` declares mutation semantics. `required:
|
|
682
|
+
# [idempotency_key]` declares the idempotency mechanism. The sets
|
|
683
|
+
# overlap ~95% but legitimately diverge for naturally-idempotent
|
|
684
|
+
# mutations. Example: `comply_test_controller` declares
|
|
685
|
+
# `x-mutates-state: true` but omits `idempotency_key` from `required`
|
|
686
|
+
# because the `scenario` enum is the dedup boundary — replaying
|
|
687
|
+
# `force_media_buy_status=active` converges to the same observable
|
|
688
|
+
# state without a key. `si_terminate_session` is the same shape
|
|
689
|
+
# (session_id is the dedup boundary). `scripts/build-compliance.cjs`
|
|
690
|
+
# reads `idempotency_key` for its own enforcement; do not try to share
|
|
691
|
+
# the two reads.
|
|
692
|
+
#
|
|
693
|
+
# --- Validation ---
|
|
694
|
+
#
|
|
695
|
+
# check: string (what to validate: "response_schema", "field_present",
|
|
696
|
+
# "envelope_field_present" (walks protocol-envelope.json instead of
|
|
697
|
+
# response_schema_ref — use for top-level envelope fields like `status`),
|
|
698
|
+
# "field_value",
|
|
699
|
+
# "field_value_or_absent", "status_code", "http_status", "http_status_in",
|
|
700
|
+
# "error_code", "on_401_require_header", "resource_equals_agent_url",
|
|
701
|
+
# "any_of", "refs_resolve", "a2a_submitted_artifact")
|
|
702
|
+
# path: string (JSON path to the field, e.g., "formats[0].format_id")
|
|
703
|
+
# value: any (expected value — string, number, or boolean; required when check is "field_value";
|
|
704
|
+
# accepted (optional) when check is "field_value_or_absent")
|
|
705
|
+
# allowed_values: array (acceptable values for "field_value", "field_value_or_absent",
|
|
706
|
+
# "http_status_in", "error_code", "any_of")
|
|
707
|
+
# description: string (human-readable description of validation)
|
|
708
|
+
#
|
|
709
|
+
# field_value_or_absent check:
|
|
710
|
+
# Passes when the field is absent OR present and equal to `value` / contained in
|
|
711
|
+
# `allowed_values`; fails only when the field is present with a disallowed value.
|
|
712
|
+
# Use for fields that the spec permits to be omitted but MUST NOT carry a wrong value
|
|
713
|
+
# when present (e.g., a `replayed` flag that MAY be omitted on a fresh path but MUST
|
|
714
|
+
# NOT be true). Accepts both `value` (single expected value) and `allowed_values` (set
|
|
715
|
+
# membership); at least one of the two MUST be provided. A `field_value_or_absent` check
|
|
716
|
+
# that declares neither `value` nor `allowed_values` is a storyboard-load error; runners
|
|
717
|
+
# MUST reject the storyboard before execution begins.
|
|
718
|
+
# For fields that are required by the response schema, pair this check with a separate
|
|
719
|
+
# `check: field_present` to avoid silently accepting absent fields that are required.
|
|
720
|
+
#
|
|
721
|
+
# Error-shape validation — use `check: error_code`:
|
|
722
|
+
# The `error_code` check is shape-agnostic. The runner resolves the code from
|
|
723
|
+
# any of the transport-binding locations defined by the client detection
|
|
724
|
+
# order (see docs/building/implementation/transport-errors#client-detection-order):
|
|
725
|
+
# - `adcp_error.code` (MCP structuredContent, A2A artifact DataPart, JSON-RPC
|
|
726
|
+
# error.data, MCP text-fallback content JSON)
|
|
727
|
+
# - `errors[0].code` (task-payload errors array, top-level or under payload)
|
|
728
|
+
# A storyboard SHOULD assert error shape via `check: error_code` rather than
|
|
729
|
+
# `check: field_present, path: "errors"` or `path: "adcp_error"` — pinning to
|
|
730
|
+
# a specific shape makes the validator flakey against conformant agents that
|
|
731
|
+
# surface errors on the other layer.
|
|
732
|
+
#
|
|
733
|
+
# Error-code vocabulary:
|
|
734
|
+
# Every code referenced in a `value:` or `allowed_values:` under `check: error_code`
|
|
735
|
+
# MUST exist in the canonical enum at `static/schemas/source/enums/error-code.json`
|
|
736
|
+
# (or be registered as a deprecation alias). The `lint:error-codes` script
|
|
737
|
+
# enforces this at build time — references to undefined codes fail the build.
|
|
738
|
+
# Sellers MAY emit vendor-specific codes outside the enum, but storyboards
|
|
739
|
+
# MUST NOT assert on them (that couples the conformance suite to a specific
|
|
740
|
+
# vendor's taxonomy). When a test needs to accept multiple possible codes,
|
|
741
|
+
# use `allowed_values: [...]` rather than a single-code `value:` assertion.
|
|
742
|
+
#
|
|
743
|
+
# Cross-step integrity — use `check: refs_resolve`:
|
|
744
|
+
# `refs_resolve` asserts every ref in a source set resolves to a member of a
|
|
745
|
+
# target set, matched on declared keys. Use it when one step's response
|
|
746
|
+
# contains references (e.g., `format_ids` on `products`) that MUST exist in
|
|
747
|
+
# another step's response (e.g., `formats` from `list_creative_formats`).
|
|
748
|
+
# Without this primitive, broken references are silent until a later call
|
|
749
|
+
# (e.g., `sync_creatives`) fails at runtime, after commitments are already
|
|
750
|
+
# made.
|
|
751
|
+
#
|
|
752
|
+
# Fields:
|
|
753
|
+
# source:
|
|
754
|
+
# from: current_step | context
|
|
755
|
+
# # `current_step` reads the step's task-result data;
|
|
756
|
+
# # `context` reads values captured by prior steps.
|
|
757
|
+
# path: <string> # supports `[*]` wildcards — see below.
|
|
758
|
+
# target:
|
|
759
|
+
# from: current_step | context
|
|
760
|
+
# path: <string>
|
|
761
|
+
# match_keys: [<key>, ...] # keys compared on each ref — e.g. [agent_url, id].
|
|
762
|
+
# # A ref missing any declared key is NEVER a match
|
|
763
|
+
# # (agents that drop a key don't fuzzy-match others
|
|
764
|
+
# # that also dropped it).
|
|
765
|
+
# scope: # optional — restrict integrity to in-scope refs.
|
|
766
|
+
# key: <string> # e.g. `agent_url`.
|
|
767
|
+
# equals: <string> # literal value, or `$agent_url` for the
|
|
768
|
+
# # runner target URL. Keys ending in `url`
|
|
769
|
+
# # get trailing-slash / case normalization
|
|
770
|
+
# # on both sides before compare. Transport
|
|
771
|
+
# # path canonicalization (`/mcp`, well-known
|
|
772
|
+
# # A2A card path) for `$agent_url` is tracked
|
|
773
|
+
# # in adcp-client#710 — until it lands, MCP
|
|
774
|
+
# # agents should expect refs to fall
|
|
775
|
+
# # out-of-scope when format_ids name the
|
|
776
|
+
# # bare agent URL.
|
|
777
|
+
# on_out_of_scope: warn | ignore | fail
|
|
778
|
+
# # how refs outside `scope` are graded.
|
|
779
|
+
# # default `warn`: pass the check, attach
|
|
780
|
+
# # observations naming the skipped refs.
|
|
781
|
+
# # `ignore`: silent. `fail`: promote to
|
|
782
|
+
# # missing so reports name them.
|
|
783
|
+
#
|
|
784
|
+
# Path wildcards:
|
|
785
|
+
# `[*]` in a `source.path` or `target.path` flattens over arrays. So
|
|
786
|
+
# `products[*].format_ids[*]` walks every product and flattens every
|
|
787
|
+
# `format_ids` array into a single list of refs. Runners cap terminal
|
|
788
|
+
# fan-out at 10,000 values to bound malicious agent responses; paths
|
|
789
|
+
# realistic for any catalog size are well under the cap.
|
|
790
|
+
#
|
|
791
|
+
# Grading output:
|
|
792
|
+
# A failing `refs_resolve` check names the specific unresolved ref tuples
|
|
793
|
+
# in `actual.missing` (projected to `match_keys`). Duplicates are collapsed
|
|
794
|
+
# on the projected tuple so one broken ref across 50 products shows up
|
|
795
|
+
# once — compliance reports stay readable.
|
|
796
|
+
#
|
|
797
|
+
# Example — every `format_id` on products resolves to a format on this agent:
|
|
798
|
+
# - check: refs_resolve
|
|
799
|
+
# description: "Every format_id on products resolves to a format returned by list_creative_formats"
|
|
800
|
+
# source:
|
|
801
|
+
# from: context
|
|
802
|
+
# path: "products[*].format_ids[*]"
|
|
803
|
+
# target:
|
|
804
|
+
# from: current_step
|
|
805
|
+
# path: "formats[*].format_id"
|
|
806
|
+
# match_keys: [agent_url, id]
|
|
807
|
+
# scope:
|
|
808
|
+
# key: agent_url
|
|
809
|
+
# equals: $agent_url
|
|
810
|
+
# on_out_of_scope: warn
|
|
811
|
+
#
|
|
812
|
+
# A2A wire-shape — use `check: a2a_submitted_artifact`:
|
|
813
|
+
# Asserts the A2A envelope invariants for AdCP `submitted` arms: `Task.id` /
|
|
814
|
+
# `Task.contextId` non-empty, `Task.state` normalised to `completed` (A2A 0.3
|
|
815
|
+
# wire value `"completed"`; A2A 1.0 enum `"TASK_STATE_COMPLETED"`), and the
|
|
816
|
+
# last non-null, object-typed DataPart's `data.status === 'submitted'`
|
|
817
|
+
# preserving the AdCP discriminator. Grades `not_applicable` on non-A2A
|
|
818
|
+
# transports so storyboards can include it alongside MCP-shape assertions
|
|
819
|
+
# without forking by transport.
|
|
820
|
+
#
|
|
821
|
+
# --- Runner output ---
|
|
822
|
+
#
|
|
823
|
+
# How a runner MUST report validation results — including which fields a
|
|
824
|
+
# failing validation carries (exact request, response, JSON Pointer, expected
|
|
825
|
+
# vs. actual, schema $id, schema URL) and how skipped storyboards MUST
|
|
826
|
+
# distinguish not_applicable vs. no_phases vs. missing_tool — is defined in
|
|
827
|
+
# runner-output-contract.yaml. Storyboard authors SHOULD assume failures will
|
|
828
|
+
# be rendered with that detail and write descriptions accordingly.
|
|
829
|
+
#
|
|
830
|
+
# Runner grading codes introduced by the context accumulator and fixture
|
|
831
|
+
# seeding:
|
|
832
|
+
#
|
|
833
|
+
# capture_path_not_resolvable
|
|
834
|
+
# Emitted on the capturing step when a `context_outputs:` entry's
|
|
835
|
+
# `path` does not resolve against the step's response body. The step
|
|
836
|
+
# grades as failed — the capture declared a contract that the response
|
|
837
|
+
# did not meet. Distinct from unresolved_substitution so tooling can
|
|
838
|
+
# discriminate "producer contract breach" from "consumer missing state."
|
|
839
|
+
#
|
|
840
|
+
# unresolved_substitution
|
|
841
|
+
# Emitted on a consumer step when a referenced $context.<name> or
|
|
842
|
+
# {{prior_step.<id>.<field>}} value is not populated at step-execution
|
|
843
|
+
# time (either because the producer step failed, was skipped, or never
|
|
844
|
+
# ran). The step grades as failed. Also used at preflight when a
|
|
845
|
+
# substitution is statically unresolvable (e.g., {{runner.webhook_url:*}}
|
|
846
|
+
# on a run with no webhook receiver) to grade the entire storyboard
|
|
847
|
+
# not_applicable before execution.
|
|
848
|
+
#
|
|
849
|
+
# fixture_seed_unsupported
|
|
850
|
+
# Emitted when a storyboard's `prerequisites.controller_seeding` requires
|
|
851
|
+
# a seed_* scenario the seller does not implement (returned as
|
|
852
|
+
# UNKNOWN_SCENARIO). The storyboard grades not_applicable (coverage gap),
|
|
853
|
+
# not failed.
|
|
854
|
+
#
|
|
855
|
+
# unresolved_scenario_reference
|
|
856
|
+
# A detailed sub-reason under the canonical skip `reason:
|
|
857
|
+
# not_applicable`. Emitted when a parent storyboard's
|
|
858
|
+
# `requires_scenarios:` entry cannot be resolved against the source
|
|
859
|
+
# tree (no file declares that `id:`). Per the
|
|
860
|
+
# `detailed_reason_mapping` convention in runner-output-contract.yaml,
|
|
861
|
+
# runners MUST populate the canonical `reason: not_applicable` and
|
|
862
|
+
# encode `unresolved_scenario_reference` in `detail`. detail MUST
|
|
863
|
+
# follow the shape
|
|
864
|
+
# 'requires_scenarios reference "<scenario_id>" did not resolve
|
|
865
|
+
# against the source tree' — and when multiple references are
|
|
866
|
+
# unresolved, detail MUST enumerate every unresolved id with the
|
|
867
|
+
# same shape (comma-separated or newline-delimited) so downstream
|
|
868
|
+
# tooling can parse each. Runners MUST NOT silently pass the parent
|
|
869
|
+
# as if the missing scenario had contributed.
|
|
870
|
+
#
|
|
871
|
+
# Distinct from `fixture_seed_unsupported`: that reason indicates an
|
|
872
|
+
# agent-coverage gap (seller doesn't implement a seed scenario);
|
|
873
|
+
# `unresolved_scenario_reference` indicates a source-tree authoring
|
|
874
|
+
# bug that the build-time lint SHOULD have caught. Dashboards and
|
|
875
|
+
# summary reports SHOULD NOT conflate the two when aggregating
|
|
876
|
+
# `not_applicable` counts — they are categorically different
|
|
877
|
+
# signals.
|
|
878
|
+
#
|
|
879
|
+
# The `lint:storyboard-branch-sets` script surfaces this as a
|
|
880
|
+
# build-time error via the `unresolved_scenario_reference` rule so
|
|
881
|
+
# unresolved references in a well-linted corpus never reach a
|
|
882
|
+
# runner. The runner-side grading is defensive for corpora that
|
|
883
|
+
# bypass the lint or come from pre-lint CI states.
|
|
884
|
+
#
|
|
885
|
+
# --- Webhook receiver (optional, for outbound-webhook conformance) ---
|
|
886
|
+
#
|
|
887
|
+
# Storyboards that verify outbound webhook conformance (signing, idempotency_key
|
|
888
|
+
# presence, idempotency_key stability across retries) require the runner to host
|
|
889
|
+
# a webhook receiver during test execution. The receiver URL is injected into
|
|
890
|
+
# push_notification_config on steps that trigger webhooks, and a subsequent
|
|
891
|
+
# expect_webhook* step asserts on what arrived.
|
|
892
|
+
#
|
|
893
|
+
# Receiver behavior is specified by a test-kit contract referenced from the
|
|
894
|
+
# storyboard's prerequisites.test_kit field — see test-kits/webhook-receiver-runner.yaml
|
|
895
|
+
# for the reference contract (endpoint modes, retry-replay shape, client-primitive
|
|
896
|
+
# hooks). Storyboard authors MUST declare the contract in prerequisites; the
|
|
897
|
+
# webhook-emission universal (universal/webhook-emission.yaml) is the reference
|
|
898
|
+
# consumer, and universal/idempotency.yaml uses it for the "no duplicate webhooks
|
|
899
|
+
# on replay" assertion.
|
|
900
|
+
#
|
|
901
|
+
# --- Substitution variables (webhook-receiver enabled) ---
|
|
902
|
+
#
|
|
903
|
+
# When a storyboard's test-kit contract declares a webhook_receiver, the runner
|
|
904
|
+
# exposes these substitutions for use in sample_request payloads and expectations:
|
|
905
|
+
#
|
|
906
|
+
# {{runner.webhook_base}} → HTTPS base of the runner's receiver for this run
|
|
907
|
+
# {{runner.webhook_url:<step_id>}} → per-step URL; unique per step
|
|
908
|
+
#
|
|
909
|
+
# Per-step receivers are the only supported shape in v1 so each step's
|
|
910
|
+
# expect_webhook matches only its own deliveries. Fan-in scenarios are deferred
|
|
911
|
+
# (see "shared_receiver" note below).
|
|
912
|
+
#
|
|
913
|
+
# --- Webhook-assertion steps ---
|
|
914
|
+
#
|
|
915
|
+
# The following step `task` values are only valid when the storyboard's test-kit
|
|
916
|
+
# declares a webhook_receiver. They are scheduled alongside regular task steps
|
|
917
|
+
# and reference earlier steps by id.
|
|
918
|
+
#
|
|
919
|
+
# task: expect_webhook
|
|
920
|
+
# Wait up to `timeout_seconds` (default 30) for a webhook matching the filter,
|
|
921
|
+
# then validate. Runner delegates to the `@adcp/client` AsyncHandler; the webhook
|
|
922
|
+
# is observed via onActivity with type webhook_received, and WebhookMetadata
|
|
923
|
+
# (including idempotency_key) is provided by the client.
|
|
924
|
+
#
|
|
925
|
+
# Fields:
|
|
926
|
+
# triggered_by: <step_id> # earlier step whose task triggered this webhook
|
|
927
|
+
# filter: # match by payload fields
|
|
928
|
+
# operation_id: "{{prior_step.<id>.operation_id}}"
|
|
929
|
+
# status: "completed" # optional; match any status if omitted
|
|
930
|
+
# timeout_seconds: 30 # optional; fail if no match within window
|
|
931
|
+
# expect_idempotency_key: true # default true; assert the key is present and pattern-valid
|
|
932
|
+
# webhook_payload_schema_ref: <path>
|
|
933
|
+
# # optional — validate payload against the webhook schema
|
|
934
|
+
# # (distinct from step-level `schema_ref` and
|
|
935
|
+
# # `response_schema_ref`, which apply to the caller→agent
|
|
936
|
+
# # request/response. Webhook steps have no caller request,
|
|
937
|
+
# # so the payload schema is named explicitly to avoid
|
|
938
|
+
# # overload of schema_ref.)
|
|
939
|
+
# expect_max_deliveries_per_logical_event: 1
|
|
940
|
+
# # optional — if set, runner asserts at most N distinct
|
|
941
|
+
# # logical webhook events (grouped by idempotency_key)
|
|
942
|
+
# # arrive within the window. Used by idempotency storyboards
|
|
943
|
+
# # to catch duplicate-side-effect bugs where a seller
|
|
944
|
+
# # re-executes on replay with a fresh idempotency_key.
|
|
945
|
+
# requires_contract: <contract_id>
|
|
946
|
+
# # optional — step grades as not_applicable (not fail)
|
|
947
|
+
# # when the named test-kit contract is not in scope.
|
|
948
|
+
# # Used on cross-specialism assertions that depend on
|
|
949
|
+
# # webhook_receiver_runner being active.
|
|
950
|
+
#
|
|
951
|
+
# Error modes: no_webhook_received, schema_violation, missing_idempotency_key,
|
|
952
|
+
# invalid_idempotency_key_format, signature_invalid (when signature assertion on),
|
|
953
|
+
# duplicate_webhook_on_replay (when expect_max_deliveries_per_logical_event exceeded).
|
|
954
|
+
#
|
|
955
|
+
# task: expect_webhook_retry_keys_stable
|
|
956
|
+
# The runner's webhook receiver deterministically returns 5xx for the first
|
|
957
|
+
# `retry_trigger.count` deliveries of the matching webhook, then 2xx. The
|
|
958
|
+
# runner asserts that every delivery within the retry window carries the
|
|
959
|
+
# byte-identical idempotency_key (the sender regenerating a key on retry is a
|
|
960
|
+
# conformance failure).
|
|
961
|
+
#
|
|
962
|
+
# Signature verification on every delivery in the retry loop: when 9421 is in
|
|
963
|
+
# effect (i.e., push_notification_config was registered without `authentication`),
|
|
964
|
+
# the runner MUST verify the 9421 signature on EVERY delivery including retries
|
|
965
|
+
# and MUST reuse the run-scoped (keyid, nonce) replay store (see test-kit
|
|
966
|
+
# client_primitives.signature_replay_store). A publisher that stably reuses
|
|
967
|
+
# idempotency_key (correct) but also reuses the 9421 `nonce` sig-param
|
|
968
|
+
# (incorrect — nonce MUST be fresh per delivery) would pass retry-stability
|
|
969
|
+
# but MUST fail signature-replay dedup on the second delivery. Running the two
|
|
970
|
+
# checks together catches this class of bug; a retry-stability step that skips
|
|
971
|
+
# signature verification hides it.
|
|
972
|
+
#
|
|
973
|
+
# Fields:
|
|
974
|
+
# triggered_by: <step_id>
|
|
975
|
+
# filter: { ... } # same shape as expect_webhook
|
|
976
|
+
# retry_trigger:
|
|
977
|
+
# count: 3 # runner returns 5xx for the first N deliveries.
|
|
978
|
+
# # MUST be 1 ≤ count ≤ 10 (test-kit
|
|
979
|
+
# # retry_replay_contract.max_count). Counts outside
|
|
980
|
+
# # the range fail storyboard validation; sellers
|
|
981
|
+
# # legitimately exercise N=3 for back-off sanity,
|
|
982
|
+
# # higher counts risk becoming DoS amplifiers in
|
|
983
|
+
# # proxy_url mode.
|
|
984
|
+
# http_status: 503 # optional; default 503. MUST be in
|
|
985
|
+
# # {429, 500, 502, 503, 504} (test-kit
|
|
986
|
+
# # retry_replay_contract.allowed_statuses). Other
|
|
987
|
+
# # statuses are not retryable signals for
|
|
988
|
+
# # at-least-once senders and fail validation.
|
|
989
|
+
# timeout_seconds: 90 # longer default — must cover sender's retry back-off
|
|
990
|
+
# expect_min_deliveries: 2 # fail if fewer deliveries observed within window
|
|
991
|
+
# verify_signature_on_every_delivery: true
|
|
992
|
+
# # default true when 9421 is in effect. Set false
|
|
993
|
+
# # ONLY when the storyboard is explicitly registered
|
|
994
|
+
# # for legacy HMAC mode; in 9421 mode, silently
|
|
995
|
+
# # disabling retry-delivery signature verification
|
|
996
|
+
# # hides nonce-replay bugs.
|
|
997
|
+
#
|
|
998
|
+
# Error modes: insufficient_retries, idempotency_key_rotated,
|
|
999
|
+
# idempotency_key_format_changed, signature_replayed (nonce reused across
|
|
1000
|
+
# retries), signature_invalid (any retry delivery fails 9421).
|
|
1001
|
+
#
|
|
1002
|
+
# task: expect_webhook_signature_valid
|
|
1003
|
+
# Assert that the inbound webhook passed the 9421 webhook-signing verifier
|
|
1004
|
+
# checklist (see docs/building/implementation/security.mdx#verifier-checklist-for-webhooks).
|
|
1005
|
+
# Runner delegates verification to the `@adcp/client` 9421 webhook verifier;
|
|
1006
|
+
# this step is gated on the client library having that verifier available
|
|
1007
|
+
# (see test-kit contract). Negative conformance — signatures that MUST be
|
|
1008
|
+
# rejected with a specific webhook_signature_* code — is tested via static
|
|
1009
|
+
# conformance vectors under /compliance/{version}/test-vectors/webhook-signing/
|
|
1010
|
+
# when those land, not by this step type.
|
|
1011
|
+
#
|
|
1012
|
+
# Cross-step replay dedup: the runner MUST share a single (keyid, nonce) replay
|
|
1013
|
+
# store across ALL expect_webhook_signature_valid invocations (and all retry
|
|
1014
|
+
# deliveries under expect_webhook_retry_keys_stable) in a storyboard run — see
|
|
1015
|
+
# test-kit client_primitives.signature_replay_store. A per-step store would let
|
|
1016
|
+
# a publisher reuse the same (keyid, nonce) bytes at two expect_webhook_signature_valid
|
|
1017
|
+
# steps and pass both in isolation, silently missing cross-step replay.
|
|
1018
|
+
#
|
|
1019
|
+
# Fields:
|
|
1020
|
+
# triggered_by: <step_id>
|
|
1021
|
+
# filter: { ... } # same shape as expect_webhook
|
|
1022
|
+
# timeout_seconds: 30
|
|
1023
|
+
# require_tag: "adcp/webhook-signing/v1" # default; sanity check
|
|
1024
|
+
#
|
|
1025
|
+
# Error modes: signature_invalid, signature_expired, signature_key_unknown,
|
|
1026
|
+
# signature_key_purpose_invalid, signature_digest_mismatch, signature_tag_invalid,
|
|
1027
|
+
# signature_replayed.
|
|
1028
|
+
#
|
|
1029
|
+
# task: expect_substitution_safe
|
|
1030
|
+
# Assert that a creative preview (preview_html or preview_url) contains
|
|
1031
|
+
# substituted catalog-item macro values that are percent-encoded per RFC 3986
|
|
1032
|
+
# (unreserved-whitelist), per docs/creative/universal-macros#substitution-safety-catalog-item-macros.
|
|
1033
|
+
# Runner delegates HTML parsing, URL extraction, and the encoding check to
|
|
1034
|
+
# the `@adcp/client` SubstitutionObserver primitive (see test-kit
|
|
1035
|
+
# substitution-observer-runner.yaml). Catches implementations that pass raw
|
|
1036
|
+
# attacker bytes through substitution, including CRLF injection, bidi-override
|
|
1037
|
+
# spoofing, reserved-character URL breakout, and javascript:-scheme injection.
|
|
1038
|
+
#
|
|
1039
|
+
# Fields:
|
|
1040
|
+
# source: html_inline | url_fetch # default html_inline
|
|
1041
|
+
# source_path: "<JSON pointer into previous step's response>"
|
|
1042
|
+
# macro_template: "<URL template with {MACRO} placeholders>"
|
|
1043
|
+
# catalog_bindings:
|
|
1044
|
+
# - macro: "{SKU}"
|
|
1045
|
+
# catalog_item_id: "<item_id in the synced catalog>"
|
|
1046
|
+
# vector_name: "<fixture entry name, e.g. reserved-character-breakout>"
|
|
1047
|
+
# # Optional overrides for custom (non-canonical-fixture) vectors:
|
|
1048
|
+
# # raw_value: "<attacker-shaped input>"
|
|
1049
|
+
# # expected_encoded: "<RFC 3986 unreserved-encoded form>"
|
|
1050
|
+
# require_every_binding_observed: true # default true. Opt to false only
|
|
1051
|
+
# # when preview shows a partial
|
|
1052
|
+
# # catalog and partial observation
|
|
1053
|
+
# # is legitimate (document why in
|
|
1054
|
+
# # the narrative). The old field
|
|
1055
|
+
# # name require_all_bindings_observed
|
|
1056
|
+
# # is deprecated.
|
|
1057
|
+
# requires_contract: substitution_observer_runner
|
|
1058
|
+
#
|
|
1059
|
+
# Error modes: substitution_encoding_violation, nested_macro_re_expansion,
|
|
1060
|
+
# substitution_scheme_injection, substitution_binding_missing,
|
|
1061
|
+
# preview_source_unavailable, preview_url_unusable (with sub-reason:
|
|
1062
|
+
# http_status | content_type | size_exceeded | redirect_returned |
|
|
1063
|
+
# ssrf_blocked | fetch_timeout).
|
|
1064
|
+
#
|
|
1065
|
+
# --- shared_receiver (deferred / out of scope v1) ---
|
|
1066
|
+
#
|
|
1067
|
+
# Storyboards with multiple webhook-triggering steps MUST use per-step receiver
|
|
1068
|
+
# URLs; the default substitution `{{runner.webhook_url:<step_id>}}` gives each
|
|
1069
|
+
# step its own receiver so expect_webhook scopes cleanly. A `shared_receiver: true`
|
|
1070
|
+
# flag on an emitting step would route all emissions from that step to a shared
|
|
1071
|
+
# bucket (for fan-in dedup tests), but the mechanics — filter matching across
|
|
1072
|
+
# multiple emitters, retry-replay policy precedence, shared replay store scoping —
|
|
1073
|
+
# are underspecified. shared_receiver is OUT OF SCOPE for the first
|
|
1074
|
+
# webhook-emission universal release; storyboard authors MUST NOT set it. Future
|
|
1075
|
+
# fan-in storyboards will either flesh out the semantics or adopt a different
|
|
1076
|
+
# idiom (e.g., per-step receivers with cross-step assertions on aggregate
|
|
1077
|
+
# observations).
|
|
1078
|
+
#
|
|
1079
|
+
# --- Context accumulator and substitution ---
|
|
1080
|
+
#
|
|
1081
|
+
# The runner maintains a storyboard-run-scoped context accumulator populated from
|
|
1082
|
+
# three sources (evaluated in this precedence, high to low):
|
|
1083
|
+
#
|
|
1084
|
+
# 1. Step `context_outputs:` captures (populated after a step's validations pass)
|
|
1085
|
+
# 2. Storyboard-root `context:` block (literal values fixed at run start)
|
|
1086
|
+
# 3. Test-kit substitutions where explicitly scoped into context
|
|
1087
|
+
#
|
|
1088
|
+
# Two substitution syntaxes are supported in `sample_request` payloads:
|
|
1089
|
+
#
|
|
1090
|
+
# $context.<name> — single-token substitution. The runner replaces
|
|
1091
|
+
# the literal string "$context.<name>" with the
|
|
1092
|
+
# captured value. Example:
|
|
1093
|
+
# account_id: '$context.account_id'
|
|
1094
|
+
# resolves to the account_id captured from an
|
|
1095
|
+
# earlier step. Works inside scalar values and
|
|
1096
|
+
# inside array elements.
|
|
1097
|
+
#
|
|
1098
|
+
# {{prior_step.<id>.<field>}} — Mustache-style interpolation for webhook
|
|
1099
|
+
# filters, operation_id matching, and any other
|
|
1100
|
+
# cross-step reference. Resolves against the
|
|
1101
|
+
# named prior step's captured response fields.
|
|
1102
|
+
# Used by expect_webhook and expect_*_webhook_*
|
|
1103
|
+
# assertion steps (see Webhook-assertion steps
|
|
1104
|
+
# above) and by storyboards correlating an
|
|
1105
|
+
# async operation_id across steps.
|
|
1106
|
+
#
|
|
1107
|
+
# Runner requirements:
|
|
1108
|
+
# - Substitution runs at step-execution time, after resolving the test-kit
|
|
1109
|
+
# overlay and before applying auth. The agent under test receives the
|
|
1110
|
+
# substituted value — it MUST NOT see the literal `$context.foo` or `{{...}}`
|
|
1111
|
+
# token on the wire (see Unresolved substitution behavior below for the one
|
|
1112
|
+
# legitimate exception: preflight not_applicable grading when a substitution
|
|
1113
|
+
# is statically unresolvable).
|
|
1114
|
+
# - $context.<name> referencing an unpopulated name (the capturing step failed
|
|
1115
|
+
# or did not run) grades the referencing step as failed with
|
|
1116
|
+
# `unresolved_substitution`. The storyboard MUST NOT proceed with a fabricated
|
|
1117
|
+
# default — test outcomes on synthesized state are not signal.
|
|
1118
|
+
# - Captured values are typed as the JSON path resolved against the response;
|
|
1119
|
+
# numeric and object captures are preserved (they do not round-trip through
|
|
1120
|
+
# string coercion).
|
|
1121
|
+
# - When the per-task request builder discards body fields and
|
|
1122
|
+
# `$context.<name>` inside `sample_request` never reaches the wire (the
|
|
1123
|
+
# observable symptom is the agent answering with an unfiltered result
|
|
1124
|
+
# and the round-trip assertion grading on fabricated state), use
|
|
1125
|
+
# `context_inputs:` as the post-builder injection path. See the
|
|
1126
|
+
# `context_inputs:` field definition above for the full semantics and
|
|
1127
|
+
# when it is acceptable to reach for.
|
|
1128
|
+
#
|
|
1129
|
+
# --- Context echo contract (agent-facing) ---
|
|
1130
|
+
#
|
|
1131
|
+
# Agents under test MUST obey the following when a storyboard sends a `context:`
|
|
1132
|
+
# block on its sample_request:
|
|
1133
|
+
#
|
|
1134
|
+
# 1. Successful responses MUST echo the `context` object verbatim. The agent
|
|
1135
|
+
# MUST NOT mutate, filter, or reorder fields. The echo applies whether the
|
|
1136
|
+
# response is synchronous (`completed`) or asynchronous (`submitted` /
|
|
1137
|
+
# `working`).
|
|
1138
|
+
# 2. Error responses MUST also echo the `context` object verbatim. Context is
|
|
1139
|
+
# used as a correlation surface; dropping it on failure prevents buyers from
|
|
1140
|
+
# correlating errors with the originating call.
|
|
1141
|
+
# 3. Agents MUST NOT synthesize a `context` object when the caller did not
|
|
1142
|
+
# send one. A storyboard whose `sample_request` lacks `context:` MUST see
|
|
1143
|
+
# the response lack `context:` — fabricating `{ correlation_id: "..." }` on
|
|
1144
|
+
# the agent side is a conformance failure because the storyboard validator
|
|
1145
|
+
# is asserting on what-the-caller-sent, not what-the-agent-invented.
|
|
1146
|
+
# 4. Agents MUST NOT parse or act on `context` contents — see
|
|
1147
|
+
# docs/building/integration/context-sessions.mdx for the normative
|
|
1148
|
+
# agent-facing rules. The storyboard runner's validators rely on verbatim
|
|
1149
|
+
# echo and will fail any mutation.
|
|
1150
|
+
#
|
|
1151
|
+
# Runners MUST NOT auto-inject an implicit `context:` block on sample_request
|
|
1152
|
+
# payloads the storyboard did not declare. Storyboards that want to assert on
|
|
1153
|
+
# `context.correlation_id` MUST declare the context block explicitly on each
|
|
1154
|
+
# sample_request. This keeps the sample_request an honest shape of what the
|
|
1155
|
+
# agent will receive, and prevents an agent from passing only because the
|
|
1156
|
+
# runner compensated for its missing contract.
|
|
1157
|
+
#
|
|
1158
|
+
# --- Unresolved substitution behavior ---
|
|
1159
|
+
#
|
|
1160
|
+
# When a storyboard references a substitution variable the runner cannot
|
|
1161
|
+
# resolve — most commonly {{runner.webhook_base}} or {{runner.webhook_url:<id>}}
|
|
1162
|
+
# on a run with no webhook receiver configured — the runner MUST NOT ship the
|
|
1163
|
+
# literal `{{...}}` token to the agent under test. Shipping unresolved
|
|
1164
|
+
# substitutions is a conformance bug on the runner side, not a grading signal
|
|
1165
|
+
# about the agent.
|
|
1166
|
+
#
|
|
1167
|
+
# Runners MUST resolve substitutions at one of two preflight points:
|
|
1168
|
+
# 1. Before the storyboard run starts — if any step references an unresolvable
|
|
1169
|
+
# substitution, grade the storyboard as not_applicable with
|
|
1170
|
+
# `unresolved_substitution` as the reason, and skip execution entirely.
|
|
1171
|
+
# This is the preferred behavior for absent webhook receivers.
|
|
1172
|
+
# 2. Before individual step execution — if a later-computed substitution
|
|
1173
|
+
# (e.g., {{prior_step.<id>.operation_id}}) fails to resolve, grade the
|
|
1174
|
+
# step as failed with `unresolved_substitution`.
|
|
1175
|
+
#
|
|
1176
|
+
# Neither path may ship a literal `{{...}}` token on the wire.
|