diff --git a/.changeset/measurement-terms-rejected-narrative.md b/.changeset/measurement-terms-rejected-narrative.md new file mode 100644 index 0000000000..4ecb6e0edc --- /dev/null +++ b/.changeset/measurement-terms-rejected-narrative.md @@ -0,0 +1,11 @@ +--- +"adcontextprotocol": patch +--- + +fix(compliance): `measurement_terms_rejected` — UUID-aliased idempotency_keys + spec-aligned narrative + +The `media_buy_seller/measurement_terms_rejected` storyboard shipped hardcoded `idempotency_key` literals on both `create_media_buy` steps. Combined with runner-side dynamic `start_time` substitution (the runner shifts stale dates forward to keep the buy future-dated), this produced **same key + different body** on every run against a long-running seller deployment, arming the spec-mandated `IDEMPOTENCY_CONFLICT` on the seller side. Switch to `$generate:uuid_v4#…` aliases so each run mints fresh keys (matches the established pattern across the storyboard suite). + +Also rewrites the narrative, which previously told implementers the buyer "retries the same `create_media_buy` `idempotency_key` with an adjusted payload" — a direct spec violation — to describe minting a fresh key for the retry. + +Closes #4219. Refs adcontextprotocol/adcp-client#1586. diff --git a/static/compliance/source/protocols/media-buy/scenarios/measurement_terms_rejected.yaml b/static/compliance/source/protocols/media-buy/scenarios/measurement_terms_rejected.yaml index 3ac9452152..650db4c9bc 100644 --- a/static/compliance/source/protocols/media-buy/scenarios/measurement_terms_rejected.yaml +++ b/static/compliance/source/protocols/media-buy/scenarios/measurement_terms_rejected.yaml @@ -12,8 +12,9 @@ narrative: | measurement_terms negotiation is a round-trip. The buyer proposes a measurement vendor, window, and variance tolerance on each package; the seller either accepts (returning measurement_terms echoed in the response) or rejects with TERMS_REJECTED and a message - explaining what is unacceptable. The buyer corrects the terms and retries the same - create_media_buy idempotency_key with an adjusted payload. + explaining what is unacceptable. The buyer mints a fresh idempotency_key and retries + create_media_buy with the adjusted payload — reusing the prior key against a different + body MUST be rejected with IDEMPOTENCY_CONFLICT per spec. This scenario sends an intentionally aggressive first proposal (max_variance_percent: 0 with a window the seller does not guarantee), verifies the seller rejects with @@ -105,7 +106,7 @@ phases: brand: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" - idempotency_key: "measurement-terms-probe-aggressive-v1" + idempotency_key: "$generate:uuid_v4#measurement_terms_rejected_aggressive_terms" start_time: "2026-05-01T00:00:00Z" end_time: "2026-05-31T23:59:59Z" packages: @@ -162,7 +163,7 @@ phases: brand: domain: "acmeoutdoor.example" operator: "pinnacle-agency.example" - idempotency_key: "measurement-terms-probe-relaxed-v1" + idempotency_key: "$generate:uuid_v4#measurement_terms_rejected_relaxed_terms" start_time: "2026-05-01T00:00:00Z" end_time: "2026-05-31T23:59:59Z" packages: