---
id: DOM-MBR-STR-005
title: Term Lifecycle
realises: CON-MBR-STR-005
product_area: membership
module: structure
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
owned_by: SVC-MBR-STR-terms
readable_by:
  - SVC-MBR-STR-configuration
lifecycle: STATE-MBR-STR-004
custom_fields: none
identity: >-
  The lifecycle calculations have no stored identity; their answers are determined by supplied rules, term
  facts and dates. Status reasons are workspace configuration records with permanent readable identifiers.
  Memberships retains the identities of the holdings, terms and detections that use these answers.
attributes:
  - name: term rule
    kind: enum
    required: true
    computed: false
    notes: >-
      The primary package or grant supplies a term rule, and an existing term supplies the continuation
      context.
  - name: term period
    kind: computed
    required: true
    computed: true
    notes: Effective dates use inclusive ends; a never-ending term has no last day.
  - name: term state
    kind: enum
    required: true
    computed: false
    notes: 'Pending, committed, superseded, terminated and cancelled are the engine''s five recognized stored states.'
  - name: holding status on a date
    kind: computed
    required: true
    computed: true
    notes: >-
      The calculated status combines covering terms, suspension, grace and benefit settings for the requested
      date.
  - name: grace allowance
    kind: number
    required: true
    computed: false
    notes: Grace uses the deciding term's type override or its program allowance and includes the final grace day.
  - name: continuity episodes and continuous-since date
    kind: computed
    required: true
    computed: true
    notes: >-
      Effective terms form continuity episodes when their intervening gaps fit the program allowance; the
      latest episode begun by the requested date supplies continuous-since.
  - name: original join date
    kind: computed
    required: false
    computed: true
    notes: >-
      When no join date was supplied, the earliest effective term start supplies it; an existing staff or
      imported date is retained.
  - name: cache changes
    kind: computed
    required: true
    computed: true
    notes: 'The cache helper reports differences from the stored cache, leaving the caller to persist them.'
  - name: sweep decisions
    kind: computed
    required: true
    computed: true
    notes: >-
      The sweep decides boundary dates, full catch-up after missed work, and expiry or lapse detections using
      the supplied business date.
  - name: status reasons
    kind: collection
    required: true
    computed: false
    notes: >-
      Reasons describe termination, suspension or cancellation; duplicate names are allowed and stored
      priority does not control listing.
relationships:
  - to: DOM-MBR-STR-004
    cardinality: A package or grant rule can determine many term periods
    ownership: 'Packages own term rules, which the calculation functions consume.'
    cross_service: true
  - to: DOM-MBR-STR-003
    cardinality: 'Each program and type supplies grace, benefit and continuity inputs for its holdings'
    ownership: Programs and types own the allowances; the domain's hooks provide their current values to the engine.
    cross_service: true
invariants:
  - id: INV-DOM-MBR-STR-005-01
    statement: >-
      Pure date, status, continuity, cache and sweep decisions use only their supplied inputs without a clock,
      database access or input mutation; facade hooks can obtain inputs from the domain.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-membership:modules/terms/tests/unit/term-rules.spec.ts#term rules: what 2.0 decides › gives the
      same result twice for the same frozen inputs (1.0 turned the fixed dates into Moments in place, so a
      second call read other inputs)
  - id: INV-DOM-MBR-STR-005-02
    statement: The term engine's module does not import another module from this repository.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-membership:modules/terms/tests/unit/isolation.spec.ts#term engine isolation › imports nothing
      of the repository outside modules/terms
  - id: INV-DOM-MBR-STR-005-03
    statement: >-
      Committed, superseded and terminated terms cover their effective dates; pending and cancelled terms
      neither cover dates nor start grace.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-membership:modules/terms/tests/unit/status.spec.ts#holding status: the effective states of §1 ›
      never lets a pending term cover a date
  - id: INV-DOM-MBR-STR-005-04
    statement: >-
      Status follows suspension, coverage with or without benefits, grace, lapse, future, pending and none in
      that order.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-membership:modules/terms/tests/unit/status.spec.ts#holding status: the precedence of §2 › puts
      suspended over grace
  - id: INV-DOM-MBR-STR-005-05
    statement: >-
      Engine column declarations constrain five stored term states, ordered start and end dates and eight
      cached holding statuses.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-membership:modules/terms/tests/unit/columns.spec.ts#term columns (§3.2) › order the dates: an
      end, when there is one, on or after the start
  - id: INV-DOM-MBR-STR-005-06
    statement: >-
      An absent previous sweep or one older than yesterday requires full recomputation instead of
      boundary-only work.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-membership:modules/terms/tests/unit/sweep.spec.ts#the sweep’s catch-up rule › catches up after
      a missed night
  - id: INV-DOM-MBR-STR-005-07
    statement: Suspended holdings and holdings with an effective covering term produce no expiry or lapse detections.
    enforced_at:
      - service
    provenance: observed
    source: 'services-membership:modules/terms/services/sweep.ts:detectionsOf'
  - id: INV-DOM-MBR-STR-005-08
    statement: >-
      The helper checks availability and matching reason use. Staff cancellation before the start allows an
      active termination reason as a fallback; Commerce cancellation merely resolves an optional reason,
      without checking availability or use.
    enforced_at:
      - service
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/operations/staff-operations.ts:terminateTerm;
      services-membership:modules/commerce-participant/services/cancellation/cancellation.ts:cancel
  - id: INV-DOM-MBR-STR-005-09
    statement: Grace and continuity calculations reject negative or fractional day allowances.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-membership:modules/terms/tests/unit/status.spec.ts#holding status: the effective states of §1 ›
      refuses a negative number of grace days
physical:
  - membership_status_reasons
evidence:
  - >-
    services-membership at 5994c270: modules/terms/services/term-rules.ts, status.ts, holding-cache.ts,
    sweep.ts, define-term-engine.ts, sql-templates.ts, local-dates.ts; modules/terms/db/term-columns.ts,
    holding-columns.ts, lifecycle-event-columns.ts; modules/memberships/services/sweep/status-sweep.ts and
    membership-caches.ts (the callers); modules/configuration/db/membership-status-reasons.table.ts and
    services/persistence/membership-status-reasons.ts; README.md#The term engine.
  - >-
    rhythm-v2-docs: specs/membership/structure/planning (CAP-MBR-STR-015, 016, 017; FND-MBR-STR-019 to 022,
    032).
---

# Term Lifecycle

## What It Models

The lifecycle gives Membership one shared interpretation of term dates, standing and continuity. It also supplies sweep decisions and the catalogue of explanations staff use for status actions. The memberships module owns and changes the records being interpreted.

## Identity and Attributes

Pure functions receive rules, dates and term facts and return dates, status, episodes, cache values or detections. The facade can obtain program and type settings through domain hooks. Reusable column declarations define the stored vocabulary; status reasons have ordinary configuration identities.

## Relationships

Packages and grants supply term rules, and programs and types supply allowances. Memberships locks its holdings, reads terms, calls the engine and persists the answers. Configuration stores and serves the reason catalogue on the lifecycle's behalf.

## Invariants

The structured rows distinguish pure decision functions from facade methods and database column declarations. A term that reaches its end retains its stored state; its calculated standing changes with the requested date.

## How It Is Stored and Shared

The terms service exposes calculation code and a workspace health route. It owns no holding or term table. Memberships implements purchase, staff and sweep execution, while the engine's declared Commerce and billing entry points still fail as unbuilt.

## Open Design Questions

Fixed-window behavior for fresh purchases, reinstatement absence and overlapping windows remain unsettled. Reason-use changes and the cancellation fallback must be reconciled with planning before implementation preparation.
