---
id: STATE-PFM-MTD-007
title: Custom record file lifecycle
object: DOM-PFM-MTD-005
product_area: platform
module: metadata
revision: 1
status: draft
design_intent: confirmed
origin: 'services-platform:docs/assets.md §7 (Andrew, 2026-10-08: deleted and replaced files move to a deleted folder
  kept 30 days; 2026-10-11: cleanup is a queue); PLAT-M10 D1 (copy at save), plan approved 2026-10-19'
implementation_evidence: partially-observed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
states:
- name: preparing
  meaning: The asset pipeline is copying, importing or checking a file before the record transaction.
- name: ready
  meaning: A prepared permanent copy awaits attachment by a successful save.
- name: attached
  meaning: A committed record currently uses the prepared file.
- name: detached
  meaning: A committed replacement, clearing or deletion removed the record reference; retirement work remains.
- name: abandoned
  meaning: Preparation or persistence failed, leaving an uncommitted copy for later disposal.
- name: discarding
  meaning: A cleanup sweep has claimed disposal work for its consumer.
- name: retired
  meaning: Cleanup has moved the file out of permanent service into the deleted prefix, where storage retention
    lasts thirty days.
- name: discarded
  meaning: Cleanup has removed a copy that never became committed record content.
- name: failed
  meaning: The final cleanup attempt failed and recorded its reason; ordinary sweeps no longer offer that entry.
transitions:
- from: preparing
  to: ready
  trigger: File preparation completes successfully.
  guard: The staging reference meets uploader, media and size checks and finishes within the save deadline.
  side_effects:
  - Write the prepared file at a fresh permanent record key.
  who: Asset pipeline, during the save
  provenance: documented
  source: services-platform:docs/assets.md#5. Prepare, then commit
- from: ready
  to: attached
  trigger: The record save commits a prepared reference.
  guard: Each changed attachment has a verified prepared value.
  side_effects:
  - Mark the new file attached and mark any replaced file detached in the same transaction.
  who: Custom record store
  provenance: observed
  source: services-platform:modules/custom-objects/services/persistence/custom-object-records.ts:createRecord
- from: preparing
  to: abandoned
  trigger: File preparation is refused or fails.
  guard: No additional state guard applies.
  side_effects:
  - Refuse the save at its property and abandon the operation’s prepared entries.
  who: Asset pipeline
  provenance: documented
  source: services-platform:docs/assets.md#7. The lifecycle record and cleanup
- from: ready
  to: abandoned
  trigger: Persistence rejects the prepared save.
  guard: No additional state guard applies.
  side_effects:
  - Abandon prepared copies while retaining the preceding record and attachment.
  who: Asset pipeline
  provenance: documented
  source: services-platform:docs/assets.md#7. The lifecycle record and cleanup
- from: attached
  to: detached
  trigger: Committed content stops referencing the file.
  guard: Replacement, clearing or deletion commits.
  side_effects:
  - Mark the entry detached; the permanent copy waits for retirement.
  who: Custom record store
  provenance: observed
  source: services-platform:modules/custom-objects/services/persistence/custom-object-records.ts:deleteWithRules
- from: detached
  to: discarding
  trigger: A scheduled cleanup sweep would claim a detached entry.
  guard: Platform must deploy the cleanup binding; this pinned code does not provide that deployment.
  side_effects:
  - No Platform transition runs today; detached entries remain waiting.
  who: Cleanup sweep (not deployed for Platform)
  provenance: documented
  source: services-platform:docs/debt/custom-object-asset-cleanup.md
- from: abandoned
  to: discarding
  trigger: A cleanup sweep would claim an abandoned copy after its grace period.
  guard: Platform cleanup must be deployed, which this pinned binding lacks.
  side_effects:
  - No Platform transition runs today; abandoned copies remain.
  who: Cleanup sweep (not deployed for Platform)
  provenance: documented
  source: services-platform:docs/debt/custom-object-asset-cleanup.md
- from: discarding
  to: retired
  trigger: A cleanup consumer would retire detached content.
  guard: Cleanup is deployed and no record still references the copy.
  side_effects:
  - Move the file to the deleted prefix and stop permanent serving.
  who: Cleanup consumer (not deployed for Platform)
  provenance: documented
  source: services-platform:docs/assets.md#7. The lifecycle record and cleanup
- from: discarding
  to: discarded
  trigger: A cleanup consumer would dispose of abandoned content.
  guard: Cleanup is deployed and no record references the uncommitted copy.
  side_effects:
  - Delete the unused copy.
  who: Cleanup consumer (not deployed for Platform)
  provenance: documented
  source: services-platform:docs/assets.md#7. The lifecycle record and cleanup
- from: discarding
  to: attached
  trigger: A cleanup consumer would discover a surviving record reference.
  guard: Cleanup is deployed and reference checking finds the file in use.
  side_effects:
  - Repair the lifecycle entry to attached.
  who: Cleanup consumer (not deployed for Platform)
  provenance: documented
  source: services-platform:docs/assets.md#7. The lifecycle record and cleanup
- from: discarding
  to: failed
  trigger: A final queue attempt would fail.
  guard: Cleanup is deployed and its retry limit is reached.
  side_effects:
  - Record failed disposal and route the message to the alarmed dead-letter queue.
  who: Cleanup consumer (not deployed for Platform)
  provenance: documented
  source: services-platform:docs/assets.md#7. The lifecycle record and cleanup
invalid:
- from: retired
  to: attached
  why: Only a ready file reference may attach through a successful commit; a removed or abandoned copy is not an
    upload for reuse.
- from: abandoned
  to: attached
  why: Only a ready file reference may attach through a successful commit; a removed or abandoned copy is not an
    upload for reuse.
concurrency: Preparation occurs outside record transactions, bounded to three files concurrently and a twenty-second
  deadline. Only ready references commit. Cleanup phases describe the shared contract but do not run under the pinned
  Platform deployment binding.
evidence:
- 'services-platform at ad3f6a65: modules/custom-objects/services/assets/custom-object-record-assets.ts (header:
  no cleanup functions yet); db/managed-assets.table.ts; services/persistence/custom-object-records.ts; docs/assets.md
  §5 and §7; docs/debt/custom-object-asset-cleanup.md'
---

# Custom record file lifecycle

## The Lifecycle

```mermaid
stateDiagram-v2
  [*] --> preparing
  preparing --> ready
  preparing --> abandoned
  ready --> attached: record commits
  ready --> abandoned: commit fails
  attached --> detached: replaced, cleared or record deleted
  detached --> discarding: sweep (not deployed)
  abandoned --> discarding: sweep after grace (not deployed)
  discarding --> retired
  discarding --> discarded
  discarding --> attached: still referenced
  discarding --> failed
```

## Transitions Explained

A save prepares copies before persistence, then attaches ready references in its transaction. Failed preparation or persistence abandons copies, and removed references detach them. Later disposal states describe shared cleanup that Platform has not deployed.

## What Cannot Happen

Only a ready file reference may attach through a successful commit; a removed or abandoned copy is not an upload for reuse.

## Concurrency and Timing

Preparation occurs outside record transactions, bounded to three files concurrently and a twenty-second deadline. Only ready references commit. Cleanup phases describe the shared contract but do not run under the pinned Platform deployment binding.
