---
id: STATE-MBR-MEM-004
title: Purchase reservation
object: DOM-MBR-MEM-002
product_area: membership
module: memberships
revision: 1
status: draft
design_intent: proposed
implementation_evidence: partially-observed
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
states:
  - name: held
    meaning: A placed order retains a pending period through its reservation.
  - name: fulfilled
    meaning: The package obligation committed the period and consumed the reservation.
  - name: released
    meaning: An order void removed the unfulfilled period and released its claim.
  - name: cancelled
    meaning: >-
      Line cancellation or application rejection closed the reserved period
      without fulfilment.
transitions:
  - from: held
    to: fulfilled
    trigger: Commerce completes fulfilment.
    guard: The holding is not blocked by a pending fence.
    side_effects:
      - Commit the reserved period and allocate initial numbering.
    who: 'Commerce, through fulfilment'
    provenance: observed
    source: >-
      services-membership:modules/commerce-participant/services/persistence/commerce-claims.ts:closeClaim
  - from: held
    to: released
    trigger: Commerce voids the unpaid purchase.
    guard: The referenced term is still pending.
    side_effects:
      - Delete pending coverage and an otherwise empty holding created by it.
    who: 'Commerce, through the owner release function'
    provenance: test
    source: >-
      services-membership:modules/commerce-participant/tests/integration/participant.integration.spec.ts#releases
      a voided order: the pending term and the membership
  - from: held
    to: cancelled
    trigger: Cancellation or a rejecting decision closes pending coverage.
    guard: The period has not committed.
    side_effects:
      - Mark the pending period and claim cancelled.
    who: Commerce cancellation; the decision handler
    provenance: observed
    source: >-
      services-membership:modules/applications/services/decisions/decision-handler.ts:cancelPendingTerms
invalid:
  - from: fulfilled
    to: released
    why: Void release retains committed coverage rather than deleting it.
  - from: released
    to: held
    why: >-
      The owner functions preserve a closed claim as audit evidence and do not
      reopen it.
concurrency: >-
  The term-based claim index permits at most one claim record. Normal checkout
  races also use owner locking and identity checks. Ordinary placement is
  currently blocked by the recorded history-source incompatibility.
invariants:
  - INV-DOM-MBR-MEM-002-11
evidence:
  - >-
    services-membership at 5994c270:
    modules/commerce-participant/db/commerce-claims.table.ts,
    participant-vocabularies.ts (CLAIM_RELEASE_REASONS);
    db/migrations/20261018044117_commerce_participant.sql
---

# Purchase reservation

## The Lifecycle

```mermaid
stateDiagram-v2
  state held
  state fulfilled
  state released
  state cancelled
  held --> fulfilled
  held --> released
  held --> cancelled
```

## Transitions Explained

A claim accompanies placed coverage and remains as audit evidence after closing. Fulfilment marks it fulfilled. Void release removes pending coverage, while cancellation and rejecting decisions close it as cancelled.

## What Cannot Happen

Release does not remove fulfilled coverage. Closed claims have no reopening path in the current functions.

## Concurrency and Timing

The term-based claim index permits at most one claim record. Normal checkout races also use owner locking and identity checks. Ordinary placement is currently blocked by the recorded history-source incompatibility.
