---
id: STATE-MBR-MEM-005
title: Unit affiliation span
object: DOM-MBR-MEM-003
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: scheduled
    meaning: The connection begins after the queried business date.
  - name: in_force
    meaning: The connection’s dates and effective term cover the queried date.
  - name: ended
    meaning: Its own end or the term end has passed.
  - name: withdrawn
    meaning: >-
      The live connection was removed; temporal versions retain its earlier
      recorded facts.
transitions:
  - from: scheduled
    to: in_force
    trigger: The queried date reaches the connection start.
    guard: The dated term resolver includes this period on the requested day.
    side_effects:
      - Dated reads now include the unit.
    who: Time
    provenance: inferred
    source: >-
      services-membership:modules/memberships/services/persistence/term-affiliations.ts:belongsToUnitsOn
      (dated reads)
  - from: in_force
    to: ended
    trigger: The span is shortened or coverage moves or closes.
    guard: >-
      The applicable end shortens coverage, and direct writes pass scope and
      version checks.
    side_effects:
      - Direct endings and transfers publish an ended event.
      - Merge ending and term trimming emit no separate affiliation event.
    who: 'Staff, officers, the structure module''s merge, term writes'
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/affiliations/affiliation-operations.ts:endAffiliation,
      keepAffiliationsInsideTerm
  - from: scheduled
    to: withdrawn
    trigger: 'An explicit last day precedes the start, or a writer removes the row.'
    guard: >-
      Explicit withdrawal must name the day immediately before the start and no
      successor may reference the row.
    side_effects:
      - >-
        Direct withdrawal publishes an ended event; merge removal and trimming
        do not.
    who: 'Staff, officers, the merge, term writes'
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/affiliations/affiliation-operations.ts:endAffiliation
  - from: scheduled
    to: scheduled
    trigger: A merge changes the unit before the span begins.
    guard: Survivor type and final affiliation rules remain valid.
    side_effects:
      - >-
        Record merge transfer details without a separate affiliation business
        event.
    who: The structure module's merge
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/affiliations/unit-merge.ts:decideMergedTerm
  - from: in_force
    to: withdrawn
    trigger: An explicit end retrospectively removes the connection.
    guard: The day is immediately before its start and no successor depends on it.
    side_effects:
      - Remove the live row and publish direct withdrawal.
    who: 'Staff, officers, the merge, term writes'
    provenance: observed
    source: >-
      services-membership:modules/memberships/services/affiliations/affiliation-operations.ts:endAffiliation
invalid:
  - from: ended
    to: in_force
    why: >-
      An end request can shorten a span but cannot extend its last day; further
      coverage uses another connection.
concurrency: >-
  Membership locking and affiliation version checks order changes. Unit sharing
  protects target validation against a simultaneous merge.
invariants:
  - INV-DOM-MBR-MEM-003-04
  - INV-DOM-MBR-MEM-003-05
  - INV-DOM-MBR-MEM-003-07
evidence:
  - >-
    services-membership at 5994c270:
    modules/memberships/services/affiliations/affiliation-operations.ts,
    unit-merge.ts; services/persistence/term-affiliations.ts
---

# Unit affiliation span

## The Lifecycle

```mermaid
stateDiagram-v2
  state scheduled
  state in_force
  state ended
  state withdrawn
  scheduled --> in_force
  in_force --> ended
  scheduled --> withdrawn
  scheduled --> scheduled
  in_force --> withdrawn
```

## Transitions Explained

These names describe a dated span and term coverage, with withdrawn representing a removed row. Transfer creates a successor and merge chooses dated transfers or re-pointings. End accepts an explicit last day and can remove a row retrospectively when that day precedes its start.

## What Cannot Happen

A connection with a successor cannot be withdrawn because lineage still references it. End can shorten an existing span; it cannot extend or reopen it. A separate record is used for later coverage.

## Concurrency and Timing

Membership locking and affiliation version checks order changes. Unit sharing protects target validation against a simultaneous merge.
