From 7668b506e65bcbda3bdd6dfd51c4b036fb64b42e Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Fri, 8 May 2026 09:45:07 -0400 Subject: [PATCH] fix(compliance): UUID-aliased idempotency_keys across remaining storyboard scenarios Extends #4218 (measurement_terms_rejected) to the rest of the suite. 15 storyboard steps across 9 scenarios still shipped hardcoded `idempotency_key` literals on state-mutating tasks. Same root cause: the runner's dynamic `start_time` substitution shifts dates forward each run, producing same key + different canonical body and arming IDEMPOTENCY_CONFLICT (or replaying stale cached payloads when seller emit shape changes between runs). Switch every remaining literal to `$generate:uuid_v4#_`. Closes #4230. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../storyboard-uuid-aliased-idempotency-keys.md | 11 +++++++++++ .../scenarios/creative_fate_after_cancellation.yaml | 10 +++++----- .../media-buy/scenarios/governance_approved.yaml | 2 +- .../media-buy/scenarios/governance_conditions.yaml | 2 +- .../media-buy/scenarios/governance_denied.yaml | 2 +- .../scenarios/governance_denied_recovery.yaml | 6 +++--- .../media-buy/scenarios/invalid_transitions.yaml | 2 +- .../media-buy/scenarios/inventory_list_no_match.yaml | 2 +- .../media-buy/scenarios/inventory_list_targeting.yaml | 2 +- .../scenarios/pending_creatives_to_start.yaml | 2 +- 10 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 .changeset/storyboard-uuid-aliased-idempotency-keys.md diff --git a/.changeset/storyboard-uuid-aliased-idempotency-keys.md b/.changeset/storyboard-uuid-aliased-idempotency-keys.md new file mode 100644 index 0000000000..d9150b2b8a --- /dev/null +++ b/.changeset/storyboard-uuid-aliased-idempotency-keys.md @@ -0,0 +1,11 @@ +--- +"adcontextprotocol": patch +--- + +fix(compliance): UUID-aliased idempotency_keys across remaining storyboard scenarios + +Extends the [#4218](https://github.com/adcontextprotocol/adcp/pull/4218) precedent (`measurement_terms_rejected`) to the rest of the suite. 15 storyboard steps across 9 scenarios still shipped hardcoded `idempotency_key` literals on state-mutating tasks (`create_media_buy`, `sync_creatives`, `sync_plans`, `update_media_buy`). The runner shifts dynamic `start_time` substitutions forward to keep buys future-dated, so against a long-running seller deployment those static keys collide cross-run with the same key + a different canonical body, arming the spec-mandated `IDEMPOTENCY_CONFLICT` (or, when the seller's emit shape changed between runs, replaying a now-spec-non-compliant cached payload — the production failure mode that surfaced this). + +Switch every remaining literal to `$generate:uuid_v4#_` so each storyboard run mints fresh keys and never collides with stale cached state. Affected scenarios: `creative_fate_after_cancellation` (5), `governance_approved`, `governance_conditions`, `governance_denied`, `governance_denied_recovery` (3), `invalid_transitions`, `inventory_list_no_match`, `inventory_list_targeting`, `pending_creatives_to_start`. + +Closes #4230. diff --git a/static/compliance/source/protocols/media-buy/scenarios/creative_fate_after_cancellation.yaml b/static/compliance/source/protocols/media-buy/scenarios/creative_fate_after_cancellation.yaml index 4c9fe67900..92d747dcd3 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/creative_fate_after_cancellation.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/creative_fate_after_cancellation.yaml @@ -114,7 +114,7 @@ phases: brand: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" - idempotency_key: "creative-fate-setup-create-buy-v1" + idempotency_key: "$generate:uuid_v4#creative_fate_after_cancellation_create_buy" start_time: "2026-09-01T00:00:00Z" end_time: "2026-09-30T23:59:59Z" packages: @@ -175,7 +175,7 @@ phases: assignments: - creative_id: "acme_reuse_banner_001" package_id: "$context.package_id" - idempotency_key: "creative-fate-setup-sync-v1" + idempotency_key: "$generate:uuid_v4#creative_fate_after_cancellation_sync_creative_with_assignment" context: correlation_id: "creative_fate--sync_creative_with_assignment" context_outputs: @@ -257,7 +257,7 @@ phases: media_buy_id: "$context.media_buy_id" canceled: true cancellation_reason: "Creative-fate scenario: releasing assignment to verify library persistence." - idempotency_key: "creative-fate-cancel-v1" + idempotency_key: "$generate:uuid_v4#creative_fate_after_cancellation_update_media_buy_canceled" context: correlation_id: "creative_fate--update_media_buy_canceled" validations: @@ -340,7 +340,7 @@ phases: brand: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" - idempotency_key: "creative-fate-second-buy-v1" + idempotency_key: "$generate:uuid_v4#creative_fate_after_cancellation_create_second_buy" start_time: "2026-10-01T00:00:00Z" end_time: "2026-10-31T23:59:59Z" packages: @@ -406,7 +406,7 @@ phases: assignments: - creative_id: "acme_reuse_banner_001" package_id: "$context.second_package_id" - idempotency_key: "creative-fate-reassign-v1" + idempotency_key: "$generate:uuid_v4#creative_fate_after_cancellation_reassign_creative" context: correlation_id: "creative_fate--reassign_creative" validations: diff --git a/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml b/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml index d6c7396d39..7dbe654a98 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/governance_approved.yaml @@ -84,7 +84,7 @@ phases: expected: | The governance agent acknowledges the plan with a plan_id. sample_request: - idempotency_key: "comply-gov-approved-sync-plans-v1" + idempotency_key: "$generate:uuid_v4#governance_approved_sync_plans" plans: - plan_id: "comply-gov-approved-plan" brand: diff --git a/static/compliance/source/protocols/media-buy/scenarios/governance_conditions.yaml b/static/compliance/source/protocols/media-buy/scenarios/governance_conditions.yaml index f545f6a786..6752e9b168 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/governance_conditions.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/governance_conditions.yaml @@ -61,7 +61,7 @@ phases: expected: | The governance agent acknowledges the plan with a plan_id. sample_request: - idempotency_key: "comply-gov-conditions-sync-plans-v1" + idempotency_key: "$generate:uuid_v4#governance_conditions_sync_plans" plans: - plan_id: "comply-gov-conditions-plan" brand: diff --git a/static/compliance/source/protocols/media-buy/scenarios/governance_denied.yaml b/static/compliance/source/protocols/media-buy/scenarios/governance_denied.yaml index 7d068839a3..e0db21d9aa 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/governance_denied.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/governance_denied.yaml @@ -61,7 +61,7 @@ phases: expected: | The governance agent acknowledges the plan with a plan_id. sample_request: - idempotency_key: "comply-gov-denied-sync-plans-v1" + idempotency_key: "$generate:uuid_v4#governance_denied_sync_plans" plans: - plan_id: "comply-gov-denied-plan" brand: diff --git a/static/compliance/source/protocols/media-buy/scenarios/governance_denied_recovery.yaml b/static/compliance/source/protocols/media-buy/scenarios/governance_denied_recovery.yaml index 8f03d7bb14..bae4539c6a 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/governance_denied_recovery.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/governance_denied_recovery.yaml @@ -61,7 +61,7 @@ phases: expected: | The governance agent acknowledges the plan. sample_request: - idempotency_key: "comply-gov-recovery-sync-plans-v1" + idempotency_key: "$generate:uuid_v4#governance_denied_recovery_sync_plans" plans: - plan_id: "comply-gov-recovery-plan" brand: @@ -184,7 +184,7 @@ phases: brand: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" - idempotency_key: "gov-recovery-initial-denied-v1" + idempotency_key: "$generate:uuid_v4#governance_denied_recovery_create_media_buy_denied" start_time: "2026-04-01T00:00:00Z" end_time: "2026-06-30T23:59:59Z" packages: @@ -229,7 +229,7 @@ phases: brand: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" - idempotency_key: "gov-recovery-retry-approved-v1" + idempotency_key: "$generate:uuid_v4#governance_denied_recovery_create_media_buy_retry" start_time: "2026-04-01T00:00:00Z" end_time: "2026-06-30T23:59:59Z" packages: diff --git a/static/compliance/source/protocols/media-buy/scenarios/invalid_transitions.yaml b/static/compliance/source/protocols/media-buy/scenarios/invalid_transitions.yaml index 18d18f1e0e..112336abc5 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/invalid_transitions.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/invalid_transitions.yaml @@ -140,7 +140,7 @@ phases: brand: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" - idempotency_key: "invalid-transitions-setup-v1" + idempotency_key: "$generate:uuid_v4#invalid_transitions_create_buy" start_time: "2026-08-01T00:00:00Z" end_time: "2026-08-31T23:59:59Z" packages: diff --git a/static/compliance/source/protocols/media-buy/scenarios/inventory_list_no_match.yaml b/static/compliance/source/protocols/media-buy/scenarios/inventory_list_no_match.yaml index 8f5f4eb301..183da2616d 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/inventory_list_no_match.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/inventory_list_no_match.yaml @@ -116,7 +116,7 @@ phases: brand: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" - idempotency_key: "inventory-list-no-match-v1" + idempotency_key: "$generate:uuid_v4#inventory_list_no_match_create_buy" start_time: "2026-07-01T00:00:00Z" end_time: "2026-09-30T23:59:59Z" packages: diff --git a/static/compliance/source/protocols/media-buy/scenarios/inventory_list_targeting.yaml b/static/compliance/source/protocols/media-buy/scenarios/inventory_list_targeting.yaml index 9502c04168..d55802ce69 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/inventory_list_targeting.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/inventory_list_targeting.yaml @@ -114,7 +114,7 @@ phases: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" sandbox: true - idempotency_key: "inventory-list-targeting-create-v1" + idempotency_key: "$generate:uuid_v4#inventory_list_targeting_create_buy" start_time: "2026-07-01T00:00:00Z" end_time: "2026-09-30T23:59:59Z" packages: diff --git a/static/compliance/source/protocols/media-buy/scenarios/pending_creatives_to_start.yaml b/static/compliance/source/protocols/media-buy/scenarios/pending_creatives_to_start.yaml index 9bf1a639a0..b5dd9230db 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/pending_creatives_to_start.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/pending_creatives_to_start.yaml @@ -103,7 +103,7 @@ phases: brand: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" - idempotency_key: "pending-creatives-transition-v1" + idempotency_key: "$generate:uuid_v4#pending_creatives_to_start_create_buy_no_creatives" start_time: "2026-08-01T00:00:00Z" end_time: "2026-08-31T23:59:59Z" packages: