@adcp/sdk 7.10.2 → 7.11.1

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 (305) hide show
  1. package/compliance/cache/3.1.0-rc.2/domains/brand/index.yaml +160 -0
  2. package/compliance/cache/3.1.0-rc.2/domains/brand/scenarios/distributed_brand_resolution.yaml +415 -0
  3. package/compliance/cache/3.1.0-rc.2/domains/brand/scenarios/single_side_trust_extension.yaml +454 -0
  4. package/compliance/cache/3.1.0-rc.2/domains/creative/index.yaml +339 -0
  5. package/compliance/cache/3.1.0-rc.2/domains/creative/scenarios/billing_out_of_band.yaml +153 -0
  6. package/compliance/cache/3.1.0-rc.2/domains/creative/scenarios/canonical_supported_formats.yaml +212 -0
  7. package/compliance/cache/3.1.0-rc.2/domains/creative/scenarios/creative_lifecycle_webhooks.yaml +389 -0
  8. package/compliance/cache/3.1.0-rc.2/domains/creative/scenarios/native_in_feed.yaml +543 -0
  9. package/compliance/cache/3.1.0-rc.2/domains/governance/index.yaml +682 -0
  10. package/compliance/cache/3.1.0-rc.2/domains/media-buy/index.yaml +789 -0
  11. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/audience_buy_flow.yaml +380 -0
  12. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/available_actions.yaml +565 -0
  13. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/billing_finality_delivery.yaml +354 -0
  14. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/canonical_formats.yaml +861 -0
  15. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/clicks_buy_flow.yaml +264 -0
  16. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/completed_views_buy_flow.yaml +344 -0
  17. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/create_media_buy_async.yaml +234 -0
  18. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/creative_fate_after_cancellation.yaml +419 -0
  19. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/creative_reception.yaml +247 -0
  20. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/delivery_reporting.yaml +357 -0
  21. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/dependency_impairment.yaml +633 -0
  22. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/dependency_impairment_cardinality.yaml +800 -0
  23. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/event_dedup_flow.yaml +399 -0
  24. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/frequency_cap_enforcement.yaml +309 -0
  25. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/governance_approved.yaml +214 -0
  26. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/governance_conditions.yaml +199 -0
  27. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/governance_denied.yaml +204 -0
  28. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/governance_denied_recovery.yaml +252 -0
  29. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/invalid_transitions.yaml +289 -0
  30. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/inventory_list_no_match.yaml +148 -0
  31. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/inventory_list_targeting.yaml +276 -0
  32. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/measurement_accountability.yaml +244 -0
  33. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/measurement_terms_rejected.yaml +203 -0
  34. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/package_correlation_legacy_fallback.yaml +113 -0
  35. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/pending_creatives_to_start.yaml +292 -0
  36. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/per_creative_conversion_attribution.yaml +500 -0
  37. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/performance_buy_flow.yaml +428 -0
  38. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/performance_buy_flow_roas.yaml +470 -0
  39. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/product_signal_targeting.yaml +373 -0
  40. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/proposal_finalize.yaml +399 -0
  41. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/proposal_finalize_asap_timing.yaml +264 -0
  42. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/proposal_not_found_errors.yaml +257 -0
  43. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/provenance_audit_observation.yaml +333 -0
  44. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/provenance_enforcement.yaml +517 -0
  45. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/provenance_truth_of_claim.yaml +294 -0
  46. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/reach_buy_flow.yaml +823 -0
  47. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/refine_finalize_exclusivity.yaml +360 -0
  48. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/refine_products.yaml +148 -0
  49. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/vendor_metric_accountability.yaml +293 -0
  50. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/vendor_metric_catalog_precondition.yaml +307 -0
  51. package/compliance/cache/3.1.0-rc.2/domains/media-buy/scenarios/vendor_metric_optimization_flow.yaml +576 -0
  52. package/compliance/cache/3.1.0-rc.2/domains/media-buy/state-machine.yaml +442 -0
  53. package/compliance/cache/3.1.0-rc.2/domains/signals/index.yaml +266 -0
  54. package/compliance/cache/3.1.0-rc.2/domains/sponsored-intelligence/index.yaml +256 -0
  55. package/compliance/cache/3.1.0-rc.2/index.json +356 -0
  56. package/compliance/cache/3.1.0-rc.2/protocols/brand/index.yaml +160 -0
  57. package/compliance/cache/3.1.0-rc.2/protocols/brand/scenarios/distributed_brand_resolution.yaml +415 -0
  58. package/compliance/cache/3.1.0-rc.2/protocols/brand/scenarios/single_side_trust_extension.yaml +454 -0
  59. package/compliance/cache/3.1.0-rc.2/protocols/creative/index.yaml +339 -0
  60. package/compliance/cache/3.1.0-rc.2/protocols/creative/scenarios/billing_out_of_band.yaml +153 -0
  61. package/compliance/cache/3.1.0-rc.2/protocols/creative/scenarios/canonical_supported_formats.yaml +212 -0
  62. package/compliance/cache/3.1.0-rc.2/protocols/creative/scenarios/creative_lifecycle_webhooks.yaml +389 -0
  63. package/compliance/cache/3.1.0-rc.2/protocols/creative/scenarios/native_in_feed.yaml +543 -0
  64. package/compliance/cache/3.1.0-rc.2/protocols/governance/index.yaml +682 -0
  65. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/index.yaml +789 -0
  66. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/audience_buy_flow.yaml +380 -0
  67. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/available_actions.yaml +565 -0
  68. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/billing_finality_delivery.yaml +354 -0
  69. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/canonical_formats.yaml +861 -0
  70. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/clicks_buy_flow.yaml +264 -0
  71. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/completed_views_buy_flow.yaml +344 -0
  72. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/create_media_buy_async.yaml +234 -0
  73. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/creative_fate_after_cancellation.yaml +419 -0
  74. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/creative_reception.yaml +247 -0
  75. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/delivery_reporting.yaml +357 -0
  76. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/dependency_impairment.yaml +633 -0
  77. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/dependency_impairment_cardinality.yaml +800 -0
  78. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/event_dedup_flow.yaml +399 -0
  79. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/frequency_cap_enforcement.yaml +309 -0
  80. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/governance_approved.yaml +214 -0
  81. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/governance_conditions.yaml +199 -0
  82. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/governance_denied.yaml +204 -0
  83. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/governance_denied_recovery.yaml +252 -0
  84. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/invalid_transitions.yaml +289 -0
  85. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/inventory_list_no_match.yaml +148 -0
  86. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/inventory_list_targeting.yaml +276 -0
  87. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/measurement_accountability.yaml +244 -0
  88. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/measurement_terms_rejected.yaml +203 -0
  89. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/package_correlation_legacy_fallback.yaml +113 -0
  90. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/pending_creatives_to_start.yaml +292 -0
  91. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/per_creative_conversion_attribution.yaml +500 -0
  92. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/performance_buy_flow.yaml +428 -0
  93. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/performance_buy_flow_roas.yaml +470 -0
  94. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/product_signal_targeting.yaml +373 -0
  95. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/proposal_finalize.yaml +399 -0
  96. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/proposal_finalize_asap_timing.yaml +264 -0
  97. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/proposal_not_found_errors.yaml +257 -0
  98. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/provenance_audit_observation.yaml +333 -0
  99. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/provenance_enforcement.yaml +517 -0
  100. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/provenance_truth_of_claim.yaml +294 -0
  101. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/reach_buy_flow.yaml +823 -0
  102. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/refine_finalize_exclusivity.yaml +360 -0
  103. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/refine_products.yaml +148 -0
  104. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/vendor_metric_accountability.yaml +293 -0
  105. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/vendor_metric_catalog_precondition.yaml +307 -0
  106. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/scenarios/vendor_metric_optimization_flow.yaml +576 -0
  107. package/compliance/cache/3.1.0-rc.2/protocols/media-buy/state-machine.yaml +442 -0
  108. package/compliance/cache/3.1.0-rc.2/protocols/signals/index.yaml +266 -0
  109. package/compliance/cache/3.1.0-rc.2/protocols/sponsored-intelligence/index.yaml +256 -0
  110. package/compliance/cache/3.1.0-rc.2/specialisms/audience-sync/index.yaml +313 -0
  111. package/compliance/cache/3.1.0-rc.2/specialisms/brand-rights/index.yaml +350 -0
  112. package/compliance/cache/3.1.0-rc.2/specialisms/brand-rights/scenarios/governance_denied.yaml +226 -0
  113. package/compliance/cache/3.1.0-rc.2/specialisms/collection-lists/index.yaml +359 -0
  114. package/compliance/cache/3.1.0-rc.2/specialisms/content-standards/index.yaml +572 -0
  115. package/compliance/cache/3.1.0-rc.2/specialisms/creative-ad-server/index.yaml +409 -0
  116. package/compliance/cache/3.1.0-rc.2/specialisms/creative-generative/generative-seller.yaml +807 -0
  117. package/compliance/cache/3.1.0-rc.2/specialisms/creative-generative/index.yaml +758 -0
  118. package/compliance/cache/3.1.0-rc.2/specialisms/creative-template/index.yaml +510 -0
  119. package/compliance/cache/3.1.0-rc.2/specialisms/governance-aware-seller/index.yaml +143 -0
  120. package/compliance/cache/3.1.0-rc.2/specialisms/governance-aware-seller/scenarios/governance_multi_agent_rejected.yaml +117 -0
  121. package/compliance/cache/3.1.0-rc.2/specialisms/governance-delivery-monitor/index.yaml +441 -0
  122. package/compliance/cache/3.1.0-rc.2/specialisms/governance-spend-authority/denied.yaml +221 -0
  123. package/compliance/cache/3.1.0-rc.2/specialisms/governance-spend-authority/index.yaml +330 -0
  124. package/compliance/cache/3.1.0-rc.2/specialisms/property-lists/index.yaml +482 -0
  125. package/compliance/cache/3.1.0-rc.2/specialisms/sales-broadcast-tv/index.yaml +738 -0
  126. package/compliance/cache/3.1.0-rc.2/specialisms/sales-catalog-driven/index.yaml +840 -0
  127. package/compliance/cache/3.1.0-rc.2/specialisms/sales-guaranteed/index.yaml +601 -0
  128. package/compliance/cache/3.1.0-rc.2/specialisms/sales-non-guaranteed/index.yaml +546 -0
  129. package/compliance/cache/3.1.0-rc.2/specialisms/sales-proposal-mode/index.yaml +586 -0
  130. package/compliance/cache/3.1.0-rc.2/specialisms/sales-social/index.yaml +919 -0
  131. package/compliance/cache/3.1.0-rc.2/specialisms/signal-marketplace/index.yaml +424 -0
  132. package/compliance/cache/3.1.0-rc.2/specialisms/signal-marketplace/scenarios/governance_denied.yaml +210 -0
  133. package/compliance/cache/3.1.0-rc.2/specialisms/signal-owned/index.yaml +317 -0
  134. package/compliance/cache/3.1.0-rc.2/specialisms/sponsored-intelligence/index.yaml +59 -0
  135. package/compliance/cache/3.1.0-rc.2/test-kits/acme-outdoor-live.yaml +78 -0
  136. package/compliance/cache/3.1.0-rc.2/test-kits/acme-outdoor.yaml +223 -0
  137. package/compliance/cache/3.1.0-rc.2/test-kits/billing-gate-runner.yaml +115 -0
  138. package/compliance/cache/3.1.0-rc.2/test-kits/bistro-oranje.yaml +126 -0
  139. package/compliance/cache/3.1.0-rc.2/test-kits/distributed-brand-runner.yaml +281 -0
  140. package/compliance/cache/3.1.0-rc.2/test-kits/nova-motors.yaml +262 -0
  141. package/compliance/cache/3.1.0-rc.2/test-kits/osei-natural.yaml +126 -0
  142. package/compliance/cache/3.1.0-rc.2/test-kits/parallel-dispatch-runner.yaml +196 -0
  143. package/compliance/cache/3.1.0-rc.2/test-kits/rate-limit-trip-runner.yaml +172 -0
  144. package/compliance/cache/3.1.0-rc.2/test-kits/signed-requests-runner.yaml +155 -0
  145. package/compliance/cache/3.1.0-rc.2/test-kits/single-side-trust-runner.yaml +294 -0
  146. package/compliance/cache/3.1.0-rc.2/test-kits/substitution-observer-runner.yaml +688 -0
  147. package/compliance/cache/3.1.0-rc.2/test-kits/summit-foods.yaml +125 -0
  148. package/compliance/cache/3.1.0-rc.2/test-kits/webhook-receiver-runner.yaml +265 -0
  149. package/compliance/cache/3.1.0-rc.2/test-vectors/plan-hash/001-minimal-plan.json +43 -0
  150. package/compliance/cache/3.1.0-rc.2/test-vectors/plan-hash/002-full-plan.json +217 -0
  151. package/compliance/cache/3.1.0-rc.2/test-vectors/plan-hash/003-bookkeeping-stripped.json +60 -0
  152. package/compliance/cache/3.1.0-rc.2/test-vectors/plan-hash/004a-human-review-omitted.json +43 -0
  153. package/compliance/cache/3.1.0-rc.2/test-vectors/plan-hash/004b-human-review-explicit-null.json +49 -0
  154. package/compliance/cache/3.1.0-rc.2/test-vectors/plan-hash/005a-policy-categories-order-1.json +53 -0
  155. package/compliance/cache/3.1.0-rc.2/test-vectors/plan-hash/005b-policy-categories-order-2.json +57 -0
  156. package/compliance/cache/3.1.0-rc.2/test-vectors/plan-hash/006a-ext-trace-v1.json +49 -0
  157. package/compliance/cache/3.1.0-rc.2/test-vectors/plan-hash/006b-ext-trace-v2.json +53 -0
  158. package/compliance/cache/3.1.0-rc.2/test-vectors/plan-hash/007-unicode-objectives.json +43 -0
  159. package/compliance/cache/3.1.0-rc.2/test-vectors/plan-hash/008-numeric-canonicalization.json +65 -0
  160. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/README.md +220 -0
  161. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/canonicalization.json +241 -0
  162. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/keys.json +60 -0
  163. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/001-no-signature-header.json +24 -0
  164. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/002-wrong-tag.json +26 -0
  165. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/003-expired-signature.json +26 -0
  166. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/004-window-too-long.json +26 -0
  167. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/005-alg-not-allowed.json +26 -0
  168. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/006-missing-covered-component.json +26 -0
  169. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/007-missing-content-digest.json +26 -0
  170. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/008-unknown-keyid.json +26 -0
  171. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/009-key-ops-missing-verify.json +27 -0
  172. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/010-content-digest-mismatch.json +33 -0
  173. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/011-malformed-header.json +27 -0
  174. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/012-missing-expires-param.json +26 -0
  175. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/013-expires-le-created.json +27 -0
  176. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/014-missing-nonce-param.json +27 -0
  177. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/015-signature-invalid.json +28 -0
  178. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/016-replayed-nonce.json +35 -0
  179. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/017-key-revoked.json +38 -0
  180. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/018-digest-covered-when-forbidden.json +28 -0
  181. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/019-signature-without-signature-input.json +26 -0
  182. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/020-rate-abuse.json +34 -0
  183. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/021-duplicate-signature-input-label.json +31 -0
  184. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/022-multi-valued-content-type.json +31 -0
  185. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/023-multi-valued-content-digest.json +32 -0
  186. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/024-unquoted-string-param.json +31 -0
  187. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/025-jwk-alg-crv-mismatch.json +43 -0
  188. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/026-non-ascii-host.json +31 -0
  189. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/027-webhook-registration-authentication-unsigned.json +25 -0
  190. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/negative/028-unsigned-protocol-method-required.json +26 -0
  191. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/001-basic-post.json +30 -0
  192. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/002-post-with-content-digest.json +31 -0
  193. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/003-es256-post.json +30 -0
  194. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/004-multiple-signature-labels.json +26 -0
  195. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/005-default-port-stripped.json +30 -0
  196. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/006-dot-segment-path.json +30 -0
  197. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/007-query-byte-preserved.json +30 -0
  198. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/008-percent-encoded-path.json +30 -0
  199. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/009-percent-encoded-unreserved-decoded.json +30 -0
  200. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/010-percent-encoded-slash-preserved.json +30 -0
  201. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/011-ipv6-authority.json +30 -0
  202. package/compliance/cache/3.1.0-rc.2/test-vectors/request-signing/positive/012-ipv6-authority-default-port-stripped.json +30 -0
  203. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/README.md +211 -0
  204. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/keys.json +61 -0
  205. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/001-wrong-tag.json +26 -0
  206. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/002-expired-signature.json +26 -0
  207. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/003-window-too-long.json +26 -0
  208. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/004-alg-not-allowed.json +26 -0
  209. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/005-missing-authority-component.json +26 -0
  210. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/006-missing-content-digest.json +25 -0
  211. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/007-unknown-keyid.json +26 -0
  212. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/008-wrong-adcp-use.json +26 -0
  213. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/009-content-digest-mismatch.json +26 -0
  214. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/010-malformed-signature-input.json +26 -0
  215. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/011-signature-without-input.json +25 -0
  216. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/012-missing-expires-param.json +26 -0
  217. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/013-expires-le-created.json +26 -0
  218. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/014-missing-nonce-param.json +26 -0
  219. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/015-signature-invalid.json +26 -0
  220. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/016-replayed-nonce.json +37 -0
  221. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/017-key-revoked.json +32 -0
  222. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/018-rate-abuse.json +33 -0
  223. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/019-revocation-stale.json +32 -0
  224. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/020-key-ops-missing-verify.json +41 -0
  225. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/negative/021-base64-alphabet-mixing.json +26 -0
  226. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/positive/001-basic-post.json +24 -0
  227. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/positive/002-es256-post.json +24 -0
  228. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/positive/003-multiple-signature-labels.json +24 -0
  229. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/positive/004-default-port-stripped.json +24 -0
  230. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/positive/005-percent-encoded-path.json +24 -0
  231. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/positive/006-query-byte-preserved.json +24 -0
  232. package/compliance/cache/3.1.0-rc.2/test-vectors/webhook-signing/positive/007-body-without-idempotency-key.json +25 -0
  233. package/compliance/cache/3.1.0-rc.2/universal/billing-gate-dispatch.yaml +450 -0
  234. package/compliance/cache/3.1.0-rc.2/universal/canonical-format-validate-input.yaml +640 -0
  235. package/compliance/cache/3.1.0-rc.2/universal/capability-discovery.yaml +125 -0
  236. package/compliance/cache/3.1.0-rc.2/universal/collection-lists-pagination-integrity.yaml +306 -0
  237. package/compliance/cache/3.1.0-rc.2/universal/comply-controller-mode-gate.yaml +141 -0
  238. package/compliance/cache/3.1.0-rc.2/universal/content-standards-pagination-integrity.yaml +326 -0
  239. package/compliance/cache/3.1.0-rc.2/universal/deterministic-testing.yaml +1430 -0
  240. package/compliance/cache/3.1.0-rc.2/universal/error-compliance-signals.yaml +377 -0
  241. package/compliance/cache/3.1.0-rc.2/universal/error-compliance.yaml +528 -0
  242. package/compliance/cache/3.1.0-rc.2/universal/fictional-entities.yaml +307 -0
  243. package/compliance/cache/3.1.0-rc.2/universal/get-media-buys-pagination-integrity.yaml +160 -0
  244. package/compliance/cache/3.1.0-rc.2/universal/get-signals-pagination-integrity.yaml +210 -0
  245. package/compliance/cache/3.1.0-rc.2/universal/idempotency.yaml +861 -0
  246. package/compliance/cache/3.1.0-rc.2/universal/notification-config-event-scope.yaml +119 -0
  247. package/compliance/cache/3.1.0-rc.2/universal/notification-config-lifecycle.yaml +337 -0
  248. package/compliance/cache/3.1.0-rc.2/universal/notification-config-rejections.yaml +107 -0
  249. package/compliance/cache/3.1.0-rc.2/universal/pagination-integrity-creative-formats.yaml +265 -0
  250. package/compliance/cache/3.1.0-rc.2/universal/pagination-integrity-list-accounts.yaml +245 -0
  251. package/compliance/cache/3.1.0-rc.2/universal/pagination-integrity.yaml +263 -0
  252. package/compliance/cache/3.1.0-rc.2/universal/property-lists-pagination-integrity.yaml +307 -0
  253. package/compliance/cache/3.1.0-rc.2/universal/read-tool-idempotency.yaml +405 -0
  254. package/compliance/cache/3.1.0-rc.2/universal/runner-output-contract.yaml +1285 -0
  255. package/compliance/cache/3.1.0-rc.2/universal/schema-validation-signals.yaml +181 -0
  256. package/compliance/cache/3.1.0-rc.2/universal/schema-validation.yaml +548 -0
  257. package/compliance/cache/3.1.0-rc.2/universal/security.yaml +539 -0
  258. package/compliance/cache/3.1.0-rc.2/universal/signed-requests.yaml +217 -0
  259. package/compliance/cache/3.1.0-rc.2/universal/stale-response-advisory.yaml +295 -0
  260. package/compliance/cache/3.1.0-rc.2/universal/storyboard-schema.yaml +2194 -0
  261. package/compliance/cache/3.1.0-rc.2/universal/v3-envelope-integrity.yaml +117 -0
  262. package/compliance/cache/3.1.0-rc.2/universal/version-negotiation.yaml +130 -0
  263. package/compliance/cache/3.1.0-rc.2/universal/webhook-emission.yaml +411 -0
  264. package/compliance/cache/3.1.0-rc.2/universal/wholesale-feed-bulk-webhooks.yaml +82 -0
  265. package/compliance/cache/3.1.0-rc.2/universal/wholesale-feed-product-webhooks.yaml +83 -0
  266. package/compliance/cache/3.1.0-rc.2/universal/wholesale-feed-products.yaml +151 -0
  267. package/compliance/cache/3.1.0-rc.2/universal/wholesale-feed-signal-webhooks.yaml +83 -0
  268. package/compliance/cache/3.1.0-rc.2/universal/wholesale-feed-signals.yaml +149 -0
  269. package/dist/lib/index.d.ts +1 -1
  270. package/dist/lib/index.d.ts.map +1 -1
  271. package/dist/lib/index.js +9 -5
  272. package/dist/lib/index.js.map +1 -1
  273. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  274. package/dist/lib/testing/storyboard/default-invariants.js +30 -1
  275. package/dist/lib/testing/storyboard/default-invariants.js.map +1 -1
  276. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  277. package/dist/lib/testing/storyboard/runner.js +84 -21
  278. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  279. package/dist/lib/testing/storyboard/types.d.ts +21 -0
  280. package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
  281. package/dist/lib/testing/storyboard/types.js.map +1 -1
  282. package/dist/lib/testing/types.d.ts +9 -0
  283. package/dist/lib/testing/types.d.ts.map +1 -1
  284. package/dist/lib/types/schemas.generated.d.ts +6707 -12040
  285. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  286. package/dist/lib/types/schemas.generated.js +1 -1
  287. package/dist/lib/types/schemas.generated.js.map +1 -1
  288. package/dist/lib/utils/signal-id-builders.d.ts +19 -0
  289. package/dist/lib/utils/signal-id-builders.d.ts.map +1 -1
  290. package/dist/lib/utils/signal-id-builders.js +30 -0
  291. package/dist/lib/utils/signal-id-builders.js.map +1 -1
  292. package/dist/lib/utils/tool-request-schemas.d.ts.map +1 -1
  293. package/dist/lib/utils/tool-request-schemas.js +3 -0
  294. package/dist/lib/utils/tool-request-schemas.js.map +1 -1
  295. package/dist/lib/v2/projection/constants.d.ts +28 -0
  296. package/dist/lib/v2/projection/constants.d.ts.map +1 -0
  297. package/dist/lib/v2/projection/constants.js +31 -0
  298. package/dist/lib/v2/projection/constants.js.map +1 -0
  299. package/dist/lib/v2/projection/registry.d.ts.map +1 -1
  300. package/dist/lib/v2/projection/registry.js +9 -4
  301. package/dist/lib/v2/projection/registry.js.map +1 -1
  302. package/dist/lib/version.d.ts +3 -3
  303. package/dist/lib/version.js +3 -3
  304. package/package.json +1 -1
  305. package/skills/SHAPE-GOTCHAS.md +5 -0
