---
id: STATE-PFM-MTD-009
title: Relationship type lifecycle
object: DOM-PFM-MTD-006
product_area: platform
module: metadata
revision: 1
status: draft
design_intent: confirmed
origin: 'PLAT-M6 D3 (Andrew, 2026-10-21: refuse, name the object) and assumption A1, plan approved 2026-10-21 (services-platform:docs/associations.md#10.
  Decisions; docs/milestones/PLAT-M6-custom-record-relationships.md)'
implementation_evidence: observed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
states:
- name: absent
  meaning: The workspace has no current type under this key.
- name: active
  meaning: The type admits new links and contributes query relationships.
- name: inactive
  meaning: Existing connections remain maintainable, but creating another link is refused.
transitions:
- from: absent
  to: active
  trigger: An administrator creates an active type.
  guard: The key is available, both category endpoints are supported and active, and labels, shape and deletion
    policy validate without a native cascade.
  side_effects:
  - Audit creation, advance the query-description stamp and write custom-endpoint recipes.
  who: Workspace Administrator (platform.association-types.create)
  provenance: test
  source: services-platform:modules/associations/tests/e2e/associations.e2e.spec.js#answers 400 to a type of a draft
    custom object, naming it as not active
- from: absent
  to: inactive
  trigger: An administrator creates an initially inactive type.
  guard: Apply the same definition checks as active creation.
  side_effects:
  - Audit the definition; defer activation recipes until the type becomes active.
  who: Workspace Administrator (platform.association-types.create)
  provenance: observed
  source: services-platform:modules/associations/services/persistence/association-types.ts:createAssociationType
- from: active
  to: active
  trigger: An administrator replaces type policy or labels.
  guard: The supplied version matches, fixed shape is unchanged and removing primary support leaves no in-force
    primary link.
  side_effects:
  - Store mutable settings, audit changed fields and advance query description.
  who: Workspace Administrator (platform.association-types.update)
  provenance: test
  source: services-platform:modules/associations/tests/integration/services/persistence/association-types.integration.spec.js#refuses
    a change of cardinality, the shape o
- from: active
  to: inactive
  trigger: An administrator deactivates a type or removes a currently used type.
  guard: No object in any status selects it as owner; replacement uses its version, while removal uses the locked
    current row.
  side_effects:
  - Retain links and audit deactivation; a direct replacement advances the query stamp.
  who: Workspace Administrator (platform.association-types.update or .delete)
  provenance: test
  source: services-platform:modules/associations/tests/integration/services/persistence/association-types.integration.spec.js#deactivates
    a type an association names inst
- from: inactive
  to: active
  trigger: An administrator reactivates a type.
  guard: The replacement version matches and its definition validates.
  side_effects:
  - Allow new links and refresh query description.
  who: Workspace Administrator (platform.association-types.update)
  provenance: test
  source: services-platform:modules/associations/tests/integration/services/persistence/association-types.integration.spec.js#deactivates
    a type an association names inst
- from: active
  to: absent
  trigger: An administrator removes an unused active type.
  guard: No lock applies, no object selects it as owner and no current link of any date references it.
  side_effects:
  - Delete the definition and audit removal.
  who: Workspace Administrator (platform.association-types.delete)
  provenance: test
  source: services-platform:modules/associations/tests/integration/services/persistence/association-types.integration.spec.js#records
    a delete and a deactivation in the s
- from: inactive
  to: absent
  trigger: An administrator removes an unused inactive type.
  guard: Apply the same removal checks without an expected version.
  side_effects:
  - Delete the retained definition and audit removal.
  who: Workspace Administrator (platform.association-types.delete)
  provenance: observed
  source: services-platform:modules/associations/services/persistence/association-types.ts:deleteAssociationType
invalid: []
concurrency: Definition replacement uses an exclusive type lock and current version; removal uses the lock alone.
  Link writes share that row, delaying type changes until current writes finish.
invariants:
- INV-DOM-PFM-MTD-006-01
- INV-DOM-PFM-MTD-006-02
- INV-DOM-PFM-MTD-006-03
evidence:
- 'services-platform at ad3f6a65: modules/associations/services/persistence/association-types.ts (createAssociationType,
  updateAssociationType, deleteAssociationType, lockTypeForAssociationWrite); docs/associations.md §1 and §2'
- 'react-console at baf6dae3: components/configuration/resources/relationship-types.tsx (Retire, Revive, Remove)'
---

# Relationship type lifecycle

## The Lifecycle

```mermaid
stateDiagram-v2
  [*] --> absent
  absent --> active: create
  absent --> inactive: create switched off
  active --> active: replace labels or rules
  active --> inactive: retire, or remove while used
  inactive --> active: revive
  active --> absent: remove unused
  inactive --> absent: remove unused
```

## Transitions Explained

New definitions can start active or inactive. Deactivation preserves existing links, revival permits new links and removal deletes only definitions with no current references. Owner selections prevent both removal and deactivation.

## What Cannot Happen

Shape-changing replacements and retirement beneath an owner selection are refused. A request that leaves an inactive type unchanged is allowed; it is not an invalid state transition.

## Concurrency and Timing

Definition replacement uses an exclusive type lock and current version; removal uses the lock alone. Link writes share that row, delaying type changes until current writes finish.
