@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,593 @@
1
+ id: idempotency
2
+ version: "1.0.0"
3
+ title: "Idempotency enforcement"
4
+ category: core
5
+ summary: "Validates that mutating requests enforce idempotency_key — replays return cached responses, key reuse with a different payload returns IDEMPOTENCY_CONFLICT, and fresh keys create new resources."
6
+ track: core
7
+
8
+ # Cross-step assertions (adcontextprotocol/adcp#2639). These convert two
9
+ # reviewer-only checks from `key_reuse_conflict` and `security` into
10
+ # programmatic gates that fail the storyboard at runtime rather than
11
+ # waiting on human review. `idempotency.conflict_no_payload_leak` catches
12
+ # the stolen-key read oracle the key-reuse phase calls out; `context.no_secret_echo`
13
+ # catches credential echo on any step's response. Both ship as default
14
+ # assertions in `@adcp/client` 5.9+ — `import '@adcp/client/testing'`
15
+ # auto-registers them; no additional module loading required. Consumers
16
+ # who need stricter per-repo checks can re-register with their own spec
17
+ # via `registerAssertion(spec, { override: true })` (adcp-client#752).
18
+ invariants:
19
+ - idempotency.conflict_no_payload_leak
20
+ - context.no_secret_echo
21
+
22
+ # Security note for the test harness: sample_request values below use
23
+ # $generate:uuid_v4 and $generate:uuid_v4#alias placeholders. The harness
24
+ # MUST replace these with fresh UUID v4 values PER RUN (not per file, not
25
+ # per deploy). Hardcoding keys across runs creates a cross-tenant replay
26
+ # oracle — a malicious reviewer could probe the cached responses of the
27
+ # compliance principal using the same keys. Multiple references to the
28
+ # same alias (e.g. $generate:uuid_v4#replay_a) in the same run MUST
29
+ # resolve to the same UUID so the replay phase can use it intentionally.
30
+ #
31
+ # The compliance principal SHOULD be a sandbox account whose idempotency
32
+ # cache is isolated per test run (prefix-scoped, or purged on start).
33
+
34
+ narrative: |
35
+ Every mutating request in AdCP carries an idempotency_key so buyers can safely retry
36
+ after network errors without double-booking. This storyboard walks through the four
37
+ observable behaviors a seller MUST implement:
38
+
39
+ 1. First call with a fresh key is processed normally and returns the canonical response.
40
+ 2. Replay with the same key and an equivalent payload returns the cached response
41
+ without re-executing side effects (same media_buy_id, no new webhooks fired).
42
+ 3. Replay with the same key but a materially different payload is rejected with
43
+ IDEMPOTENCY_CONFLICT. The buyer has clearly reused a key by mistake.
44
+ 4. A request with a different key is treated as a new request, even if the payload
45
+ is identical — the key is what makes retries safe, not payload equivalence.
46
+ 5. Error responses (returned envelopes, thrown envelopes, and uncaught exceptions)
47
+ do not cache. The next request carrying the same key re-executes the handler.
48
+ This applies to every recovery class, including terminal — terminal states in
49
+ AdCP are mostly state-dependent (e.g., ACCOUNT_SUSPENDED flips after buyer
50
+ remediation) and cached error replay would mask legitimate recovery. Handler
51
+ authors must mutate state last: a handler that writes state then returns or
52
+ throws an error envelope will double-write on retry. See
53
+ security.mdx#idempotency rule 3 ("Only successful responses are cached"),
54
+ which this storyboard validates structurally via the reviewer check on
55
+ key_reuse_conflict. An end-to-end phase that drives a deterministic terminal
56
+ error and replays the key is deferred pending a generic force-error controller
57
+ verb (see adcontextprotocol/adcp#2760).
58
+
59
+ Missing idempotency_key on any mutating request MUST be rejected with INVALID_REQUEST.
60
+ Sellers MUST declare adcp.idempotency on get_adcp_capabilities — either
61
+ { supported: true, replay_ttl_seconds: N } to opt into replay protection, or
62
+ { supported: false } to declare they do not deduplicate retries. This storyboard
63
+ validates the supported: true behavior; sellers declaring supported: false
64
+ MUST skip this storyboard (they have no replay window to test).
65
+
66
+ **Cache-growth defense (MUST, not yet runtime-graded).** Per
67
+ security.mdx bullet 8 on the idempotency section, sellers MUST apply per-agent
68
+ rate limits on idempotency-cache inserts and MUST return RATE_LIMITED when the
69
+ per-agent insert rate exceeds the configured ceiling. The recommended
70
+ first-deployment ceiling is 60 inserts/sec sustained per agent (3,600/min)
71
+ with burst allowance to 300/sec over rolling 10-second windows. This storyboard
72
+ does not yet drive the high-volume burst that would exercise the ceiling —
73
+ runtime grading is tracked as a follow-up once a burst-runner test-kit
74
+ contract is defined. Sellers SHOULD self-attest to this requirement until the
75
+ runtime phase lands.
76
+
77
+ agent:
78
+ interaction_model: media_buy_seller
79
+ capabilities:
80
+ - sells_media
81
+ examples:
82
+ - "Any AdCP seller agent"
83
+
84
+ caller:
85
+ role: buyer_agent
86
+ example: "Compliance test harness"
87
+
88
+ prerequisites:
89
+ description: |
90
+ No special prerequisites. The storyboard uses create_media_buy as the canonical
91
+ mutating request. Sellers that do not support create_media_buy SHOULD still pass
92
+ idempotency compliance on whichever mutating task they do implement.
93
+ test_kit: "test-kits/acme-outdoor.yaml"
94
+
95
+ phases:
96
+ - id: capability_discovery
97
+ title: "Capability discovery"
98
+ narrative: |
99
+ Confirm the agent supports media buying and check whether it declares an
100
+ explicit replay-window TTL.
101
+
102
+ steps:
103
+ - id: get_capabilities
104
+ title: "Check idempotency capability declaration"
105
+ narrative: |
106
+ Call get_adcp_capabilities and verify adcp.idempotency is declared with
107
+ supported: true and a replay_ttl_seconds value. The block is REQUIRED —
108
+ clients MUST NOT fall back to an assumed default, and a seller that omits
109
+ it is non-compliant. Sellers running this storyboard must declare
110
+ supported: true; supported: false sellers skip this storyboard entirely.
111
+ task: get_adcp_capabilities
112
+ schema_ref: "protocol/get-adcp-capabilities-request.json"
113
+ response_schema_ref: "protocol/get-adcp-capabilities-response.json"
114
+ doc_ref: "/protocol/get_adcp_capabilities"
115
+ comply_scenario: capability_discovery
116
+ stateful: false
117
+ expected: |
118
+ Return capabilities declaring media_buy in supported_protocols AND
119
+ adcp.idempotency with supported: true and replay_ttl_seconds (minimum
120
+ 3600s; recommended 86400s or longer).
121
+ sample_request:
122
+ context:
123
+ correlation_id: "idempotency--get_capabilities"
124
+ validations:
125
+ - check: response_schema
126
+ description: "Response matches get-adcp-capabilities-response.json schema"
127
+ - check: field_present
128
+ path: "supported_protocols"
129
+ description: "Agent declares supported protocols"
130
+ - check: field_value
131
+ path: "adcp.idempotency.supported"
132
+ value: true
133
+ description: "Agent declares supported: true for this storyboard (supported: false sellers skip)"
134
+ - check: field_present
135
+ path: "adcp.idempotency.replay_ttl_seconds"
136
+ description: "Agent declares replay window TTL (required when supported: true)"
137
+
138
+ - check: field_present
139
+ path: "context"
140
+ description: "Response echoes back the context object"
141
+ - check: field_value
142
+ path: "context.correlation_id"
143
+ value: "idempotency--get_capabilities"
144
+ description: "Context correlation_id returned unchanged"
145
+
146
+ - id: missing_key
147
+ title: "Reject requests without idempotency_key"
148
+ narrative: |
149
+ A create_media_buy request without idempotency_key MUST be rejected. The schema
150
+ marks the field as required, so this is typically caught at validation time.
151
+
152
+ The reference `@adcp/client` SDK auto-injects `idempotency_key` on mutating
153
+ tasks via `applyIdempotencyInvariant` + client-side shaping. Without an
154
+ explicit opt-out this vector would never reach the agent with a missing key
155
+ — the runner would inject one before dispatch, and the vector would silently
156
+ pass for every SDK-speaking agent regardless of enforcement. The step below
157
+ sets `omit_idempotency_key: true` so the runner skips both its own
158
+ invariant application and the SDK's auto-inject. Do not remove the flag:
159
+ without it, "certified" agents all carry an unverified assertion on this
160
+ vector.
161
+
162
+ steps:
163
+ - id: create_media_buy_missing_key
164
+ title: "Missing idempotency_key returns INVALID_REQUEST"
165
+ narrative: |
166
+ Send a create_media_buy with no idempotency_key. The agent MUST reject this
167
+ with INVALID_REQUEST (or VALIDATION_ERROR as an accepted alternative) and
168
+ MUST NOT create a media buy.
169
+ task: create_media_buy
170
+ schema_ref: "media-buy/create-media-buy-request.json"
171
+ response_schema_ref: "media-buy/create-media-buy-response.json"
172
+ doc_ref: "/media-buy/task-reference/create_media_buy"
173
+ comply_scenario: error_handling
174
+ expect_error: true
175
+ negative_path: schema_invalid
176
+ omit_idempotency_key: true
177
+ stateful: false
178
+ expected: |
179
+ Reject with:
180
+ - code: INVALID_REQUEST or VALIDATION_ERROR
181
+ - recovery: correctable
182
+ - field: idempotency_key (recommended)
183
+
184
+ sample_request:
185
+ account:
186
+ brand:
187
+ domain: "acmeoutdoor.example"
188
+ operator: "pinnacle-agency.example"
189
+ brand:
190
+ domain: "acmeoutdoor.example"
191
+ start_time: "2026-05-01T00:00:00Z"
192
+ end_time: "2026-05-31T23:59:59Z"
193
+ packages:
194
+ - product_id: "test-product"
195
+ budget: 5000
196
+ pricing_option_id: "test-pricing"
197
+
198
+ context:
199
+ correlation_id: "idempotency--create_media_buy_missing_key"
200
+ validations:
201
+ - check: error_code
202
+ allowed_values: ["INVALID_REQUEST", "VALIDATION_ERROR"]
203
+ description: "Missing idempotency_key rejected with INVALID_REQUEST or VALIDATION_ERROR"
204
+
205
+ - check: field_present
206
+ path: "context"
207
+ description: "Response echoes back the context object"
208
+ - check: field_value
209
+ path: "context.correlation_id"
210
+ value: "idempotency--create_media_buy_missing_key"
211
+ description: "Context correlation_id returned unchanged"
212
+
213
+ - id: replay_same_payload
214
+ title: "Replay returns cached response"
215
+ narrative: |
216
+ The canonical retry-safety case: a buyer's first request times out but actually
217
+ succeeded on the server. The buyer retries with the same idempotency_key and
218
+ the same payload. The seller MUST return the same response — same media_buy_id,
219
+ no new side effects.
220
+
221
+ steps:
222
+ - id: create_media_buy_initial
223
+ title: "Initial create_media_buy with fresh key"
224
+ narrative: |
225
+ Create a media buy with a fresh UUID v4 idempotency_key. Capture the
226
+ returned media_buy_id for comparison against the replay.
227
+ task: create_media_buy
228
+ schema_ref: "media-buy/create-media-buy-request.json"
229
+ response_schema_ref: "media-buy/create-media-buy-response.json"
230
+ doc_ref: "/media-buy/task-reference/create_media_buy"
231
+ comply_scenario: idempotency_replay
232
+ stateful: true
233
+ context_outputs:
234
+ - name: initial_media_buy_id
235
+ path: "media_buy_id"
236
+ - name: idempotency_key_a
237
+ path: "idempotency_key"
238
+ expected: |
239
+ Create a new media buy and return a media_buy_id. This establishes the
240
+ canonical response that subsequent replays must match.
241
+
242
+ sample_request:
243
+ idempotency_key: "$generate:uuid_v4#replay_key"
244
+ account:
245
+ brand:
246
+ domain: "acmeoutdoor.example"
247
+ operator: "pinnacle-agency.example"
248
+ brand:
249
+ domain: "acmeoutdoor.example"
250
+ start_time: "2026-06-01T00:00:00Z"
251
+ end_time: "2026-06-30T23:59:59Z"
252
+ packages:
253
+ - product_id: "test-product"
254
+ budget: 5000
255
+ pricing_option_id: "test-pricing"
256
+ # Both the initial call and the replay below use the SAME webhook URL
257
+ # so the canonical payload hash matches across the two calls. A
258
+ # different URL between the two requests would hash differently and
259
+ # trip IDEMPOTENCY_CONFLICT instead of exercising replay. The URL
260
+ # template resolves against the runner's ephemeral webhook receiver
261
+ # (webhook_receiver_runner contract); runners without a receiver
262
+ # grade `no_duplicate_webhooks_on_replay` as not_applicable.
263
+ push_notification_config:
264
+ url: "{{runner.webhook_url:create_media_buy_initial}}"
265
+
266
+ context:
267
+ correlation_id: "idempotency--create_media_buy_initial"
268
+ validations:
269
+ - check: response_schema
270
+ description: "Response matches create-media-buy-response.json schema"
271
+ - check: field_present
272
+ path: "media_buy_id"
273
+ description: "Agent returns a media_buy_id for the initial request"
274
+ # Per `protocol-envelope.json`, fresh execution MAY omit
275
+ # `replayed` entirely; agents that do report it MUST NOT
276
+ # report `true` on a fresh call. `field_value_or_absent`
277
+ # (adcp-client#873, shipped in 5.16) asserts that tolerance:
278
+ # passes when absent OR present-and-matching. The replay
279
+ # step below asserts the positive side (`replayed: true`
280
+ # on replay) with plain `field_value`.
281
+ - check: field_value_or_absent
282
+ path: "replayed"
283
+ allowed_values: [false]
284
+ description: "If reported on fresh execution, replayed must be false"
285
+
286
+ - check: field_present
287
+ path: "context"
288
+ description: "Response echoes back the context object"
289
+ - check: field_value
290
+ path: "context.correlation_id"
291
+ value: "idempotency--create_media_buy_initial"
292
+ description: "Context correlation_id returned unchanged"
293
+
294
+ - id: create_media_buy_replay
295
+ title: "Replay with same key and payload returns cached response"
296
+ narrative: |
297
+ Re-send the exact same create_media_buy request — same idempotency_key,
298
+ same payload, same everything. The seller MUST return the same media_buy_id
299
+ from the prior step. It MUST NOT create a second media buy or fire a
300
+ second set of webhooks. This is the whole point of idempotency_key.
301
+ task: create_media_buy
302
+ schema_ref: "media-buy/create-media-buy-request.json"
303
+ response_schema_ref: "media-buy/create-media-buy-response.json"
304
+ doc_ref: "/media-buy/task-reference/create_media_buy"
305
+ comply_scenario: idempotency_replay
306
+ stateful: true
307
+ expected: |
308
+ Return the cached response from the initial request:
309
+ - media_buy_id: same as $context.initial_media_buy_id
310
+ - No new side effects (no duplicate webhooks, no new audit log entry)
311
+
312
+ Programmatic verification of "no duplicate webhooks" runs in the next step
313
+ (no_duplicate_webhooks_on_replay) when the storyboard is driven by a runner
314
+ that implements the webhook_receiver_runner contract. Runners without a
315
+ webhook receiver grade that step as not_applicable — agents MAY still claim
316
+ idempotency compliance, but the replay-side-effect invariant is only
317
+ programmatically graded when the webhook_callbacks specialism is also in
318
+ scope.
319
+
320
+ sample_request:
321
+ idempotency_key: "$generate:uuid_v4#replay_key"
322
+ account:
323
+ brand:
324
+ domain: "acmeoutdoor.example"
325
+ operator: "pinnacle-agency.example"
326
+ brand:
327
+ domain: "acmeoutdoor.example"
328
+ start_time: "2026-06-01T00:00:00Z"
329
+ end_time: "2026-06-30T23:59:59Z"
330
+ packages:
331
+ - product_id: "test-product"
332
+ budget: 5000
333
+ pricing_option_id: "test-pricing"
334
+ # Byte-identical to the initial call above — same URL, same step id
335
+ # token — so the canonical payload hash matches and the request
336
+ # lands on the replay branch rather than IDEMPOTENCY_CONFLICT.
337
+ push_notification_config:
338
+ url: "{{runner.webhook_url:create_media_buy_initial}}"
339
+
340
+ context:
341
+ correlation_id: "idempotency--create_media_buy_replay"
342
+ validations:
343
+ - check: response_schema
344
+ description: "Response matches create-media-buy-response.json schema"
345
+ - check: field_present
346
+ path: "media_buy_id"
347
+ description: "Replay returns a media_buy_id (same as initial)"
348
+ - check: field_value
349
+ path: "media_buy_id"
350
+ value: "$context.initial_media_buy_id"
351
+ description: "Replay returns the SAME media_buy_id as the initial call"
352
+ - check: field_value
353
+ path: "replayed"
354
+ value: true
355
+ description: "Replay sets replayed: true on the response envelope"
356
+
357
+ - check: field_present
358
+ path: "context"
359
+ description: "Response echoes back the context object"
360
+ - check: field_value
361
+ path: "context.correlation_id"
362
+ value: "idempotency--create_media_buy_replay"
363
+ description: "Context correlation_id returned unchanged"
364
+
365
+ - id: no_duplicate_webhooks_on_replay
366
+ title: "No duplicate webhooks fired across initial + replay"
367
+ narrative: |
368
+ The central invariant of idempotency: replaying with the same key MUST
369
+ NOT produce duplicate side effects. Webhook emission is the observable
370
+ side effect most likely to leak if the seller re-executes on replay
371
+ instead of returning cached state.
372
+
373
+ This step requires the storyboard runner to implement the
374
+ `webhook_receiver_runner` contract at
375
+ `test-kits/webhook-receiver-runner.yaml`. Runners without a webhook
376
+ receiver skip this step with a stable not_applicable marker; the
377
+ replay-side-effect invariant then relies on the next phase's cached-
378
+ response assertions plus manual audit of the seller's webhook
379
+ delivery history.
380
+
381
+ When the receiver IS in scope, the runner counts webhooks arriving at
382
+ {{runner.webhook_url:create_media_buy_initial}} across the initial
383
+ call's window AND the replay call's window. A conformant seller emits
384
+ webhooks only on the first execution; a non-conformant seller that
385
+ re-executes on replay emits a second set, catchable only by live
386
+ observation.
387
+ task: expect_webhook
388
+ # `triggered_by` points at the initial call (not the replay) because the
389
+ # default filter resolves `step_id` + `operation_id` from `stepOperationIds`
390
+ # for that step — which is populated when the initial sample_request's
391
+ # `push_notification_config.url` expanded `{{runner.webhook_url:create_media_buy_initial}}`
392
+ # above. The execution-order wait still spans the replay window: this
393
+ # step runs after `create_media_buy_replay` in YAML order and `wait_all`
394
+ # drains the full `timeout_seconds` so any webhook re-fired by the
395
+ # replay is captured and counted against the cap below. A non-conformant
396
+ # seller that re-executes on replay would deliver a second webhook with
397
+ # a fresh idempotency_key and trip `duplicate_webhook_on_replay`.
398
+ triggered_by: create_media_buy_initial
399
+ timeout_seconds: 30
400
+ expect_max_deliveries_per_logical_event: 1
401
+ requires_contract: webhook_receiver_runner
402
+ stateful: true
403
+ expected: |
404
+ At most one logical webhook event delivered for the create_media_buy
405
+ operation across both the initial and replay windows. If the runner
406
+ observes a second webhook delivery tagged with a distinct
407
+ idempotency_key but the same media_buy_id/operation_id, the seller is
408
+ re-executing on replay and the step fails with
409
+ duplicate_webhook_on_replay. Not_applicable when the runner does not
410
+ host a webhook receiver.
411
+
412
+ - id: key_reuse_conflict
413
+ title: "Key reuse with different payload returns IDEMPOTENCY_CONFLICT"
414
+ narrative: |
415
+ When a buyer reuses an idempotency_key with a different payload within the
416
+ replay window, the seller MUST reject the second request with
417
+ IDEMPOTENCY_CONFLICT. Silently applying the new payload would break the
418
+ at-most-once guarantee; silently returning the old response would hide a
419
+ real bug in the buyer's retry logic.
420
+
421
+ steps:
422
+ - id: create_media_buy_conflict
423
+ title: "Same key, different payload returns IDEMPOTENCY_CONFLICT"
424
+ narrative: |
425
+ Re-send create_media_buy with the same idempotency_key as the replay
426
+ phase but a materially different payload — a different end_time and
427
+ a different budget. The seller MUST reject this with IDEMPOTENCY_CONFLICT.
428
+ CONFLICT is accepted as a fallback for sellers that haven't adopted the
429
+ new error code yet.
430
+ task: create_media_buy
431
+ schema_ref: "media-buy/create-media-buy-request.json"
432
+ response_schema_ref: "media-buy/create-media-buy-response.json"
433
+ doc_ref: "/media-buy/task-reference/create_media_buy"
434
+ comply_scenario: idempotency_conflict
435
+ expect_error: true
436
+ negative_path: payload_well_formed
437
+ stateful: true
438
+ expected: |
439
+ Reject with:
440
+ - code: IDEMPOTENCY_CONFLICT (preferred) or CONFLICT (fallback)
441
+ - recovery: correctable (buyer should use a fresh UUID v4)
442
+
443
+ sample_request:
444
+ idempotency_key: "$generate:uuid_v4#replay_key"
445
+ account:
446
+ brand:
447
+ domain: "acmeoutdoor.example"
448
+ operator: "pinnacle-agency.example"
449
+ brand:
450
+ domain: "acmeoutdoor.example"
451
+ start_time: "2026-06-01T00:00:00Z"
452
+ end_time: "2026-09-30T23:59:59Z"
453
+ packages:
454
+ - product_id: "test-product"
455
+ budget: 25000
456
+ pricing_option_id: "test-pricing"
457
+
458
+ context:
459
+ correlation_id: "idempotency--create_media_buy_conflict"
460
+ validations:
461
+ - check: error_code
462
+ allowed_values: ["IDEMPOTENCY_CONFLICT", "CONFLICT"]
463
+ description: "Key reuse with different payload returns IDEMPOTENCY_CONFLICT"
464
+
465
+ - check: field_present
466
+ path: "context"
467
+ description: "Response echoes back the context object"
468
+ - check: field_value
469
+ path: "context.correlation_id"
470
+ value: "idempotency--create_media_buy_conflict"
471
+ description: "Context correlation_id returned unchanged"
472
+
473
+ reviewer_checks:
474
+ - "Error body MUST NOT include the cached payload, the original request, or any fingerprint (hash, digest, field diff). Leaking cached state turns key-reuse into a read oracle for attackers who stole a key."
475
+ - "Error body MAY include code + message only. No `field` json-pointer — even a pointer like `/packages/0/budget` reveals schema shape."
476
+ - "Reviewer MUST confirm the seller's documentation states that errored requests release the idempotency claim, or manually probe the behavior (force a deterministic terminal error, then retry with the same key and a valid payload — the seller MUST return a fresh success, not IDEMPOTENCY_CONFLICT and not the cached error). See security.mdx#idempotency rule 3."
477
+
478
+ - id: fresh_key_new_resource
479
+ title: "Different key creates a new resource"
480
+ narrative: |
481
+ A request with a DIFFERENT idempotency_key is treated as a new request, even
482
+ if the payload is byte-for-byte identical to a prior request. The key — not
483
+ payload equivalence — is what provides the dedup boundary. This confirms the
484
+ seller is checking the key, not fingerprinting requests.
485
+
486
+ steps:
487
+ - id: create_media_buy_fresh_key
488
+ title: "Fresh key with identical payload creates a new media buy"
489
+ narrative: |
490
+ Send the same payload as the initial create_media_buy but with a different
491
+ idempotency_key. The seller MUST treat this as a new request and return a
492
+ NEW media_buy_id distinct from $context.initial_media_buy_id.
493
+ task: create_media_buy
494
+ schema_ref: "media-buy/create-media-buy-request.json"
495
+ response_schema_ref: "media-buy/create-media-buy-response.json"
496
+ doc_ref: "/media-buy/task-reference/create_media_buy"
497
+ comply_scenario: idempotency_fresh_key
498
+ stateful: true
499
+ context_outputs:
500
+ - name: fresh_key_media_buy_id
501
+ path: "media_buy_id"
502
+ expected: |
503
+ Return a NEW media_buy_id, distinct from $context.initial_media_buy_id.
504
+ This confirms the seller treats the fresh key as a new request.
505
+
506
+ sample_request:
507
+ idempotency_key: "$generate:uuid_v4#fresh_key"
508
+ account:
509
+ brand:
510
+ domain: "acmeoutdoor.example"
511
+ operator: "pinnacle-agency.example"
512
+ brand:
513
+ domain: "acmeoutdoor.example"
514
+ start_time: "2026-06-01T00:00:00Z"
515
+ end_time: "2026-06-30T23:59:59Z"
516
+ packages:
517
+ - product_id: "test-product"
518
+ budget: 5000
519
+ pricing_option_id: "test-pricing"
520
+
521
+ context:
522
+ correlation_id: "idempotency--create_media_buy_fresh_key"
523
+ validations:
524
+ - check: response_schema
525
+ description: "Response matches create-media-buy-response.json schema"
526
+ - check: field_present
527
+ path: "media_buy_id"
528
+ description: "Fresh key returns a media_buy_id"
529
+ # Fresh-key execution is a non-replay call — same tolerance as
530
+ # create_media_buy_initial applies: `replayed` MAY be omitted,
531
+ # but if present MUST NOT be `true`.
532
+ - check: field_value_or_absent
533
+ path: "replayed"
534
+ allowed_values: [false]
535
+ description: "If reported on fresh-key execution, replayed must be false"
536
+
537
+ - check: field_present
538
+ path: "context"
539
+ description: "Response echoes back the context object"
540
+ - check: field_value
541
+ path: "context.correlation_id"
542
+ value: "idempotency--create_media_buy_fresh_key"
543
+ description: "Context correlation_id returned unchanged"
544
+
545
+ - id: verify_media_buy_count
546
+ title: "Verify dedup actually happened"
547
+ narrative: |
548
+ Call get_media_buys to confirm that only TWO media buys were created across
549
+ all prior steps — one for the initial + replay pair (same key, deduplicated)
550
+ and one for the fresh-key request. If the seller ignored the idempotency_key,
551
+ there would be three or more.
552
+
553
+ steps:
554
+ - id: get_media_buys_dedup_check
555
+ title: "Confirm dedup via get_media_buys"
556
+ narrative: |
557
+ Query the two media_buy_ids captured from prior steps. Both should exist
558
+ and be distinct. This is an end-to-end verification that the replay did
559
+ not create a duplicate row.
560
+ task: get_media_buys
561
+ schema_ref: "media-buy/get-media-buys-request.json"
562
+ response_schema_ref: "media-buy/get-media-buys-response.json"
563
+ doc_ref: "/media-buy/task-reference/get_media_buys"
564
+ comply_scenario: idempotency_verify
565
+ stateful: true
566
+ expected: |
567
+ Return exactly two media buys with the two captured IDs. Not three.
568
+
569
+ sample_request:
570
+ account:
571
+ brand:
572
+ domain: "acmeoutdoor.example"
573
+ operator: "pinnacle-agency.example"
574
+ media_buy_ids:
575
+ - "$context.initial_media_buy_id"
576
+ - "$context.fresh_key_media_buy_id"
577
+
578
+ context:
579
+ correlation_id: "idempotency--get_media_buys_dedup_check"
580
+ validations:
581
+ - check: response_schema
582
+ description: "Response matches get-media-buys-response.json schema"
583
+ - check: field_present
584
+ path: "media_buys"
585
+ description: "Response contains the queried media buys"
586
+
587
+ - check: field_present
588
+ path: "context"
589
+ description: "Response echoes back the context object"
590
+ - check: field_value
591
+ path: "context.correlation_id"
592
+ value: "idempotency--get_media_buys_dedup_check"
593
+ description: "Context correlation_id returned unchanged"