---
id: WF-MBR-STR-003
title: Close or remove a unit
trigger: Staff close a unit or request deletion of an unused unit.
product_area: membership
module: structure
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
inference: []
implements:
  - CAP-MBR-STR-004
moves:
  - STATE-MBR-STR-001
crosses:
  - SVC-MBR-STR-structure
idempotency: >-
  Repeated closure returns an already inactive unit without another write. Repeated deletion returns not found
  once the unit has gone.
concurrency: >-
  Closure and deletion lock the workspace tree and unit row. Closure optionally compares the supplied version;
  deletion has no version argument and checks current dependencies under its locks.
hops:
  - 'n': 1
    actor_or_service: Structure service
    action: >-
      Acquire tree and unit locks; reject a missing record, and for closure reject merged standing or a
      supplied stale version.
    anchor: 'services-membership:modules/structure/services/tree/tree-edits.ts:deactivateUnit'
    seam: sync
    on_failure: Stop the operation without committing changes.
    left_inconsistent: No partial Membership write survives the failed transaction.
    observer: the caller
    provenance: observed
    source: 'services-membership:modules/structure/services/tree/tree-edits.ts:deactivateUnit'
  - 'n': 2
    actor_or_service: Structure service
    action: >-
      For closure, change standing only when necessary and preserve placement, children, history, links,
      finance and affiliations.
    anchor: 'services-membership:modules/structure/services/persistence/organizational-units.ts:setUnitStatus'
    seam: sync
    on_failure: A failed standing write rolls back closure.
    left_inconsistent: No partial Membership write survives the failed transaction.
    observer: unit versions
    provenance: test
    source: >-
      services-membership:modules/structure/tests/integration/services/tree/unit-merge.integration.spec.ts#the
      tree edits affiliations bind › delete and deactivate › deactivates it instead
  - 'n': 3
    actor_or_service: Memberships module persistence
    action: 'For deletion, count all affiliations naming the unit, including ended ones.'
    anchor: >-
      services-membership:modules/memberships/services/persistence/term-affiliations.ts:countAffiliationsNamingUnit
    seam: sync
    on_failure: Reject a nonzero count and advise closure instead.
    left_inconsistent: No partial Membership write survives the failed transaction.
    observer: the caller
    provenance: test
    source: >-
      services-membership:modules/structure/tests/integration/services/tree/unit-merge.integration.spec.ts#the
      tree edits affiliations bind › delete and deactivate › refuses to delete a unit a closed affiliation
      names, 409 unit_has_affiliations with the count
  - 'n': 4
    actor_or_service: Structure service
    action: >-
      Count current children, retained parent references, organization links, dues products and merged sources
      naming the unit as survivor.
    anchor: 'services-membership:modules/structure/services/persistence/organizational-units.ts:unitReferences'
    seam: sync
    on_failure: Reject deletion and report reference categories and counts.
    left_inconsistent: No partial Membership write survives the failed transaction.
    observer: the caller
    provenance: test
    source: >-
      services-membership:tests/e2e/structure-api.e2e.spec.ts#the structure API, end to end in process ›
      refuses to delete a unit in use, 409 unit_in_use
  - 'n': 5
    actor_or_service: Membership database
    action: >-
      Remove the unit's ancestry pairs and unit row with owned cascading data; protected program, identity or
      application references can still block deletion.
    anchor: >-
      services-membership:modules/structure/services/persistence/organizational-units.ts:deleteOrganizationalUnitRow
    seam: sync
    on_failure: Translate a foreign-key refusal to in-use and roll back the whole deletion.
    left_inconsistent: No partial Membership write survives the failed transaction.
    observer: the caller
    provenance: test
    source: >-
      services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches, through
      the API › DEF-MBR-0084: a structure delete a program’s reference blocks › refuses deleting a unit a
      program is owned by 409 unit_in_use, and keeps it
branches:
  - >-
    Membership does not query committee attachments or call Committees before deletion; any cross-service
    protection remains outside this verified flow.
waits:
  - The request waits behind an existing workspace tree edit.
evidence:
  - >-
    services-membership at 5994c270: modules/structure/services/tree/tree-edits.ts (deactivateUnit,
    deleteUnit); services/persistence/organizational-units.ts (unitReferences, deleteOrganizationalUnitRow);
    modules/memberships/services/persistence/term-affiliations.ts; docs/migrations/unit-merge/README.md
    (Delete).
controls: [TC-AVL-002, TC-AVL-003, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-CHG-001, TC-CHG-002, TC-CHG-003, TC-CHG-004, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-DAT-001, TC-DAT-004, TC-DAT-007, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-005, TC-SEC-001, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003]
---

# Close or remove a unit

## What It Does

Closure retains the unit's work and removes its ability to grow through new children or affiliations. Deletion instead removes an unreferenced current record after affiliation, structure and database dependency checks.

## The Flow

```mermaid
flowchart TD
  A[Close or delete] --> B[Lock workspace tree and unit]
  B --> C{Close?}
  C -->|Yes| D[Reject merged or stale; set inactive if needed]
  C -->|No| E[Count affiliations and structural references]
  E --> F{Any reference?}
  F -->|Yes| G[Refuse and retain record]
  F -->|No| H[Remove owned rows subject to database references]
  D --> I[Commit]
  H --> I
```

## When Something Fails

Deletion is atomic within Membership and retains unit versions. Current parent-history checks can miss a former relationship replaced on the same effective day, because its earlier value survives only in versions. Committee-side behavior is not a verified deletion guard.

## Open Design Questions

Product must decide whether closed units can reopen. Separate links, settings and dues extensions remain writable after closure, and their intended administration needs clarification.
