---
id: STATE-MBR-MEM-001
title: Membership standing
object: DOM-MBR-MEM-001
product_area: membership
module: memberships
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
states:
  - name: none
    meaning: >-
      No relevant pending or effective period determines coverage for the
      requested date.
  - name: pending
    meaning: Reserved coverage is waiting for fulfilment and supplies no rights.
  - name: future
    meaning: Effective coverage exists but begins after the requested date.
  - name: active
    meaning: Coverage on the requested date comes from a benefits-bearing type.
  - name: active_without_benefits
    meaning: Coverage exists on the date under a type that provides no benefits.
  - name: grace
    meaning: >-
      The latest ended period is still inside its type or program grace
      allowance.
  - name: lapsed
    meaning: The latest coverage ended and all applicable grace has elapsed.
  - name: suspended
    meaning: An outstanding suspension overrides the standing calculated from periods.
transitions:
  - from: none
    to: pending
    trigger: A first purchase is placed and reserves coverage.
    guard: No conflicting pending or effective coverage prevents reservation.
    side_effects:
      - Create an unnumbered holding if none exists.
    who: 'Commerce, through the owner functions'
    provenance: test
    source: >-
      services-membership:modules/commerce-participant/tests/integration/participant.integration.spec.ts#lets
      one of two concurrent checkouts for the same person win
  - from: pending
    to: active
    trigger: An authorization fulfils reserved coverage whose dates include today.
    guard: The authorization is new and the period’s type confers benefits.
    side_effects:
      - >-
        Assign initial numbering, record enrolment and write affiliations and
        grants.
      - Refresh cached standing.
    who: 'Commerce, through fulfilment'
    provenance: test
    source: >-
      services-membership:modules/commerce-participant/tests/integration/participant.integration.spec.ts#answers
      a redelivered authorization with the same records
  - from: none
    to: active
    trigger: Staff enter a current first period.
    guard: Creation checks pass and the selected type supplies benefits.
    side_effects:
      - 'Record enrolment, eligible grants, children and commitment events.'
    who: Membership staff
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/operations/staff-operations.ts:createMembership
  - from: future
    to: active
    trigger: Time reaches the effective period’s start.
    guard: The holding is unsuspended and the term confers benefits.
    side_effects:
      - The boundary sweep refreshes the list cache.
    who: Time; the status sweep
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/sweep/status-sweep.ts:sweepTenant
  - from: active
    to: grace
    trigger: Time passes the final covered day.
    guard: Positive grace remains and no following period supplies coverage.
    side_effects:
      - Record expiry detection once and refresh the cache.
    who: Time; the status sweep
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/integration/db/membership-functions.integration.spec.ts#answers
      Sam grace, lapsed, then active across the lapse
  - from: grace
    to: lapsed
    trigger: The last grace day passes.
    guard: No later coverage prevents lapse.
    side_effects:
      - Record first lapse detection and publish the term’s termination event.
    who: Time; the status sweep
    provenance: test
    source: >-
      services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
      membership.term.terminated once when the midnight sweep finds a lapse
  - from: grace
    to: active
    trigger: Renewal adds effective coverage during grace.
    guard: Renewal checks pass and the new period provides current benefits.
    side_effects:
      - Record renewal and resolve enabled expiry attention.
    who: 'Staff, officers, members through Commerce, billing runs'
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/operations/term-writes.ts:writeCommittedTerm
  - from: lapsed
    to: active
    trigger: Reinstatement or rejoin adds current coverage.
    guard: >-
      The term provides benefits; a gap beyond the continuity limit starts
      another episode.
    side_effects:
      - >-
        Record the resulting lifecycle event while retaining the original join
        date.
    who: Staff or members through Commerce
    provenance: test
    source: >-
      services-membership:tests/e2e/s04-lapse-and-rejoin.e2e.spec.ts#keeps the
      original join date and restarts continuity at the rejoin
  - from: active
    to: suspended
    trigger: Staff or payment enforcement suspends an active holding.
    guard: >-
      Staff require a principal unsuspended holding; Commerce records
      enforcement and suspends only when needed.
    side_effects:
      - Suspend derived children and record suspension events.
    who: Membership staff; Commerce
    provenance: test
    source: >-
      services-membership:tests/acceptance/memberships/MBR-ACC-0174.acceptance.ts#MBR-ACC-0174
  - from: suspended
    to: active
    trigger: 'Staff restore, or Commerce restores its last suspension.'
    guard: >-
      Underlying periods resolve active; Commerce also requires no other open
      suspension and no staff reason.
    side_effects:
      - 'Clear eligible suspension, restore children and recompute standing.'
    who: Membership staff; Commerce
    provenance: test
    source: >-
      services-membership:modules/commerce-participant/tests/integration/participant.integration.spec.ts#suspends
      on a reversed payment, idempotently, and restores on
  - from: future
    to: none
    trigger: The only future period is cancelled.
    guard: No historical effective or other pending period determines standing.
    side_effects:
      - Record closure and update dependent child coverage.
    who: Staff; Commerce cancellation
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/operations/staff-operations.ts:terminateTerm
  - from: pending
    to: none
    trigger: The only pending period is cancelled.
    guard: No other period remains relevant to the requested date.
    side_effects:
      - Record cancellation and refresh cached standing.
    who: Commerce cancellation
    provenance: observed
    source: >-
      services-membership:modules/commerce-participant/services/cancellation/cancellation.ts:cancel
invalid:
  - from: suspended
    to: grace
    why: >-
      Time cannot change computed standing while suspension remains set;
      clearing suspension can reveal grace.
  - from: suspended
    to: suspended
    why: >-
      The staff suspension operation rejects a holding already suspended;
      repeated Commerce enforcement instead records or recognizes its
      obligation.
concurrency: >-
  Writers order changes with the membership lock. Per-membership sweep
  transactions share that lock, and a missed sweep does not change the resolver
  used for rights.
invariants:
  - INV-DOM-MBR-MEM-001-08
  - INV-DOM-MBR-MEM-001-09
evidence:
  - >-
    services-membership at 5994c270: modules/terms/services/status.ts
    (statusOf), sweep.ts; modules/memberships/services/sweep/status-sweep.ts;
    services/operations/staff-operations.ts
---

# Membership standing

## The Lifecycle

```mermaid
stateDiagram-v2
  state none
  state pending
  state future
  state active
  state active_without_benefits
  state grace
  state lapsed
  state suspended
  none --> pending
  pending --> active
  none --> active
  future --> active
  active --> grace
  grace --> lapsed
  grace --> active
  lapsed --> active
  active --> suspended
  suspended --> active
  future --> none
  pending --> none
```

## Transitions Explained

Standing is calculated for a date from coverage, grace and suspension. The table describes common outcomes rather than a persisted status machine; a current type can produce either active state. Nightly work refreshes list caches and records newly detected expiry or lapse.

## What Cannot Happen

Suspension keeps dated standing suspended until cleared. Staff cannot repeat an already-applied suspension. Correcting dates can change grace classification, and restoration returns whatever the remaining periods imply rather than promising active coverage.

## Concurrency and Timing

Writers order changes with the membership lock. Per-membership sweep transactions share that lock, and a missed sweep does not change the resolver used for rights.
