Lifecycle & resolution
ABP has two orthogonal axes. The commercial FSM tracks the Booking; the resolution axis tracks which seat, per Allocation. They move independently.
The commercial FSM
Section titled “The commercial FSM”Every transition is validated against a matrix, audited in an append-only log, and fires side-effects as named handlers (via NATS, not DB triggers). Transitions are always 1-to-1 (no bulk).
Hold Confirm Settle Fulfill Redeem init ────────▶ HELD ──────────▶ CONFIRMED ──────▶ PAID ──────▶ FULFILLED ──────▶ USED │ TTL expires │ Release │ Release │ Refund ▼ ▼ ▼ ▼ EXPIRED RELEASED RELEASED REFUNDED └──────────── Unit returns to the Bucket (available) ───────────┘| State | Meaning | Unit |
|---|---|---|
HELD | allocated, TTL running | leaves the Bucket |
CONFIRMED | buyer committed (PII attached) | held |
PAID | payment captured | held |
FULFILLED | artifact issued (idempotent) | sold |
USED | check-in via Redeem; validated against physical capacity | sold |
REFUNDED | real post-sale refund | back to the Bucket |
EXPIRED | TTL elapsed before fulfill | back to the Bucket |
RELEASED | explicit release pre-fulfill | back to the Bucket |
Settle is separate from Confirm: payment ≠ commitment (an invoice confirms without
paying). USED is reached by Redeem — Access triggers it, but the
Registry validates against physical capacity and may deny with BUMPED (overbook only).
The resolution axis
Section titled “The resolution axis”A second independent axis per Allocation: the resolution of the Unit. Same principle as Tickets’ physical × commercial split, here applied to which seat.
COMMERCIAL: HELD → CONFIRMED → PAID → FULFILLED → USEDRESOLUTION: ANONYMOUS (slot guaranteed, Unit TBD) ──Assign/AutoAssign──▶ CONCRETE (Unit X)- A quantitative Hold (N seats of a QUALITATIVE Bucket) guarantees the count (no
oversell, headroom reserved). Whether the response is
ANONYMOUS(resolve later) or alreadyCONCRETE(eager best-available) is the implementation’s choice — the protocol describes the states, not the timing. An explicit Hold is bornCONCRETE. In a QUANTITATIVE Bucket there is no Unit → resolution is N/A. Assign(Channel chooses) /AutoAssign(Registry chooses) concretize. The concretization atomically pulls the Unit out of the still-available pool.- The order between the two axes is not the protocol’s. You can
Fulfillwith allocations stillANONYMOUS(e.g. a self-service totem issues a “seat TBD” voucher). If a Catalog requiresCONCRETEbefore some state, that is the implementer’s policy. - The linear status a consumer sees (
RESERVED → ALLOCATING → DONE) is a derived projection of this axis — not a first-class FSM state.
Touching a resolved Allocation
Section titled “Touching a resolved Allocation”| Op | Effect |
|---|---|
| Reseat | Assign with overwrite=true over a CONCRETE allocation — assigns the new Unit, releases the old one, atomically. Without overwrite: ALREADY_ASSIGNED. |
| Unassign | CONCRETE → ANONYMOUS: drops the Unit but keeps the slot (the count). Over an ANONYMOUS allocation: NOT_RESOLVED. |
| Swap | atomic exchange of Units between two CONCRETE allocations (possibly across Bookings → between holders). A dedicated RPC because Unassign+Assign would leave a “loose seat” window. |