Skip to content

[Impl] commitment.idempotency_key has no scope, conflict rule or retention #128

Open

Description

@EvgenyAndroid

What did you build?

An implementation that parses and validates OpenProposal proposals, line items and commitments end to end.

What you tried to express, and what happened

§ 5.6.1 says "A request carrying an idempotency_key already seen MUST return the original result and MUST NOT create a second commitment." Three things are left open:

  • Scope. Unique to what: the buyer, the seller, the proposal? Two buyers that pick the same string would collide.
  • Conflict. If the same key arrives with a different payload (a changed amount, say), returning the original result silently hides a real disagreement.
  • Retention. How long a seller must remember the key.

What you did instead

Scoped keys to buyer plus seller, returned a conflict error when a reused key carries a different payload, and declared a retention window.

Suggested resolution: scope the key (buyer plus seller, or plus line item), require a conflict error on a changed payload, and require the seller to state how long keys are retained.

Fields involved

commitment.idempotency_key

How blocking was it?

Workaround required, but it works

Before you submit

  • I have read CONTRIBUTING.md and agree my contribution may be published under this repository's licence.
  • This contains no confidential or commercially sensitive information — no real rate cards, deal terms, client names or inventory data.

Activity

  1. EvgenyAndroid commented on Oct 27, 2026

    @EvgenyAndroid
    Author

    Where this stands after PR #73 and #130:

    Suggested floor: the seller MUST keep each key and its result at least until the proposal's valid_until passes. Before then, a forgotten key turns a retry into a new attempt, and that attempt passes PR #73's version and expiry checks if the proposal hasn't changed. After valid_until, PR #73 rejects every new attempt, so a forgotten key can't produce a second commitment. The floor leaves one case open: a buyer that never saw the first response and retries after valid_until gets expired and still doesn't know whether it committed. A longer retention, or a way to look a commitment up by its key, would close that.

  2. KonstantinMirin commented on Oct 27, 2026

    @KonstantinMirin

    Thank you, @EvgenyAndroid. Postindustria (IAB Tech Lab member) had proposed the same floor in our comment on #24, and our signing PR (#142) now states yours.

    In our PRs, a commit that wrote an entry keeps its key in assent[], so its result lasts as long as the record. A rejection writes no entry, so § 5.6.1 says the seller MUST keep one whose retry isn't same at least until offer_window.end, which replaces valid_until, or, with no end, as long as it keeps the proposal. After that date, every new commit fails as expired.

    Revised 2026-11-06: Your floor is back in our signing PR, after @pkanna000 asked for it too.

    Analyzed and generated with the help of Claude, fully reviewed by me.

  3. pkanna000 commented on Nov 6, 2026

    @pkanna000

    Implementation evidence in support of making @EvgenyAndroid's resolution normative in § 5.6.1 itself.

    I read the IAB Tech Lab Agent Registry on 5 Nov 2026 (53 agents, 1,735 tool definitions, public read-only access). 52 tools across 25 agents commit or change spend: media buys, orders, campaigns. 20 of them publish an input schema, and 2 of those 20 accept anything resembling an idempotency key. Buying agents retry on timeouts automatically, so today most retried commitments have no protection beyond what § 5.6.1 will require. The reference seller agent has the same gap on POST /proposals: IABTechLab/seller-agent#131.

    That argues for writing all three parts into § 5.6.1:

    1. Scope: the key is unique per buyer identity + seller + proposal_id.
    2. Conflict: the same key with a different payload MUST be rejected with a conflict error (the error object in [Change] § 2, § 5.5, § 5.6 — One error object for rejections: code, pointer, current version, retry #130), never answered silently with the original result.
    3. Retention: at least until the later of the proposal's valid_until and any hold's valid_until (see § 5.6.2 — Holds: offered hold terms and the live hold (#66) #143), so a retry inside the offer window can't create a second commitment.

    The same retry problem applies to hold requests: a hold retried after a timeout can take a second hold on the same inventory. If #143 lands, the same key rule would be worth applying there, and it pairs with the per-buyer cap in #156.

  4. KonstantinMirin commented on Nov 6, 2026

    @KonstantinMirin

    Thanks, @pkanna000, for the registry data. Our signing PR (#142) covers your first two parts. § 4.1.2 scopes the key to one buyer and seller pair, which is wider than one proposal_id. In § 5.6.1, reusing it with a different Commit returns idempotency_conflict and writes nothing.

    For retention, that PR now sets @EvgenyAndroid's floor, which meets yours. A commit the seller doesn't reject writes the buyer's commitment entry, which carries the key, and assent[] keeps that entry for as long as the proposal exists. A rejection writes no entry, so the seller MUST keep one whose retry isn't same at least until offer_window.end, which replaces the draft's valid_until. In our holds PR (#143), a hold's valid_until can't pass that date (§ 4.3.4), so it's already the later of your two dates.

    A retried hold can't take a second one. That PR says a repeated request while a hold is requested or held MUST NOT start a second hold.

    Analyzed and generated with the help of Claude, fully reviewed by me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions