Skip to content

[Change] § 4, § 5.1 — Add § 4.3 Lifecycle: who moves each status #65

Open

Description

@KonstantinMirin

What kind of change?

Change a normative requirement (MUST / SHOULD / MAY)

Which section?

Whole document / more than one section

Current text

§ 4.1:

| `status` | Enum | derived | `draft` \| `published` \| `under_review` \| `agreed` \| `withdrawn` \| `expired` |

§ 5.1 adds sold_out.

Proposed text

### 4.3 Lifecycle

A proposal with no `buyer` is *shared*; one with a `buyer` is *issued* to that
buyer. Asks, `under_review`, holds, and assent live on an issued proposal. A
shared proposal, or any other record more than one buyer can read (§ 5.8), carries
none of them.

A buyer `request` (a `requestable` value, not a hold) or `commit` (§ 5.6.1) is an
*ask*. It names a `version` and stays *open* until answered or withdrawn, or until
it goes stale (§ 4.1.3) or void. Holds change neither `status` nor
`negotiation_history[]`.

#### 4.3.2 Line-item status transitions

These transitions are exhaustive:

- `draft` → `draft` at a new `version`: seller `publish`.
- `draft` → `published`: seller `publish`. In a frozen version this changes only
  `status`, so it makes no new `version` and no entry (§ 4.1.3).
- `draft` → `withdrawn`: seller.
- `published` → `published` at a new `version`: seller `publish`.
- `published` → `under_review`: buyer ask.
- `under_review` → `under_review`: an act that leaves an ask open.
- `under_review` → `published`: the last open ask ends by seller `grant`,
  `counter`, or `decline`, by buyer `withdraw`, or by going stale (rule 2).
- `under_review` → `agreed`: seller `accept` of a `commit` (§ 4.1.2); open asks
  are void.
- `published` | `under_review` → `sold_out`: seller, never while `hold.status` is
  `held`; open asks are void.
- `published` | `under_review` | `sold_out` → `withdrawn`: seller, never while
  `hold.status` is `held` or a firm entry holds; open asks are void.
- `sold_out` → `published`: seller, when quantity returns, with no new `version`.
- `published` | `under_review` | `sold_out` → `expired`: `offer_window.end` passes.

A *firm entry* is a `pricing[]` entry with a `price_valid_until`. It holds until
that date or `offer_window.end`, whichever comes first. While it holds, a seller
revision MUST NOT change that entry, `pricing_id` included, and MUST NOT end it
sooner by moving `offer_window.end`. A `grant` or `counter` of a `request` on
that entry MAY lower its `list_rate` or widen its `unit_bounds`. A revision that
narrows who may commit (§ 5.8) does not apply, while the entry holds, to a buyer
that was eligible when the entry was first offered.

The seller of an issued proposal MUST answer a resolved `commit` under a firm
entry that:

- arrives while the entry holds, on or after `offer_window.start`; and
- passes the checks of § 5.6.1 and any authority check the seller applies (§ 5.8).

The seller accepts the `commit`, or declines it for lack of availability, an
exclusivity conflict (§ 5.6.7), or a ground stated in the `terms_refs[]` in force
when the entry was first offered.

Rules:

1. A stale ask (§ 4.1.3) is not open and not evaluated. The seller rejects it
   with `stale_version` (§ 2.4).
2. A seller MAY revise a line item while an ask is open or a hold runs. The
   revision is a new `version`. An ask it makes stale ends, and the seller MUST
   release a `held` hold it makes stale or re-offer it at the new `version`. A
   `commit` MUST NOT carry a `requestable` ask.

#### 4.3.3 Proposal status

A proposal's `status` is the first of the following that applies:

1. `draft` when every line item is `draft`.
2. `under_review` when any line item is.
3. `published` when any line item is `draft`, `published`, or `sold_out`.
4. `agreed` when any line item is `agreed`.
5. Otherwise `expired` when any line item is `expired`, else `withdrawn`.

#### 4.3.4 Expiry

| Instant | Effect when passed |
| :--- | :--- |
| `offer_window.end` | Every `published`, `under_review`, or `sold_out` line item is `expired`; open asks are void |
| `pricing[].price_valid_until` | `status` unchanged; a later `commit` under that entry is rejected with `expired`, and the seller SHOULD `publish` or `withdraw` |
| `hold.valid_until` | `hold.status` becomes `expired` |

`hold.valid_until` MUST NOT exceed `offer_window.end` or the earliest
`pricing[].price_valid_until`.

Why

The draft lists status values but never says whose act moves a line item between them, so two implementations can answer differently.

agreed rests on assent. A resolved commitment is an offer to buy (our commitment acceptance change request (#67)). A line item is agreed only when assent[] holds a matching commitment and acceptance (our binding-record change request (#63)). A commit can't carry an ask: accept makes no new version, so the asked value would sit outside the version the buyer named.

Rule 1 applies #24's stale test per line item (§ 4.1.3), as our reply on #24 asks. Rule 2 lets the seller change any term before agreed, at the cost of a new version. The buyer never commits to terms it hasn't seen. BCN's dated inventory change request (#105), filed by @matthew-iabtechlab for Carsten Sander (BCN), surfaced a moved issue date.

A firm entry is the exception, as @EvgenyAndroid proposed. The text closes the four exits of their answer to open question 1. A revision of other terms keeps the entry, so the buyer re-commits at the new version and the same rate.

Our lifecycle PR carries this text. The rules issue lists the rules we left out.

Knock-on effects

Open questions for the working group:

  1. Answered by @EvgenyAndroid, in our lifecycle PR. Should a firm rate also bar the other ways a seller can end the offer before price_valid_until? Under rule 2, it can move offer_window.end earlier or narrow who may commit, such as eligible_buyers[]. It can also withdraw the line item, or leave a commit unanswered until it lapses (our binding-record change request (#63)). Yes to all four. While a firm entry holds, the seller can't end it by moving offer_window.end or withdraw the line item. While it holds, a narrowing doesn't apply to a buyer that was eligible at first offer. A resolved commit that arrives after the offer opens must be answered. sold_out stays.
  2. Should a seller have to issue a proposal while a firm entry on its shared proposal holds, and carry the entry over unchanged? Otherwise, declining to issue is a fifth exit. The rules issue lists it.
  3. Should a seller answer a live commitment entry within a stated time, and under a firm entry before price_valid_until? As proposed, the duty to answer has no deadline, and the entry stays live until offer_window.end. The rules issue lists it.
  4. While a firm entry holds, should a revision also be barred from raising what an eligible buyer pays through deltas or included[]? Should it be barred from removing the mechanism the entry prices? The rules issue lists it.

Would this break an implementation already built to this draft?

Yes — breaking

You are commenting as

Other — IAB Tech Lab member (Postindustria).

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.

Details and examples: Lifecycle: full rationale and examples

Revised 2026-11-03: Rewritten to match our lifecycle PR, with a firm entry per pricing[] entry, and our signing PR, where assent[] decides agreed. Open question 1 is answered in the lifecycle PR, and questions 2 to 4 are new.

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

Activity

  1. added
    change-requestProposes specific normative wording
    normativeChanges what the spec requires
    breakingWould break an implementation already built to this draft
    on Oct 23, 2026
  2. EvgenyAndroid commented on Oct 27, 2026

    @EvgenyAndroid

    Two cases the transition table should cover:

    1. A seller's firm offer awaiting the buyer. A seller firms the terms (and may reserve inventory) and gives the buyer until an expiry to accept. Under [Change] § 4, § 5.1 — Add § 4.3 Lifecycle: who moves each status #65 plus [Change] § 5.6.2 — hold_status needs refused and re-entry to requested #66 that seems to be published with hold_status.state: held. That only works if a hold freezes the offered terms, which is the addition on [Change] § 5.6.2 — hold_status needs refused and re-entry to requested #66. It would help if § 4.3 said that this is the intended representation.
    2. A buyer declining an offer. [Change] § 4, § 5.1 — Add § 4.3 Lifecycle: who moves each status #65 gives the buyer withdraw for its own ask, and withdrawn is the seller's status. A buyer turning down a seller's offer, as opposed to withdrawing an ask, has no action. Today the only reading is "no ask", with nothing recorded. If a decline should be recordable, for audit or to release a hold early, it needs an action; if not, saying so would settle it.
  3. EvgenyAndroid commented on Oct 27, 2026

    @EvgenyAndroid

    Thanks @KonstantinMirin. The decline sentence settles my second point.

    On the firm offer, one mismatch. § 5.6 defines pricing[].price_valid_until as "How long the posted rate holds", and a buyer will read that as a promise. #66's open question 4 now says the date "bars a late acceptance (#65); it doesn't promise one (#67)". Rule 2 here also lets the seller revise the rate before that date, which makes a commit at the earlier version stale. So a firm offer as § 4.3 defines it promises nothing that any published line item with a price_valid_until doesn't. My reply on #66 assumed the date was a promise.

    Two ways to resolve it:

    1. Keep it a bound. § 5.6 rewords price_valid_until as the last moment the seller may accept a commit at that rate, and § 4.3 drops "firm".
    2. Make it a promise. Until price_valid_until, a seller revision MUST NOT change that pricing[] entry, and the seller MUST NOT decline a commit under it except for lack of availability. Nothing is reserved, so the availability decline stays ([Change] § 3, § 5.6.1, Appendix B — A resolved commitment is an offer until the seller accepts #67), and so does marking the line item sold_out.

    I'd take 2. It's the terms-only guarantee a seller means when it sets hold_available: false, without a hold. If the group prefers 1, the § 5.6 wording still needs to change, because "holds" reads as 2.

  4. KonstantinMirin commented on Oct 28, 2026

    @KonstantinMirin
    Author

    Thank you, @EvgenyAndroid. Postindustria (IAB Tech Lab member) takes your second option in § 4.3 above, with one change. The seller may still decline on a ground the line item already states, such as eligible_bidders[] or terms_acceptance_required. Before price_valid_until, rule 2 keeps that pricing[] entry and its pricing_id (#94) in every new version. After a revision of other terms, the buyer re-commits at the same rate. #67 adds the decline limit, and #66 open question 4 follows. New open question 1 above asks whether a seller may still end a firm offer early.

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

  5. EvgenyAndroid commented on Oct 28, 2026

    @EvgenyAndroid

    Thanks @KonstantinMirin. I'd answer open question 1 yes. Each of those exits lets a seller walk away from the rate without changing it:

    • Moving valid_until earlier. Until price_valid_until, a revision MUST NOT set valid_until before it.
    • Narrowing a stated ground. The grounds that count should be the ones stated when the firm rate was first offered. A revision that narrows eligible_bidders[], or sets terms_acceptance_required to true, shouldn't create a new ground against a buyer that already qualified.
    • Withdrawing. Until price_valid_until, withdrawn should be barred. sold_out stays, since lack of availability is the one exit the offer keeps.
    • Leaving a commit unanswered. [Change] § 4.1 — Define record_digest and assent[]; defer signer identity to the AAMP profile #63 voids an unmatched commitment once price_valid_until passes, so silence works as a decline on no ground. A commit that arrives before the date should need an answer: accept, or decline on an allowed ground.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    breakingWould break an implementation already built to this draftchange-requestProposes specific normative wordingnormativeChanges what the spec requiressection:4-proposal

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions