From 6518d0cd08df7e4521acd00241c10b5bfa068b8d Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Fri, 8 May 2026 07:13:00 -0400 Subject: [PATCH 1/4] spec(compliance): correct measurement_terms_rejected idempotency narrative MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Narrative said the buyer 'retries the same create_media_buy idempotency_key with an adjusted payload', which contradicts the spec — reusing a key against a different body MUST yield IDEMPOTENCY_CONFLICT. The sample payloads in the fixture already use distinct keys (aggressive-v1 / relaxed-v1); only the prose was wrong. Rewrite to match. Refs adcontextprotocol/adcp-client#1586. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../media-buy/scenarios/measurement_terms_rejected.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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..80e617dbf2 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 From af3c82a28ffe01391e5c81473b115f7f0b508290 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Fri, 8 May 2026 07:17:02 -0400 Subject: [PATCH 2/4] chore: add changeset Co-Authored-By: Claude Opus 4.7 (1M context) --- .changeset/measurement-terms-rejected-narrative.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/measurement-terms-rejected-narrative.md diff --git a/.changeset/measurement-terms-rejected-narrative.md b/.changeset/measurement-terms-rejected-narrative.md new file mode 100644 index 0000000000..fb772f42d3 --- /dev/null +++ b/.changeset/measurement-terms-rejected-narrative.md @@ -0,0 +1,8 @@ +--- +--- + +spec(compliance): correct `measurement_terms_rejected` idempotency narrative + +The narrative on the `media_buy_seller/measurement_terms_rejected` storyboard told implementers the buyer "retries the same `create_media_buy` `idempotency_key` with an adjusted payload." That contradicts the spec — reusing a key against a different body MUST yield `IDEMPOTENCY_CONFLICT`. The sample payloads in the fixture already use distinct keys (`measurement-terms-probe-aggressive-v1` / `measurement-terms-probe-relaxed-v1`); only the prose was wrong. Rewrite the narrative to match the spec and the actual sample payloads. + +Refs adcontextprotocol/adcp-client#1586. From 80ea6d6d113e90b92d2048526cdfa244a2222b23 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Fri, 8 May 2026 07:30:15 -0400 Subject: [PATCH 3/4] fix(compliance): UUID-aliased idempotency_keys for measurement_terms_rejected Hardcoded literals + runner-side start_time substitution = same key + different body on every run against a long-running seller, arming the spec-mandated IDEMPOTENCY_CONFLICT. Switch both create_media_buy steps to \$generate:uuid_v4#... aliases (the established pattern in this suite). Closes #4219. Co-Authored-By: Claude Opus 4.7 (1M context) --- .changeset/measurement-terms-rejected-narrative.md | 8 +++++--- .../media-buy/scenarios/measurement_terms_rejected.yaml | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.changeset/measurement-terms-rejected-narrative.md b/.changeset/measurement-terms-rejected-narrative.md index fb772f42d3..f8bb9b08a8 100644 --- a/.changeset/measurement-terms-rejected-narrative.md +++ b/.changeset/measurement-terms-rejected-narrative.md @@ -1,8 +1,10 @@ --- --- -spec(compliance): correct `measurement_terms_rejected` idempotency narrative +fix(compliance): `measurement_terms_rejected` — UUID-aliased idempotency_keys + spec-aligned narrative -The narrative on the `media_buy_seller/measurement_terms_rejected` storyboard told implementers the buyer "retries the same `create_media_buy` `idempotency_key` with an adjusted payload." That contradicts the spec — reusing a key against a different body MUST yield `IDEMPOTENCY_CONFLICT`. The sample payloads in the fixture already use distinct keys (`measurement-terms-probe-aggressive-v1` / `measurement-terms-probe-relaxed-v1`); only the prose was wrong. Rewrite the narrative to match the spec and the actual sample payloads. +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). -Refs adcontextprotocol/adcp-client#1586. +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 80e617dbf2..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 @@ -106,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: @@ -163,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: From 3ee495b2a51401654b2e5bf996861758a34ffa82 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Fri, 8 May 2026 07:38:03 -0400 Subject: [PATCH 4/4] chore(changeset): bump measurement_terms_rejected fix to patch level The fix is user-visible to anyone running the conformance suite against a long-running seller. Promote from --empty so it lands in the 3.0.7 release notes. Co-Authored-By: Claude Opus 4.7 (1M context) --- .changeset/measurement-terms-rejected-narrative.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.changeset/measurement-terms-rejected-narrative.md b/.changeset/measurement-terms-rejected-narrative.md index f8bb9b08a8..4ecb6e0edc 100644 --- a/.changeset/measurement-terms-rejected-narrative.md +++ b/.changeset/measurement-terms-rejected-narrative.md @@ -1,4 +1,5 @@ --- +"adcontextprotocol": patch --- fix(compliance): `measurement_terms_rejected` — UUID-aliased idempotency_keys + spec-aligned narrative