@@ -0,0 +1,823 @@
1
+ id: media_buy_seller/reach_buy_flow
2
+ version: "1.0.0"
3
+ title: "Seller fulfills a media buy with a reach optimization goal"
4
+ category: media_buy_seller
5
+ summary: "Verifies that a seller advertising reach in supported_optimization_metrics can accept a media buy whose metric-kind optimization_goal targets unique reach with a buyer-supplied reach_unit, reject reach_unit values not in the product's metric_optimization.supported_reach_units, and report reach + frequency on delivery. Sibling to the performance buy scenarios on the brand-reach side: sellers that don't advertise reach as an optimization metric (most pure performance DSPs, retail-media networks) grade not_applicable."
6
+ track: media_buy
7
+
8
+ # Gate: scenario runs only when the seller declares reach in
9
+ # media_buy.supported_optimization_metrics (proposed in #4669). Sellers
10
+ # whose products optimize for clicks / conversions / views but not unique
11
+ # reach grade `not_applicable`, not failing. Reach as an optimization
12
+ # metric is upper-funnel-shaped — broadcast TV, CTV, social brand
13
+ # campaigns, audio — and gating on the explicit metric in
14
+ # supported_optimization_metrics avoids over-claiming against the
15
+ # performance-tracking community. Requires runner support for the
16
+ # `contains:` matcher (adcp-client >= 7.70).
17
+ requires_capability:
18
+ path: media_buy.supported_optimization_metrics
19
+ contains: "reach"
20
+
21
+ required_tools:
22
+ - get_products
23
+ - create_media_buy
24
+ - get_media_buy_delivery
25
+ - comply_test_controller
26
+
27
+ invariants:
28
+ - status.monotonic
29
+
30
+ narrative: |
31
+ A "reach buy" is a media buy whose optimization_goal is metric-kind with
32
+ metric: reach and a buyer-supplied reach_unit naming the units of the
33
+ count (individuals / households / devices / accounts / cookies / custom).
34
+ Optionally the buyer specifies a target_frequency band — the seller treats
35
+ impressions toward entities already within [min, max] as lower-value and
36
+ prioritizes fresh reach. This is the upper-funnel optimization shape
37
+ (broadcast TV, CTV, social brand campaigns, audio) that's distinct from
38
+ the performance / conversion-tracking shapes.
39
+
40
+ Discriminating behaviors this scenario verifies:
41
+ 1. create_media_buy with metric: reach, a reach_unit value declared in
42
+ the product's metric_optimization.supported_reach_units, and an optional
43
+ target_frequency band is accepted.
44
+ 2. create_media_buy with a reach_unit value NOT in the product's
45
+ supported_reach_units is rejected with INVALID_REQUEST. error.field
46
+ points at the offending reach_unit path. Silent acceptance with
47
+ unit-coercion would create cross-platform comparison errors because
48
+ reach in cookies is not reach in households.
49
+ 3. get_media_buy_delivery surfaces totals.reach (the unique-count value)
50
+ and totals.frequency (average exposures per reach unit over the
51
+ declared reach_window). reach_unit on delivery aligns with the
52
+ reach_unit declared on the optimization goal — the unit declaration is
53
+ load-bearing for cross-platform comparison.
54
+
55
+ Per-window reach-window breakdown (cumulative vs period vs rolling
56
+ semantics declared in delivery-metrics.json's reach_window block) is
57
+ asserted in dedicated follow-up phases after the base reach delivery
58
+ check. The omission case remains an advisory because reach_window is
59
+ schema-valid to omit, but buyers must treat omitted-window reach rows
60
+ as unsafe to sum or average across reporting periods.
61
+
62
+ agent:
63
+ interaction_model: media_buy_seller
64
+ capabilities:
65
+ - sells_media
66
+ - supports_non_guaranteed
67
+ examples:
68
+ - "Broadcast TV / CTV sellers optimizing for unique households"
69
+ - "Social platforms with reach optimization (Meta Reach & Frequency, TikTok Reach)"
70
+ - "Audio sellers optimizing for unique listeners"
71
+ - "OOH / DOOH sellers optimizing for unique passers-by"
72
+
73
+ caller:
74
+ role: buyer_agent
75
+ example: "Pinnacle Agency (buyer)"
76
+
77
+ prerequisites:
78
+ description: |
79
+ The seller must implement comply_test_controller with simulate_delivery
80
+ (already required for delivery_reporting). simulate_delivery injects
81
+ impressions, reach, and frequency values so get_media_buy_delivery has
82
+ something to surface for the reach-optimized buy.
83
+
84
+ The buyer fixture supplies a brand, an operator, and reach targeting
85
+ parameters sufficient to exercise the reach_unit binding contract and
86
+ the unsupported-unit rejection.
87
+ test_kit: "test-kits/acme-outdoor.yaml"
88
+ controller_seeding: true
89
+
90
+ fixtures:
91
+ products:
92
+ - product_id: "reach_ctv_q2"
93
+ delivery_type: "non_guaranteed"
94
+ channels: ["video"]
95
+ format_ids:
96
+ - id: "video_30s"
97
+ metric_optimization:
98
+ supported_metrics: ["reach"]
99
+ supported_reach_units: ["households", "individuals"]
100
+ pricing_options:
101
+ - product_id: "reach_ctv_q2"
102
+ pricing_option_id: "auction_cpm"
103
+ pricing_model: "cpm"
104
+ currency: "USD"
105
+ floor_price: 18.00
106
+
107
+ phases:
108
+
109
+ - id: setup
110
+ title: "Establish account and discover products"
111
+ steps:
112
+ - id: sync_accounts
113
+ title: "Establish account"
114
+ task: sync_accounts
115
+ schema_ref: "account/sync-accounts-request.json"
116
+ response_schema_ref: "account/sync-accounts-response.json"
117
+ doc_ref: "/accounts/tasks/sync_accounts"
118
+ stateful: true
119
+ expected: |
120
+ Return the account with account_id and status active.
121
+ sample_request:
122
+ accounts:
123
+ - brand:
124
+ domain: "acmeoutdoor.example"
125
+ operator: "pinnacle-agency.example"
126
+ billing: "operator"
127
+ idempotency_key: "$generate:uuid_v4#reach_buy_flow_setup_sync_accounts"
128
+ validations:
129
+ - check: response_schema
130
+ description: "Response matches sync-accounts-response.json schema"
131
+ - check: field_present
132
+ path: "accounts[0].account_id"
133
+ description: "Account has a platform-assigned ID"
134
+
135
+ - id: get_products_for_reach
136
+ title: "Discover products that support reach optimization"
137
+ task: get_products
138
+ schema_ref: "media-buy/get-products-request.json"
139
+ response_schema_ref: "media-buy/get-products-response.json"
140
+ doc_ref: "/media-buy/task-reference/get_products"
141
+ stateful: false
142
+ expected: |
143
+ Return at least one product whose metric_optimization capabilities
144
+ include reach as an optimization metric and declare
145
+ supported_reach_units. The buyer selects a unit from that list for
146
+ the create_media_buy call below.
147
+ sample_request:
148
+ buying_mode: "brief"
149
+ brief: "CTV video, US adults 25-54. Q2 brand flight ~$50K, optimizing for unique household reach with a target frequency cap."
150
+ account:
151
+ brand:
152
+ domain: "acmeoutdoor.example"
153
+ operator: "pinnacle-agency.example"
154
+ validations:
155
+ - check: response_schema
156
+ description: "Response matches get-products-response.json schema"
157
+ - check: field_present
158
+ path: "products"
159
+ description: "Response contains products"
160
+
161
+ - id: create_reach_buy
162
+ title: "Create a reach-optimized buy with a supported reach_unit"
163
+ narrative: |
164
+ The buyer creates a media buy whose package carries a metric-kind
165
+ optimization_goal with metric: reach, reach_unit: households (explicitly
166
+ declared in the product fixture's supported_reach_units), and
167
+ a target_frequency band of 1–3 over a 7-day rolling window. The seller
168
+ should accept and return a media_buy_id used for subsequent delivery
169
+ checks.
170
+
171
+ steps:
172
+ - id: create_media_buy_reach
173
+ title: "Create media buy with metric-kind reach goal"
174
+ task: create_media_buy
175
+ schema_ref: "media-buy/create-media-buy-request.json"
176
+ response_schema_ref: "media-buy/create-media-buy-response.json"
177
+ doc_ref: "/media-buy/task-reference/create_media_buy"
178
+ stateful: true
179
+ expected: |
180
+ Create the media buy. Response carries a media_buy_id used for
181
+ subsequent get_media_buy_delivery calls.
182
+ sample_request:
183
+ brand:
184
+ domain: "acmeoutdoor.example"
185
+ account:
186
+ brand:
187
+ domain: "acmeoutdoor.example"
188
+ operator: "pinnacle-agency.example"
189
+ start_time: "2026-06-01T00:00:00Z"
190
+ end_time: "2026-06-30T23:59:59Z"
191
+ packages:
192
+ - product_id: "reach_ctv_q2"
193
+ budget: 50000
194
+ pricing_option_id: "auction_cpm"
195
+ optimization_goals:
196
+ - kind: "metric"
197
+ metric: "reach"
198
+ reach_unit: "households"
199
+ target_frequency:
200
+ window:
201
+ interval: 7
202
+ unit: "days"
203
+ min: 1
204
+ max: 3
205
+ priority: 1
206
+ idempotency_key: "$generate:uuid_v4#reach_buy_flow_create_reach_buy_create_media_buy"
207
+ context_outputs:
208
+ - name: media_buy_id
209
+ path: "media_buy_id"
210
+ validations:
211
+ - check: response_schema
212
+ description: "Response matches create-media-buy-response.json schema"
213
+ - check: field_present
214
+ path: "media_buy_id"
215
+ description: "Media buy has a platform-assigned ID"
216
+
217
+ - id: rejection_unsupported_reach_unit
218
+ title: "Reject a reach goal whose reach_unit is not in supported_reach_units"
219
+ narrative: |
220
+ Silent acceptance of a reach_unit value not declared in the product's
221
+ metric_optimization.supported_reach_units is a façade — the seller
222
+ either coerces the unit (creating cross-platform comparison errors)
223
+ or runs without a unit (delivery reach values become uncomparable).
224
+ The seller MUST reject with INVALID_REQUEST and set error.field to the
225
+ offending reach_unit path. Same shape as the unbound event_source_id
226
+ rejection in performance_buy_flow and the unbound audience_id
227
+ rejection in audience_buy_flow.
228
+
229
+ "devices" is chosen because it is a valid spec-defined reach_unit enum
230
+ value that the reach_ctv_q2 product fixture deliberately omits from
231
+ supported_reach_units (which declares only "households" and
232
+ "individuals"). Using a valid enum value satisfies
233
+ negative_path: payload_well_formed (the request is schema-valid),
234
+ while the fixture exclusion ensures the rejection must come from the
235
+ seller's capability-checking logic rather than schema validation.
236
+ Sellers that silently accept "devices" are demonstrating they don't
237
+ validate reach_unit against their product capability declarations.
238
+
239
+ steps:
240
+ - id: create_media_buy_with_unsupported_reach_unit
241
+ title: "Submit a reach goal whose reach_unit is not in supported_reach_units"
242
+ task: create_media_buy
243
+ schema_ref: "media-buy/create-media-buy-request.json"
244
+ response_schema_ref: "media-buy/create-media-buy-response.json"
245
+ doc_ref: "/media-buy/task-reference/create_media_buy"
246
+ expect_error: true
247
+ negative_path: payload_well_formed
248
+ stateful: false
249
+ expected: |
250
+ Reject with INVALID_REQUEST. error.field points at the offending
251
+ reach_unit path. media_buy_id is not allocated.
252
+ sample_request:
253
+ brand:
254
+ domain: "acmeoutdoor.example"
255
+ account:
256
+ brand:
257
+ domain: "acmeoutdoor.example"
258
+ operator: "pinnacle-agency.example"
259
+ start_time: "2026-06-01T00:00:00Z"
260
+ end_time: "2026-06-30T23:59:59Z"
261
+ packages:
262
+ - product_id: "reach_ctv_q2"
263
+ budget: 10000
264
+ pricing_option_id: "auction_cpm"
265
+ optimization_goals:
266
+ - kind: "metric"
267
+ metric: "reach"
268
+ reach_unit: "devices"
269
+ idempotency_key: "$generate:uuid_v4#reach_buy_flow_rejection_unsupported_reach_unit"
270
+ validations:
271
+ - check: error_code
272
+ allowed_values: ["INVALID_REQUEST"]
273
+ description: "Seller rejects the unsupported reach_unit with INVALID_REQUEST"
274
+ - check: field_value
275
+ path: "errors[0].field"
276
+ value: "packages[0].optimization_goals[0].reach_unit"
277
+ description: "Error.field points at the offending reach_unit path (core/error.json defines field as deterministic JSONPath-lite — literal equality)"
278
+
279
+ - id: reach_delivery
280
+ title: "Delivery reporting carries reach and frequency"
281
+ narrative: |
282
+ The discriminating assertion of the reach buy mode: delivery reporting
283
+ MUST surface reach and frequency at the buy level — not just
284
+ impressions and spend. The runner injects simulated impressions,
285
+ reach, and frequency via the test controller, then verifies
286
+ get_media_buy_delivery returns reach + frequency on totals.
287
+
288
+ Per-row reach_window semantics (cumulative vs period vs rolling — see
289
+ delivery-metrics.json) are deliberately not asserted at this layer.
290
+ The scenario asserts the discriminating fields are present at the
291
+ buy level; reach_window correctness is asserted by the schema
292
+ validation alongside the totals payload.
293
+
294
+ steps:
295
+ - id: simulate_reach_delivery
296
+ title: "Inject impressions + reach + frequency"
297
+ task: comply_test_controller
298
+ requires_tool: comply_test_controller
299
+ stateful: true
300
+ expected: |
301
+ The test controller acknowledges the simulated delivery.
302
+ sample_request:
303
+ account:
304
+ sandbox: true
305
+ scenario: "simulate_delivery"
306
+ params:
307
+ media_buy_id: "$context.media_buy_id"
308
+ impressions: 750000
309
+ reach: 250000
310
+ frequency: 3.0
311
+ reported_spend:
312
+ amount: 14000.00
313
+ currency: "USD"
314
+ validations:
315
+ - check: field_value
316
+ path: "success"
317
+ allowed_values: [true]
318
+ description: "Delivery simulation succeeds"
319
+
320
+ - id: get_reach_delivery
321
+ title: "Get delivery and verify reach + frequency metrics"
322
+ task: get_media_buy_delivery
323
+ schema_ref: "media-buy/get-media-buy-delivery-request.json"
324
+ response_schema_ref: "media-buy/get-media-buy-delivery-response.json"
325
+ doc_ref: "/media-buy/task-reference/get_media_buy_delivery"
326
+ stateful: true
327
+ expected: |
328
+ Delivery response includes totals.reach (the unique-count value
329
+ in the declared reach_unit) and totals.frequency (average
330
+ exposures per reach unit over the declared reach_window). These
331
+ two fields are the discriminating output of reach optimization
332
+ and are required on reach-optimized buys.
333
+ sample_request:
334
+ account:
335
+ brand:
336
+ domain: "acmeoutdoor.example"
337
+ operator: "pinnacle-agency.example"
338
+ media_buy_ids:
339
+ - "$context.media_buy_id"
340
+ include_package_daily_breakdown: true
341
+ validations:
342
+ - check: response_schema
343
+ description: "Response matches get-media-buy-delivery-response.json schema"
344
+ - check: field_present
345
+ path: "media_buy_deliveries[0].totals.reach"
346
+ description: "Reach count surfaced at the buy level (in the declared reach_unit)"
347
+ - check: field_present
348
+ path: "media_buy_deliveries[0].totals.frequency"
349
+ description: "Frequency surfaced at the buy level — average exposures per reach unit"
350
+
351
+ - id: reach_window_cumulative
352
+ title: "Delivery row carries reach_window.kind cumulative"
353
+ narrative: |
354
+ A cumulative reach_window declares that the reach value represents
355
+ unique audiences since campaign start. Each successive delivery row
356
+ supersedes the prior one — buyers MUST NOT sum cumulative rows to
357
+ compute campaign reach, since each later row already includes all
358
+ prior audiences. No period field is required for cumulative.
359
+
360
+ This phase injects a delivery row with reach_window.kind cumulative
361
+ and verifies that the seller surfaces the window semantics on the
362
+ delivery response. Sellers that omit reach_window on a reach-optimized
363
+ buy force buyers to guess the window — creating silent double-counting
364
+ when buyers aggregate across reporting periods.
365
+
366
+ steps:
367
+ - id: create_media_buy_cumulative_reach
368
+ title: "Create media buy for cumulative reach-window delivery"
369
+ task: create_media_buy
370
+ schema_ref: "media-buy/create-media-buy-request.json"
371
+ response_schema_ref: "media-buy/create-media-buy-response.json"
372
+ doc_ref: "/media-buy/task-reference/create_media_buy"
373
+ stateful: true
374
+ expected: |
375
+ Create a fresh reach-optimized media buy so the cumulative
376
+ reach_window assertion is not contaminated by prior delivery
377
+ simulation calls.
378
+ sample_request:
379
+ brand:
380
+ domain: "acmeoutdoor.example"
381
+ account:
382
+ brand:
383
+ domain: "acmeoutdoor.example"
384
+ operator: "pinnacle-agency.example"
385
+ start_time: "2026-06-01T00:00:00Z"
386
+ end_time: "2026-06-30T23:59:59Z"
387
+ packages:
388
+ - product_id: "reach_ctv_q2"
389
+ budget: 10000
390
+ pricing_option_id: "auction_cpm"
391
+ optimization_goals:
392
+ - kind: "metric"
393
+ metric: "reach"
394
+ reach_unit: "households"
395
+ priority: 1
396
+ idempotency_key: "$generate:uuid_v4#reach_buy_flow_reach_window_cumulative_create_media_buy"
397
+ context_outputs:
398
+ - name: cumulative_media_buy_id
399
+ path: "media_buy_id"
400
+ validations:
401
+ - check: response_schema
402
+ description: "Response matches create-media-buy-response.json schema"
403
+ - check: field_present
404
+ path: "media_buy_id"
405
+ description: "Media buy has a platform-assigned ID"
406
+
407
+ - id: simulate_cumulative_reach
408
+ title: "Inject delivery with cumulative reach_window"
409
+ task: comply_test_controller
410
+ requires_tool: comply_test_controller
411
+ stateful: true
412
+ expected: |
413
+ The test controller acknowledges the simulated delivery with a
414
+ cumulative reach_window.
415
+ sample_request:
416
+ account:
417
+ sandbox: true
418
+ scenario: "simulate_delivery"
419
+ params:
420
+ media_buy_id: "$context.cumulative_media_buy_id"
421
+ impressions: 1200000
422
+ reach: 400000
423
+ frequency: 3.0
424
+ reach_window:
425
+ kind: "cumulative"
426
+ reported_spend:
427
+ amount: 22000.00
428
+ currency: "USD"
429
+ validations:
430
+ - check: field_value
431
+ path: "success"
432
+ allowed_values: [true]
433
+ description: "Delivery simulation with cumulative reach_window succeeds"
434
+
435
+ - id: get_cumulative_reach_delivery
436
+ title: "Verify reach_window.kind cumulative on delivery row"
437
+ task: get_media_buy_delivery
438
+ schema_ref: "media-buy/get-media-buy-delivery-request.json"
439
+ response_schema_ref: "media-buy/get-media-buy-delivery-response.json"
440
+ doc_ref: "/media-buy/task-reference/get_media_buy_delivery"
441
+ stateful: true
442
+ expected: |
443
+ Delivery row carries reach_window with kind: cumulative. No period
444
+ field is expected — cumulative reach is campaign-to-date and the
445
+ window is implicit. The row's reach value is the total unique count
446
+ since campaign start; each subsequent cumulative row supersedes
447
+ this one.
448
+ sample_request:
449
+ account:
450
+ brand:
451
+ domain: "acmeoutdoor.example"
452
+ operator: "pinnacle-agency.example"
453
+ media_buy_ids:
454
+ - "$context.cumulative_media_buy_id"
455
+ include_package_daily_breakdown: true
456
+ validations:
457
+ - check: response_schema
458
+ description: "Response matches get-media-buy-delivery-response.json schema"
459
+ - check: field_present
460
+ path: "media_buy_deliveries[0].totals.reach_window"
461
+ description: "Seller populates reach_window on the delivery row"
462
+ - check: field_value
463
+ path: "media_buy_deliveries[0].totals.reach_window.kind"
464
+ allowed_values: ["cumulative"]
465
+ description: "reach_window.kind is cumulative as injected"
466
+
467
+ - id: reach_window_period
468
+ title: "Delivery row carries reach_window.kind period with period field"
469
+ narrative: |
470
+ A period reach_window declares that the reach value covers a single
471
+ non-overlapping reporting period (e.g., one calendar day). Adjacent
472
+ period rows do not share audiences by construction — the same person
473
+ MAY appear across multiple periods — so buyers MUST NOT sum period
474
+ rows to compute campaign reach. The period field is REQUIRED for
475
+ kind: period and declares the snapshot length (e.g., 1 day).
476
+
477
+ This phase injects a delivery row with reach_window.kind period and
478
+ period: {interval:1, unit:"days"} and verifies the seller surfaces
479
+ both the kind and the period duration on the delivery response.
480
+
481
+ steps:
482
+ - id: create_media_buy_period_reach
483
+ title: "Create media buy for period reach-window delivery"
484
+ task: create_media_buy
485
+ schema_ref: "media-buy/create-media-buy-request.json"
486
+ response_schema_ref: "media-buy/create-media-buy-response.json"
487
+ doc_ref: "/media-buy/task-reference/create_media_buy"
488
+ stateful: true
489
+ expected: |
490
+ Create a fresh reach-optimized media buy so the period
491
+ reach_window assertion is isolated from cumulative or rolling
492
+ delivery simulations.
493
+ sample_request:
494
+ brand:
495
+ domain: "acmeoutdoor.example"
496
+ account:
497
+ brand:
498
+ domain: "acmeoutdoor.example"
499
+ operator: "pinnacle-agency.example"
500
+ start_time: "2026-06-01T00:00:00Z"
501
+ end_time: "2026-06-30T23:59:59Z"
502
+ packages:
503
+ - product_id: "reach_ctv_q2"
504
+ budget: 10000
505
+ pricing_option_id: "auction_cpm"
506
+ optimization_goals:
507
+ - kind: "metric"
508
+ metric: "reach"
509
+ reach_unit: "households"
510
+ priority: 1
511
+ idempotency_key: "$generate:uuid_v4#reach_buy_flow_reach_window_period_create_media_buy"
512
+ context_outputs:
513
+ - name: period_media_buy_id
514
+ path: "media_buy_id"
515
+ validations:
516
+ - check: response_schema
517
+ description: "Response matches create-media-buy-response.json schema"
518
+ - check: field_present
519
+ path: "media_buy_id"
520
+ description: "Media buy has a platform-assigned ID"
521
+
522
+ - id: simulate_period_reach
523
+ title: "Inject delivery with period reach_window"
524
+ task: comply_test_controller
525
+ requires_tool: comply_test_controller
526
+ stateful: true
527
+ expected: |
528
+ The test controller acknowledges the simulated delivery with a
529
+ period reach_window carrying a 1-day period.
530
+ sample_request:
531
+ account:
532
+ sandbox: true
533
+ scenario: "simulate_delivery"
534
+ params:
535
+ media_buy_id: "$context.period_media_buy_id"
536
+ impressions: 175000
537
+ reach: 140000
538
+ frequency: 1.25
539
+ reach_window:
540
+ kind: "period"
541
+ period:
542
+ interval: 1
543
+ unit: "days"
544
+ reported_spend:
545
+ amount: 3200.00
546
+ currency: "USD"
547
+ validations:
548
+ - check: field_value
549
+ path: "success"
550
+ allowed_values: [true]
551
+ description: "Delivery simulation with period reach_window succeeds"
552
+
553
+ - id: get_period_reach_delivery
554
+ title: "Verify reach_window.kind period and period duration on delivery row"
555
+ task: get_media_buy_delivery
556
+ schema_ref: "media-buy/get-media-buy-delivery-request.json"
557
+ response_schema_ref: "media-buy/get-media-buy-delivery-response.json"
558
+ doc_ref: "/media-buy/task-reference/get_media_buy_delivery"
559
+ stateful: true
560
+ expected: |
561
+ Delivery row carries reach_window with kind: period and a period
562
+ duration of 1 day. The period field is required for kind: period —
563
+ its absence would leave buyers unable to determine the snapshot
564
+ length, making cross-period reach comparisons unreliable.
565
+ sample_request:
566
+ account:
567
+ brand:
568
+ domain: "acmeoutdoor.example"
569
+ operator: "pinnacle-agency.example"
570
+ media_buy_ids:
571
+ - "$context.period_media_buy_id"
572
+ include_package_daily_breakdown: true
573
+ validations:
574
+ - check: response_schema
575
+ description: "Response matches get-media-buy-delivery-response.json schema"
576
+ - check: field_present
577
+ path: "media_buy_deliveries[0].totals.reach_window"
578
+ description: "Seller populates reach_window on the delivery row"
579
+ - check: field_value
580
+ path: "media_buy_deliveries[0].totals.reach_window.kind"
581
+ allowed_values: ["period"]
582
+ description: "reach_window.kind is period as injected"
583
+ - check: field_present
584
+ path: "media_buy_deliveries[0].totals.reach_window.period"
585
+ description: "period field is present — required for kind: period"
586
+
587
+ - id: reach_window_rolling
588
+ title: "Delivery row carries reach_window.kind rolling with period field"
589
+ narrative: |
590
+ A rolling reach_window declares that the reach value covers a trailing
591
+ window ending at the row's reporting timestamp (e.g., trailing-7-day
592
+ unique reach). Adjacent rolling rows overlap — each row stands alone
593
+ and MUST NOT be summed; summing rolling rows double-counts audiences
594
+ seen in multiple windows. The period field is REQUIRED for kind: rolling
595
+ and declares the trailing-window length (e.g., 7 days).
596
+
597
+ This phase injects a delivery row with reach_window.kind rolling and
598
+ period: {interval:7, unit:"days"} and verifies the seller surfaces
599
+ both the kind and the window duration. A trailing-7-day reach window
600
+ is the canonical frequency-cap companion — buyers reading this value
601
+ know exactly which audiences are within the 7-day dedup window.
602
+
603
+ steps:
604
+ - id: create_media_buy_rolling_reach
605
+ title: "Create media buy for rolling reach-window delivery"
606
+ task: create_media_buy
607
+ schema_ref: "media-buy/create-media-buy-request.json"
608
+ response_schema_ref: "media-buy/create-media-buy-response.json"
609
+ doc_ref: "/media-buy/task-reference/create_media_buy"
610
+ stateful: true
611
+ expected: |
612
+ Create a fresh reach-optimized media buy so the rolling
613
+ reach_window assertion is isolated from period or cumulative
614
+ delivery simulations.
615
+ sample_request:
616
+ brand:
617
+ domain: "acmeoutdoor.example"
618
+ account:
619
+ brand:
620
+ domain: "acmeoutdoor.example"
621
+ operator: "pinnacle-agency.example"
622
+ start_time: "2026-06-01T00:00:00Z"
623
+ end_time: "2026-06-30T23:59:59Z"
624
+ packages:
625
+ - product_id: "reach_ctv_q2"
626
+ budget: 10000
627
+ pricing_option_id: "auction_cpm"
628
+ optimization_goals:
629
+ - kind: "metric"
630
+ metric: "reach"
631
+ reach_unit: "households"
632
+ priority: 1
633
+ idempotency_key: "$generate:uuid_v4#reach_buy_flow_reach_window_rolling_create_media_buy"
634
+ context_outputs:
635
+ - name: rolling_media_buy_id
636
+ path: "media_buy_id"
637
+ validations:
638
+ - check: response_schema
639
+ description: "Response matches create-media-buy-response.json schema"
640
+ - check: field_present
641
+ path: "media_buy_id"
642
+ description: "Media buy has a platform-assigned ID"
643
+
644
+ - id: simulate_rolling_reach
645
+ title: "Inject delivery with rolling reach_window"
646
+ task: comply_test_controller
647
+ requires_tool: comply_test_controller
648
+ stateful: true
649
+ expected: |
650
+ The test controller acknowledges the simulated delivery with a
651
+ rolling reach_window carrying a 7-day trailing window.
652
+ sample_request:
653
+ account:
654
+ sandbox: true
655
+ scenario: "simulate_delivery"
656
+ params:
657
+ media_buy_id: "$context.rolling_media_buy_id"
658
+ impressions: 450000
659
+ reach: 210000
660
+ frequency: 2.1
661
+ reach_window:
662
+ kind: "rolling"
663
+ period:
664
+ interval: 7
665
+ unit: "days"
666
+ reported_spend:
667
+ amount: 8500.00
668
+ currency: "USD"
669
+ validations:
670
+ - check: field_value
671
+ path: "success"
672
+ allowed_values: [true]
673
+ description: "Delivery simulation with rolling reach_window succeeds"
674
+
675
+ - id: get_rolling_reach_delivery
676
+ title: "Verify reach_window.kind rolling and period duration on delivery row"
677
+ task: get_media_buy_delivery
678
+ schema_ref: "media-buy/get-media-buy-delivery-request.json"
679
+ response_schema_ref: "media-buy/get-media-buy-delivery-response.json"
680
+ doc_ref: "/media-buy/task-reference/get_media_buy_delivery"
681
+ stateful: true
682
+ expected: |
683
+ Delivery row carries reach_window with kind: rolling and a period
684
+ of 7 days. The period field is required for kind: rolling — without
685
+ it, buyers cannot determine the trailing window length and cannot
686
+ safely use the reach value for frequency optimization.
687
+ sample_request:
688
+ account:
689
+ brand:
690
+ domain: "acmeoutdoor.example"
691
+ operator: "pinnacle-agency.example"
692
+ media_buy_ids:
693
+ - "$context.rolling_media_buy_id"
694
+ include_package_daily_breakdown: true
695
+ validations:
696
+ - check: response_schema
697
+ description: "Response matches get-media-buy-delivery-response.json schema"
698
+ - check: field_present
699
+ path: "media_buy_deliveries[0].totals.reach_window"
700
+ description: "Seller populates reach_window on the delivery row"
701
+ - check: field_value
702
+ path: "media_buy_deliveries[0].totals.reach_window.kind"
703
+ allowed_values: ["rolling"]
704
+ description: "reach_window.kind is rolling as injected"
705
+ - check: field_present
706
+ path: "media_buy_deliveries[0].totals.reach_window.period"
707
+ description: "period field is present — required for kind: rolling"
708
+
709
+ - id: reach_window_absent_advisory
710
+ title: "Advisory: delivery row with reach but no reach_window is schema-valid but sum-unsafe"
711
+ narrative: |
712
+ Reach/frequency rows with `reach_window` omitted are schema-valid —
713
+ the schema marks the field SHOULD (not MUST) populate it. However,
714
+ buyers MUST NOT treat omitted-window rows as sum-safe: the value may
715
+ be a daily snapshot, a cumulative total, or something else. Summing
716
+ reach values across rows without a declared window silently
717
+ double-counts audiences.
718
+
719
+ This phase simulates a delivery row without reach_window and issues
720
+ an advisory (not a conformance fail) when the seller returns reach
721
+ without the window declaration. Advisory grading: sellers that do
722
+ not populate reach_window on reach-optimized buys are warned but
723
+ not failed — the omission is spec-valid. Sellers should treat this
724
+ advisory as a signal to add reach_window to their delivery responses
725
+ for 3.1+ buyers.
726
+
727
+ steps:
728
+ - id: create_media_buy_reach_no_window
729
+ title: "Create media buy for omitted reach-window advisory"
730
+ task: create_media_buy
731
+ schema_ref: "media-buy/create-media-buy-request.json"
732
+ response_schema_ref: "media-buy/create-media-buy-response.json"
733
+ doc_ref: "/media-buy/task-reference/create_media_buy"
734
+ stateful: true
735
+ expected: |
736
+ Create a fresh reach-optimized media buy so the omitted-window
737
+ advisory observes a delivery row whose reach_window was never
738
+ populated by earlier simulation calls.
739
+ sample_request:
740
+ brand:
741
+ domain: "acmeoutdoor.example"
742
+ account:
743
+ brand:
744
+ domain: "acmeoutdoor.example"
745
+ operator: "pinnacle-agency.example"
746
+ start_time: "2026-06-01T00:00:00Z"
747
+ end_time: "2026-06-30T23:59:59Z"
748
+ packages:
749
+ - product_id: "reach_ctv_q2"
750
+ budget: 10000
751
+ pricing_option_id: "auction_cpm"
752
+ optimization_goals:
753
+ - kind: "metric"
754
+ metric: "reach"
755
+ reach_unit: "households"
756
+ priority: 1
757
+ idempotency_key: "$generate:uuid_v4#reach_buy_flow_reach_window_absent_create_media_buy"
758
+ context_outputs:
759
+ - name: no_window_media_buy_id
760
+ path: "media_buy_id"
761
+ validations:
762
+ - check: response_schema
763
+ description: "Response matches create-media-buy-response.json schema"
764
+ - check: field_present
765
+ path: "media_buy_id"
766
+ description: "Media buy has a platform-assigned ID"
767
+
768
+ - id: simulate_reach_no_window
769
+ title: "Inject delivery with reach but no reach_window"
770
+ task: comply_test_controller
771
+ requires_tool: comply_test_controller
772
+ stateful: true
773
+ expected: |
774
+ The test controller injects a delivery row with reach and frequency
775
+ but no reach_window. This is a valid operation — the scenario
776
+ tests the advisory behavior, not a rejection.
777
+ sample_request:
778
+ account:
779
+ sandbox: true
780
+ scenario: "simulate_delivery"
781
+ params:
782
+ media_buy_id: "$context.no_window_media_buy_id"
783
+ impressions: 300000
784
+ reach: 180000
785
+ frequency: 1.67
786
+ reported_spend:
787
+ amount: 5800.00
788
+ currency: "USD"
789
+ validations:
790
+ - check: field_value
791
+ path: "success"
792
+ allowed_values: [true]
793
+ description: "Delivery simulation without reach_window succeeds"
794
+
795
+ - id: get_delivery_reach_no_window
796
+ title: "Verify advisory when reach is present but reach_window is absent"
797
+ task: get_media_buy_delivery
798
+ schema_ref: "media-buy/get-media-buy-delivery-request.json"
799
+ response_schema_ref: "media-buy/get-media-buy-delivery-response.json"
800
+ doc_ref: "/media-buy/task-reference/get_media_buy_delivery"
801
+ stateful: true
802
+ expected: |
803
+ Delivery row contains reach without reach_window. Schema-valid —
804
+ this is a SHOULD, not a MUST. The advisory validation below fires
805
+ when reach_window is absent, surfacing the omission for the seller
806
+ to address in future delivery responses.
807
+ sample_request:
808
+ account:
809
+ brand:
810
+ domain: "acmeoutdoor.example"
811
+ operator: "pinnacle-agency.example"
812
+ media_buy_ids:
813
+ - "$context.no_window_media_buy_id"
814
+ include_package_daily_breakdown: true
815
+ validations:
816
+ - check: response_schema
817
+ description: "Response matches get-media-buy-delivery-response.json schema"
818
+ - check: field_present
819
+ path: "media_buy_deliveries[0].totals.reach_window"
820
+ severity: advisory
821
+ permanent_advisory:
822
+ reason: "delivery-metrics.json declares reach_window as SHOULD (not MUST) for 3.1. Omitting it is schema-valid but prevents buyers from determining the measurement window, making the reach value sum-unsafe across reporting periods. Sellers on the 3.1 track should populate reach_window on all reach-bearing delivery rows. This advisory is permanent: promotion to required is a 4.0 breaking change and will be tracked separately."
823
+ description: "reach_window populated on reach-bearing delivery row (advisory — SHOULD per 3.1 spec)"