---
id: STATE-PFM-MTD-003
title: Custom object definition lifecycle
object: DOM-PFM-MTD-004
product_area: platform
module: metadata
revision: 1
status: draft
design_intent: confirmed
origin: Custom objects architecture (approved 2026-10-18); PLAT-M5 D3 (readable, read-only) and B4 (no activation
  rule for placement or owner), services-platform:docs/milestones/PLAT-M5-custom-records.md#Decisions
implementation_evidence: observed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
states:
- name: absent
  meaning: No object with this key has been created locally.
- name: draft
  meaning: The definition is configurable and describable but ordinary record writes are unavailable.
- name: active
  meaning: Ordinary records, datasets, readable navigation entries and relationship endpoints can use the object.
- name: archived
  meaning: Configuration is retained but hidden from current use; existing records remain readable by identity and
    reject direct changes.
transitions:
- from: absent
  to: draft
  trigger: An administrator creates an object definition.
  guard: The object and plural keys satisfy grammar and remain unused, and the workspace holds fewer than fifty
    objects.
  side_effects:
  - Create a draft and audit the configured field names.
  who: Workspace Administrator (platform.custom-objects.create)
  provenance: test
  source: services-platform:modules/custom-objects/tests/integration/services/persistence/custom-objects.integration.spec.js#refuses
    the fifty-first object of a tenant, w
- from: draft
  to: active
  trigger: An administrator activates a draft.
  guard: The version is current, naming properties are valid and any selected owner type is active and permits one
    owner.
  side_effects:
  - Commit activation with Explorer recipes; the console applies its browser-held owner choice through later calls.
  who: Workspace Administrator (platform.custom-objects.update)
  provenance: test
  source: services-platform:modules/custom-objects/tests/integration/services/persistence/custom-objects.integration.spec.js#refuses
    activating a draft whose display prop
- from: draft
  to: archived
  trigger: An administrator archives a draft.
  guard: The version matches and no lock blocks archival.
  side_effects:
  - Record the lifecycle change and security audit.
  who: Workspace Administrator (platform.custom-objects.delete)
  provenance: observed
  source: services-platform:modules/custom-objects/services/persistence/custom-objects.ts:archiveCustomObject
- from: active
  to: archived
  trigger: An administrator archives an active definition.
  guard: The version matches and no lock applies; shared object reads held by record writes finish before the exclusive
    change.
  side_effects:
  - Make direct record writes, layouts, navigation and dataset access unavailable; retain stored Explorer recipes
    and audit the action.
  who: Workspace Administrator (platform.custom-objects.delete)
  provenance: test
  source: services-platform:modules/layouts/tests/e2e/custom-object-forms.e2e.spec.js#answers 404 for a stored form
    once its custom object is archived
- from: archived
  to: draft
  trigger: An administrator restores the definition.
  guard: The definition is archived and the supplied version is current.
  side_effects:
  - Return it as a draft requiring another activation.
  who: Workspace Administrator (platform.custom-objects.delete)
  provenance: test
  source: services-platform:modules/custom-objects/tests/integration/services/persistence/custom-objects.integration.spec.js#restores
    an archived object as a draft, keepi
- from: draft
  to: draft
  trigger: An administrator replaces draft settings.
  guard: The version matches and valid settings preserve identity and draft status.
  side_effects:
  - Store the settings and audit changed fields.
  who: Workspace Administrator (platform.custom-objects.update)
  provenance: test
  source: services-platform:modules/custom-objects/tests/e2e/custom-objects.e2e.spec.js#answers 400 to a replacement
    that changes the status, which only the actions chang
- from: active
  to: active
  trigger: An administrator replaces active settings.
  guard: The version matches, identities and status stay fixed, naming remains valid and disabling publication leaves
    no published records.
  side_effects:
  - Store the settings and audit changed fields.
  who: Workspace Administrator (platform.custom-objects.update)
  provenance: test
  source: services-platform:modules/custom-objects/tests/integration/services/persistence/custom-objects.integration.spec.js#refuses
    turning publication off while the rec
invalid:
- from: active
  to: draft
  why: Explicit lifecycle actions preserve the definition; restoration yields a draft before activation, and replacement
    cannot change status.
- from: archived
  to: active
  why: Explicit lifecycle actions preserve the definition; restoration yields a draft before activation, and replacement
    cannot change status.
- from: archived
  to: archived
  why: Explicit lifecycle actions preserve the definition; restoration yields a draft before activation, and replacement
    cannot change status.
- from: active
  to: absent
  why: Explicit lifecycle actions preserve the definition; restoration yields a draft before activation, and replacement
    cannot change status.
concurrency: Lifecycle actions lock and version the object. Ordinary record writes hold a shared object lock, forcing
  archival to wait. Browser-managed ownership is applied after activation and can fail separately.
invariants:
- INV-DOM-PFM-MTD-004-01
- INV-DOM-PFM-MTD-004-02
- INV-DOM-PFM-MTD-004-03
- INV-DOM-PFM-MTD-004-05
- INV-DOM-PFM-MTD-004-06
evidence:
- 'services-platform at ad3f6a65: modules/custom-objects/services/persistence/custom-objects.ts (createCustomObject,
  updateCustomObject, activateCustomObject, archiveCustomObject, restoreCustomObject); services/persistence/custom-object-records.ts:sharedActiveObject;
  docs/custom-objects.md §1'
- 'react-console at baf6dae3: lib/custom-objects/designer/save.ts:activateObject'
---

# Custom object definition lifecycle

## The Lifecycle

```mermaid
stateDiagram-v2
  [*] --> absent
  absent --> draft: create
  draft --> draft: replace settings
  draft --> active: activate
  active --> active: replace settings
  draft --> archived: archive
  active --> archived: archive
  archived --> draft: restore
```

## Transitions Explained

Administrators prepare definitions in draft before activation validates naming. Archival retains the register, and restoration returns to draft for another activation check.

## What Cannot Happen

Explicit lifecycle actions preserve the definition; restoration yields a draft before activation, and replacement cannot change status.

## Concurrency and Timing

Lifecycle actions lock and version the object. Ordinary record writes hold a shared object lock, forcing archival to wait. Browser-managed ownership is applied after activation and can fail separately.
