---
id: STATE-PFM-MTD-002
title: Custom field definition lifecycle
object: DOM-PFM-MTD-002
product_area: platform
module: metadata
revision: 1
status: draft
design_intent: confirmed
origin: 'services-platform:docs/metadata.md#Custom fields: the rules (removal is archival; Andrew 2026-10-08); custom
  objects architecture D-20 (Andrew, 2026-10-18: refuse the archive)'
implementation_evidence: observed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
states:
- name: absent
  meaning: The workspace has no definition under this type and key.
- name: live
  meaning: The unarchived definition is eligible for description, values, replacement and rule configuration while
    its type is available.
- name: archived
  meaning: The key and values remain reserved, but the field is excluded from current use and cannot be edited before
    restoration.
transitions:
- from: absent
  to: live
  trigger: An administrator adds a custom field.
  guard: The parent category is eligible, the key is valid and available, and a custom object remains under its
    two-hundred-property cap.
  side_effects:
  - Store the new definition at version one.
  who: Workspace Administrator (platform.custom-fields.create)
  provenance: test
  source: services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a key an archived field of the tenant still holds
- from: live
  to: live
  trigger: An administrator saves a definition or rule set.
  guard: Definition replacement requires the read version, no update lock and compatible widening; rules require
    a coherent set and a successful custom-object uniqueness check when enabled.
  side_effects:
  - Rule changes rebuild required unique reservations transactionally and advance the query-description stamp.
  who: Workspace Administrator (platform.custom-fields.update)
  provenance: test
  source: services-platform:modules/metadata/tests/e2e/custom-fields.e2e.spec.js#refuses a stale version as 409
- from: live
  to: archived
  trigger: An administrator archives a live definition.
  guard: The field has no lock and no existing archive stamp; custom-object conditions cannot still reference it.
  side_effects:
  - Stamp archival and remove its rules with search degradation and rebuild requests; start rebuilds after commit.
  who: Workspace Administrator (platform.custom-fields.delete)
  provenance: test
  source: services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.rules.integration.spec.js#removes
    a field’s rules when the field is arch
- from: archived
  to: live
  trigger: An administrator restores an archived definition.
  guard: The definition is archived and its parent is an eligible live registered type or a draft or active custom
    object; no expected-version or field-lock check applies.
  side_effects:
  - Remove the archive stamp, recover value visibility and leave earlier rules and search selections absent.
  who: Workspace Administrator (platform.custom-fields.delete)
  provenance: observed
  source: services-platform:modules/metadata/services/persistence/custom-fields.ts:restoreCustomField
invalid:
- from: archived
  to: archived
  why: Archival retains the key and values; changes to archived definitions require restoration.
- from: live
  to: absent
  why: Archival retains the key and values; changes to archived definitions require restoration.
- from: archived
  to: absent
  why: Archival retains the key and values; changes to archived definitions require restoration.
concurrency: Replacement uses the version read. Archival and restoration serialize on the definition row without
  an expected version, and restoration additionally rechecks current type eligibility. Search job starts follow
  the archive commit and may fail independently.
invariants:
- INV-DOM-PFM-MTD-002-01
- INV-DOM-PFM-MTD-002-02
- INV-DOM-PFM-MTD-002-03
- INV-DOM-PFM-MTD-002-08
evidence:
- 'services-platform at ad3f6a65: modules/metadata/services/persistence/custom-fields.ts (archiveCustomField, restoreCustomField,
  setFieldRules); services/field-lifecycle/archive-field.ts; docs/metadata.md'
---

# Custom field definition lifecycle

## The Lifecycle

```mermaid
stateDiagram-v2
  [*] --> absent
  absent --> live: create
  live --> live: replace definition or rules
  live --> archived: archive
  archived --> live: restore
```

## Transitions Explained

Creation opens use of a field. Compatible edits maintain its contract; archival removes current use and rules while preserving stored values. Restoration rechecks the parent category and returns the field without earlier rules.

## What Cannot Happen

Archival retains the key and values; changes to archived definitions require restoration.

## Concurrency and Timing

Replacement uses the version read. Archival and restoration serialize on the definition row without an expected version, and restoration additionally rechecks current type eligibility. Search job starts follow the archive commit and may fail independently.
