@adcp/sdk 6.9.0 → 6.11.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.
Files changed (261) hide show
  1. package/bin/adcp.js +285 -5
  2. package/compliance/cache/3.0.6.previous/domains/brand/index.yaml +163 -0
  3. package/compliance/cache/3.0.6.previous/domains/creative/index.yaml +412 -0
  4. package/compliance/cache/3.0.6.previous/domains/governance/index.yaml +683 -0
  5. package/compliance/cache/3.0.6.previous/domains/media-buy/creative-reception.yaml +247 -0
  6. package/compliance/cache/3.0.6.previous/domains/media-buy/index.yaml +769 -0
  7. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/create_media_buy_async.yaml +232 -0
  8. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/creative_fate_after_cancellation.yaml +414 -0
  9. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/delivery_reporting.yaml +205 -0
  10. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/governance_approved.yaml +211 -0
  11. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/governance_conditions.yaml +196 -0
  12. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/governance_denied.yaml +192 -0
  13. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/governance_denied_recovery.yaml +244 -0
  14. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/invalid_transitions.yaml +284 -0
  15. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/inventory_list_no_match.yaml +143 -0
  16. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/inventory_list_targeting.yaml +271 -0
  17. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/measurement_terms_rejected.yaml +195 -0
  18. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/pending_creatives_to_start.yaml +250 -0
  19. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/proposal_finalize.yaml +243 -0
  20. package/compliance/cache/3.0.6.previous/domains/media-buy/scenarios/refine_products.yaml +148 -0
  21. package/compliance/cache/3.0.6.previous/domains/media-buy/state-machine.yaml +442 -0
  22. package/compliance/cache/3.0.6.previous/domains/signals/index.yaml +266 -0
  23. package/compliance/cache/3.0.6.previous/domains/sponsored-intelligence/index.yaml +256 -0
  24. package/compliance/cache/3.0.6.previous/index.json +324 -0
  25. package/compliance/cache/3.0.6.previous/protocols/brand/index.yaml +163 -0
  26. package/compliance/cache/3.0.6.previous/protocols/creative/index.yaml +412 -0
  27. package/compliance/cache/3.0.6.previous/protocols/governance/index.yaml +683 -0
  28. package/compliance/cache/3.0.6.previous/protocols/media-buy/creative-reception.yaml +247 -0
  29. package/compliance/cache/3.0.6.previous/protocols/media-buy/index.yaml +769 -0
  30. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/create_media_buy_async.yaml +232 -0
  31. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/creative_fate_after_cancellation.yaml +414 -0
  32. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/delivery_reporting.yaml +205 -0
  33. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/governance_approved.yaml +211 -0
  34. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/governance_conditions.yaml +196 -0
  35. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/governance_denied.yaml +192 -0
  36. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/governance_denied_recovery.yaml +244 -0
  37. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/invalid_transitions.yaml +284 -0
  38. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/inventory_list_no_match.yaml +143 -0
  39. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/inventory_list_targeting.yaml +271 -0
  40. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/measurement_terms_rejected.yaml +195 -0
  41. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/pending_creatives_to_start.yaml +250 -0
  42. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/proposal_finalize.yaml +243 -0
  43. package/compliance/cache/3.0.6.previous/protocols/media-buy/scenarios/refine_products.yaml +148 -0
  44. package/compliance/cache/3.0.6.previous/protocols/media-buy/state-machine.yaml +442 -0
  45. package/compliance/cache/3.0.6.previous/protocols/signals/index.yaml +266 -0
  46. package/compliance/cache/3.0.6.previous/protocols/sponsored-intelligence/index.yaml +256 -0
  47. package/compliance/cache/3.0.6.previous/specialisms/audience-sync/index.yaml +280 -0
  48. package/compliance/cache/3.0.6.previous/specialisms/brand-rights/index.yaml +350 -0
  49. package/compliance/cache/3.0.6.previous/specialisms/brand-rights/scenarios/governance_denied.yaml +204 -0
  50. package/compliance/cache/3.0.6.previous/specialisms/collection-lists/index.yaml +359 -0
  51. package/compliance/cache/3.0.6.previous/specialisms/content-standards/index.yaml +572 -0
  52. package/compliance/cache/3.0.6.previous/specialisms/creative-ad-server/index.yaml +383 -0
  53. package/compliance/cache/3.0.6.previous/specialisms/creative-generative/generative-seller.yaml +758 -0
  54. package/compliance/cache/3.0.6.previous/specialisms/creative-generative/index.yaml +746 -0
  55. package/compliance/cache/3.0.6.previous/specialisms/creative-template/index.yaml +413 -0
  56. package/compliance/cache/3.0.6.previous/specialisms/governance-aware-seller/index.yaml +136 -0
  57. package/compliance/cache/3.0.6.previous/specialisms/governance-delivery-monitor/index.yaml +441 -0
  58. package/compliance/cache/3.0.6.previous/specialisms/governance-spend-authority/denied.yaml +221 -0
  59. package/compliance/cache/3.0.6.previous/specialisms/governance-spend-authority/index.yaml +330 -0
  60. package/compliance/cache/3.0.6.previous/specialisms/property-lists/index.yaml +482 -0
  61. package/compliance/cache/3.0.6.previous/specialisms/sales-broadcast-tv/index.yaml +689 -0
  62. package/compliance/cache/3.0.6.previous/specialisms/sales-catalog-driven/index.yaml +779 -0
  63. package/compliance/cache/3.0.6.previous/specialisms/sales-guaranteed/index.yaml +504 -0
  64. package/compliance/cache/3.0.6.previous/specialisms/sales-non-guaranteed/index.yaml +428 -0
  65. package/compliance/cache/3.0.6.previous/specialisms/sales-proposal-mode/index.yaml +520 -0
  66. package/compliance/cache/3.0.6.previous/specialisms/sales-social/index.yaml +584 -0
  67. package/compliance/cache/3.0.6.previous/specialisms/signal-marketplace/index.yaml +415 -0
  68. package/compliance/cache/3.0.6.previous/specialisms/signal-marketplace/scenarios/governance_denied.yaml +207 -0
  69. package/compliance/cache/3.0.6.previous/specialisms/signal-owned/index.yaml +316 -0
  70. package/compliance/cache/3.0.6.previous/test-kits/acme-outdoor.yaml +210 -0
  71. package/compliance/cache/3.0.6.previous/test-kits/bistro-oranje.yaml +126 -0
  72. package/compliance/cache/3.0.6.previous/test-kits/nova-motors.yaml +262 -0
  73. package/compliance/cache/3.0.6.previous/test-kits/osei-natural.yaml +126 -0
  74. package/compliance/cache/3.0.6.previous/test-kits/signed-requests-runner.yaml +155 -0
  75. package/compliance/cache/3.0.6.previous/test-kits/substitution-observer-runner.yaml +690 -0
  76. package/compliance/cache/3.0.6.previous/test-kits/summit-foods.yaml +125 -0
  77. package/compliance/cache/3.0.6.previous/test-kits/webhook-receiver-runner.yaml +265 -0
  78. package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/001-minimal-plan.json +43 -0
  79. package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/002-full-plan.json +217 -0
  80. package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/003-bookkeeping-stripped.json +60 -0
  81. package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/004a-human-review-omitted.json +43 -0
  82. package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/004b-human-review-explicit-null.json +49 -0
  83. package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/005a-policy-categories-order-1.json +53 -0
  84. package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/005b-policy-categories-order-2.json +57 -0
  85. package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/006a-ext-trace-v1.json +49 -0
  86. package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/006b-ext-trace-v2.json +53 -0
  87. package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/007-unicode-objectives.json +43 -0
  88. package/compliance/cache/3.0.6.previous/test-vectors/plan-hash/008-numeric-canonicalization.json +65 -0
  89. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/README.md +219 -0
  90. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/canonicalization.json +241 -0
  91. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/keys.json +60 -0
  92. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/001-no-signature-header.json +24 -0
  93. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/002-wrong-tag.json +26 -0
  94. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/003-expired-signature.json +26 -0
  95. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/004-window-too-long.json +26 -0
  96. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/005-alg-not-allowed.json +26 -0
  97. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/006-missing-covered-component.json +26 -0
  98. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/007-missing-content-digest.json +26 -0
  99. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/008-unknown-keyid.json +26 -0
  100. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/009-key-ops-missing-verify.json +27 -0
  101. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/010-content-digest-mismatch.json +33 -0
  102. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/011-malformed-header.json +27 -0
  103. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/012-missing-expires-param.json +26 -0
  104. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/013-expires-le-created.json +27 -0
  105. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/014-missing-nonce-param.json +27 -0
  106. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/015-signature-invalid.json +28 -0
  107. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/016-replayed-nonce.json +35 -0
  108. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/017-key-revoked.json +38 -0
  109. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/018-digest-covered-when-forbidden.json +28 -0
  110. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/019-signature-without-signature-input.json +26 -0
  111. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/020-rate-abuse.json +34 -0
  112. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/021-duplicate-signature-input-label.json +31 -0
  113. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/022-multi-valued-content-type.json +31 -0
  114. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/023-multi-valued-content-digest.json +32 -0
  115. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/024-unquoted-string-param.json +31 -0
  116. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/025-jwk-alg-crv-mismatch.json +43 -0
  117. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/026-non-ascii-host.json +31 -0
  118. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/negative/027-webhook-registration-authentication-unsigned.json +25 -0
  119. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/001-basic-post.json +30 -0
  120. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/002-post-with-content-digest.json +31 -0
  121. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/003-es256-post.json +30 -0
  122. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/004-multiple-signature-labels.json +26 -0
  123. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/005-default-port-stripped.json +30 -0
  124. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/006-dot-segment-path.json +30 -0
  125. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/007-query-byte-preserved.json +30 -0
  126. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/008-percent-encoded-path.json +30 -0
  127. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/009-percent-encoded-unreserved-decoded.json +30 -0
  128. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/010-percent-encoded-slash-preserved.json +30 -0
  129. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/011-ipv6-authority.json +30 -0
  130. package/compliance/cache/3.0.6.previous/test-vectors/request-signing/positive/012-ipv6-authority-default-port-stripped.json +30 -0
  131. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/README.md +211 -0
  132. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/keys.json +61 -0
  133. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/001-wrong-tag.json +26 -0
  134. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/002-expired-signature.json +26 -0
  135. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/003-window-too-long.json +26 -0
  136. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/004-alg-not-allowed.json +26 -0
  137. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/005-missing-authority-component.json +26 -0
  138. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/006-missing-content-digest.json +25 -0
  139. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/007-unknown-keyid.json +26 -0
  140. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/008-wrong-adcp-use.json +26 -0
  141. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/009-content-digest-mismatch.json +26 -0
  142. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/010-malformed-signature-input.json +26 -0
  143. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/011-signature-without-input.json +25 -0
  144. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/012-missing-expires-param.json +26 -0
  145. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/013-expires-le-created.json +26 -0
  146. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/014-missing-nonce-param.json +26 -0
  147. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/015-signature-invalid.json +26 -0
  148. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/016-replayed-nonce.json +37 -0
  149. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/017-key-revoked.json +32 -0
  150. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/018-rate-abuse.json +33 -0
  151. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/019-revocation-stale.json +32 -0
  152. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/020-key-ops-missing-verify.json +41 -0
  153. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/negative/021-base64-alphabet-mixing.json +26 -0
  154. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/001-basic-post.json +24 -0
  155. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/002-es256-post.json +24 -0
  156. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/003-multiple-signature-labels.json +24 -0
  157. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/004-default-port-stripped.json +24 -0
  158. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/005-percent-encoded-path.json +24 -0
  159. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/006-query-byte-preserved.json +24 -0
  160. package/compliance/cache/3.0.6.previous/test-vectors/webhook-signing/positive/007-body-without-idempotency-key.json +25 -0
  161. package/compliance/cache/3.0.6.previous/universal/capability-discovery.yaml +125 -0
  162. package/compliance/cache/3.0.6.previous/universal/collection-lists-pagination-integrity.yaml +306 -0
  163. package/compliance/cache/3.0.6.previous/universal/content-standards-pagination-integrity.yaml +326 -0
  164. package/compliance/cache/3.0.6.previous/universal/deterministic-testing.yaml +1343 -0
  165. package/compliance/cache/3.0.6.previous/universal/error-compliance.yaml +474 -0
  166. package/compliance/cache/3.0.6.previous/universal/fictional-entities.yaml +307 -0
  167. package/compliance/cache/3.0.6.previous/universal/get-media-buys-pagination-integrity.yaml +160 -0
  168. package/compliance/cache/3.0.6.previous/universal/get-signals-pagination-integrity.yaml +211 -0
  169. package/compliance/cache/3.0.6.previous/universal/idempotency.yaml +593 -0
  170. package/compliance/cache/3.0.6.previous/universal/pagination-integrity-creative-formats.yaml +258 -0
  171. package/compliance/cache/3.0.6.previous/universal/pagination-integrity-list-accounts.yaml +262 -0
  172. package/compliance/cache/3.0.6.previous/universal/pagination-integrity.yaml +263 -0
  173. package/compliance/cache/3.0.6.previous/universal/property-lists-pagination-integrity.yaml +307 -0
  174. package/compliance/cache/3.0.6.previous/universal/runner-output-contract.yaml +358 -0
  175. package/compliance/cache/3.0.6.previous/universal/schema-validation.yaml +526 -0
  176. package/compliance/cache/3.0.6.previous/universal/security.yaml +431 -0
  177. package/compliance/cache/3.0.6.previous/universal/signed-requests.yaml +205 -0
  178. package/compliance/cache/3.0.6.previous/universal/storyboard-schema.yaml +1176 -0
  179. package/compliance/cache/3.0.6.previous/universal/v3-envelope-integrity.yaml +106 -0
  180. package/compliance/cache/3.0.6.previous/universal/webhook-emission.yaml +337 -0
  181. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  182. package/dist/lib/server/create-adcp-server.d.ts +33 -0
  183. package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
  184. package/dist/lib/server/create-adcp-server.js +127 -1
  185. package/dist/lib/server/create-adcp-server.js.map +1 -1
  186. package/dist/lib/server/credential-policy.d.ts +221 -0
  187. package/dist/lib/server/credential-policy.d.ts.map +1 -0
  188. package/dist/lib/server/credential-policy.js +260 -0
  189. package/dist/lib/server/credential-policy.js.map +1 -0
  190. package/dist/lib/server/decisioning/async-outcome.d.ts +17 -0
  191. package/dist/lib/server/decisioning/async-outcome.d.ts.map +1 -1
  192. package/dist/lib/server/decisioning/async-outcome.js +23 -18
  193. package/dist/lib/server/decisioning/async-outcome.js.map +1 -1
  194. package/dist/lib/server/decisioning/context.d.ts +8 -2
  195. package/dist/lib/server/decisioning/context.d.ts.map +1 -1
  196. package/dist/lib/server/decisioning/index.d.ts +1 -0
  197. package/dist/lib/server/decisioning/index.d.ts.map +1 -1
  198. package/dist/lib/server/decisioning/index.js.map +1 -1
  199. package/dist/lib/server/decisioning/runtime/from-platform.js +6 -4
  200. package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
  201. package/dist/lib/server/decisioning/runtime/postgres-task-registry.d.ts.map +1 -1
  202. package/dist/lib/server/decisioning/runtime/postgres-task-registry.js +5 -2
  203. package/dist/lib/server/decisioning/runtime/postgres-task-registry.js.map +1 -1
  204. package/dist/lib/server/decisioning/runtime/task-registry.d.ts +5 -0
  205. package/dist/lib/server/decisioning/runtime/task-registry.d.ts.map +1 -1
  206. package/dist/lib/server/decisioning/runtime/task-registry.js +4 -1
  207. package/dist/lib/server/decisioning/runtime/task-registry.js.map +1 -1
  208. package/dist/lib/server/decisioning/runtime/to-context.d.ts.map +1 -1
  209. package/dist/lib/server/decisioning/runtime/to-context.js +10 -2
  210. package/dist/lib/server/decisioning/runtime/to-context.js.map +1 -1
  211. package/dist/lib/server/dynamic-registry.d.ts +219 -0
  212. package/dist/lib/server/dynamic-registry.d.ts.map +1 -0
  213. package/dist/lib/server/dynamic-registry.js +245 -0
  214. package/dist/lib/server/dynamic-registry.js.map +1 -0
  215. package/dist/lib/server/index.d.ts +8 -0
  216. package/dist/lib/server/index.d.ts.map +1 -1
  217. package/dist/lib/server/index.js +15 -4
  218. package/dist/lib/server/index.js.map +1 -1
  219. package/dist/lib/server/operational-platform.d.ts +239 -0
  220. package/dist/lib/server/operational-platform.d.ts.map +1 -0
  221. package/dist/lib/server/operational-platform.js +94 -0
  222. package/dist/lib/server/operational-platform.js.map +1 -0
  223. package/dist/lib/server/test-controller.d.ts +2 -0
  224. package/dist/lib/server/test-controller.d.ts.map +1 -1
  225. package/dist/lib/server/test-controller.js +6 -11
  226. package/dist/lib/server/test-controller.js.map +1 -1
  227. package/dist/lib/server/wire-safe.d.ts +211 -0
  228. package/dist/lib/server/wire-safe.d.ts.map +1 -0
  229. package/dist/lib/server/wire-safe.js +231 -0
  230. package/dist/lib/server/wire-safe.js.map +1 -0
  231. package/dist/lib/server/wire-spec-fields.generated.d.ts +168 -0
  232. package/dist/lib/server/wire-spec-fields.generated.d.ts.map +1 -0
  233. package/dist/lib/server/wire-spec-fields.generated.js +172 -0
  234. package/dist/lib/server/wire-spec-fields.generated.js.map +1 -0
  235. package/dist/lib/testing/compliance/index.d.ts +2 -0
  236. package/dist/lib/testing/compliance/index.d.ts.map +1 -1
  237. package/dist/lib/testing/compliance/index.js +6 -1
  238. package/dist/lib/testing/compliance/index.js.map +1 -1
  239. package/dist/lib/testing/compliance/summary.d.ts +77 -0
  240. package/dist/lib/testing/compliance/summary.d.ts.map +1 -0
  241. package/dist/lib/testing/compliance/summary.js +176 -0
  242. package/dist/lib/testing/compliance/summary.js.map +1 -0
  243. package/dist/lib/testing/comply-controller.d.ts +2 -0
  244. package/dist/lib/testing/comply-controller.d.ts.map +1 -1
  245. package/dist/lib/testing/comply-controller.js.map +1 -1
  246. package/dist/lib/testing/storyboard/compliance.d.ts +26 -0
  247. package/dist/lib/testing/storyboard/compliance.d.ts.map +1 -1
  248. package/dist/lib/testing/storyboard/compliance.js +51 -0
  249. package/dist/lib/testing/storyboard/compliance.js.map +1 -1
  250. package/dist/lib/testing/storyboard/index.d.ts +2 -2
  251. package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
  252. package/dist/lib/testing/storyboard/index.js +4 -2
  253. package/dist/lib/testing/storyboard/index.js.map +1 -1
  254. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  255. package/dist/lib/testing/storyboard/runner.js +58 -5
  256. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  257. package/dist/lib/version.d.ts +3 -3
  258. package/dist/lib/version.d.ts.map +1 -1
  259. package/dist/lib/version.js +3 -3
  260. package/dist/lib/version.js.map +1 -1
  261. 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.