---
id: DOM-PFM-MTD-002
title: Custom Field
realises: CON-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 (Andrew, 2026-10-08 and 2026-10-09; final acceptance
  2026-10-08); custom objects architecture D-03, D-10, D-16 and D-20 (approved 2026-10-18; docs/milestones/PLAT-M5-custom-records.md#Decisions);
  R2-ARCH-001 v1.0 (Andrew, 2026-10-19); PLAT-M10 D1 and D4 (docs/milestones/PLAT-M10-image-and-file-fields.md,
  plan approved 2026-10-19)'
implementation_evidence: observed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
owned_by: SVC-PFM-MTD-metadata
readable_by:
- SVC-PFM-MTD-custom-objects
- SVC-PFM-MTD-files
lifecycle: STATE-PFM-MTD-002
custom_fields: none
identity: The key and value type form a durable contract. Labels, compatible displays and widening settings may
  change; rules are replaced as a complete set.
attributes:
- name: record type
  kind: reference
  required: true
  computed: false
  notes: A new or changed definition targets an eligible live registered type or a draft or active custom object
    in the workspace.
- name: key
  kind: text
  required: true
  computed: false
  notes: The identity key is fixed. Service validation and a database constraint apply the grammar appropriate to
    the record type.
- name: label and description
  kind: text
  required: true
  computed: false
  notes: Labels and explanatory text may be revised for staff and member presentation.
- name: data type
  kind: enum
  required: true
  computed: false
  notes: The chosen value type is one of seventeen and cannot be converted implicitly to another type.
- name: display type
  kind: enum
  required: true
  computed: false
  notes: Presentation uses a display compatible with the fixed value type; switching display does not retype values.
- name: configuration
  kind: collection
  required: false
  computed: false
  notes: Settings govern size, range, precision, fixed currency, stable options, multiplicity, reference target
    and media types. Updates can broaden permitted values; options are archived rather than removed.
- name: required level
  kind: enum
  required: true
  computed: false
  notes: Requirements range from API through console and portal to optional. Omitting the level in replacement makes
    it optional and leaves existing values intact; the record owner enforces API requirements.
- name: place and group
  kind: text
  required: false
  computed: false
  notes: Ordering and section labels arrange generated presentations. Replacement clears either setting when omitted.
- name: rules
  kind: collection
  required: false
  computed: false
  notes: 'The complete rule set permits one of each kind: requirement, visibility, uniqueness, default and exposure.
    Conditions use other fields of the same type and fit sixteen kilobytes; uniqueness is limited to custom objects.
    Defaults use a literal or a supported date or caller token, and absent exposure means staff.'
- name: archived at
  kind: date
  required: false
  computed: false
  notes: Archival marks the retained definition unavailable to descriptions and new values; restoration removes
    that mark.
- name: version, lock and modification facts
  kind: computed
  required: true
  computed: true
  notes: The system maintains versions, locks and modification facts. Replacement requires the read version; archival
    checks locks without a version, and restoration checks current eligibility.
- name: image and file fields · upload issue
  kind: collection
  required: false
  computed: true
  notes: Files records each issued staging location with its caller, type, field path, declared media type, byte
    size and fifteen-minute expiry. The issue ledger has no pruning process.
relationships:
- to: DOM-PFM-MTD-001
  cardinality: many-to-one
  ownership: The custom field owns its type reference, with eligibility determined by the referenced category.
  cross_service: false
- to: DOM-PFM-MTD-004
  cardinality: many-to-one
  ownership: Metadata stores properties under the custom object's identity; the object selects naming properties
    from that set.
  cross_service: false
- to: DOM-PFM-MTD-005
  cardinality: one-to-many
  ownership: The record owner stores values under property keys and validates current definitions. Native applications
    retain their own value stores.
  cross_service: false
invariants:
- id: INV-DOM-PFM-MTD-002-01
  statement: Field keys satisfy the type-specific grammar, avoid standard and reserved keys, and remain reserved
    after archival within the workspace and type.
  enforced_at:
  - service
  - database
  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
- id: INV-DOM-PFM-MTD-002-02
  statement: Field replacement preserves the identity key and stored value type.
  enforced_at:
  - api
  - service
  provenance: test
  source: services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    a change of data type, the row unchanged
- id: INV-DOM-PFM-MTD-002-03
  statement: Configuration updates cannot tighten value bounds or precision, remove options, reduce multiplicity,
    change reference targets or remove accepted media types.
  enforced_at:
  - service
  provenance: test
  source: services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.integration.spec.js#refuses
    removing an option, saying to archive it instead
- id: INV-DOM-PFM-MTD-002-04
  statement: New and replacement definitions require an eligible live registered type or a draft or active local
    custom object; retained fields can survive later loss of that eligibility.
  enforced_at:
  - service
  provenance: observed
  source: services-platform:modules/metadata/services/persistence/custom-fields.ts:requireLiveOrTenantObject
- id: INV-DOM-PFM-MTD-002-05
  statement: The custom-object property limit is two hundred, including archived definitions.
  enforced_at:
  - service
  provenance: test
  source: services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.custom-objects.integration.spec.js#refuses
    the 201st property, archived
- id: INV-DOM-PFM-MTD-002-06
  statement: Each rule kind occurs once, conditions use compatible operators on other fields of the same type, and
    validation failure preserves the preceding set.
  enforced_at:
  - service
  - database
  provenance: test
  source: services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.rules.integration.spec.js#refuses
    a condition naming a field the object
- id: INV-DOM-PFM-MTD-002-07
  statement: Enabling uniqueness requires a custom-object property whose existing values contain no duplicates.
  enforced_at:
  - service
  provenance: test
  source: services-platform:modules/custom-objects/tests/integration/services/persistence/custom-object-records.integration.spec.js#refuses
    a unique rule on a property tw
- id: INV-DOM-PFM-MTD-002-08
  statement: Archival refuses a custom-object property still referenced by another condition and removes the archived
    field's own rules atomically.
  enforced_at:
  - service
  - database
  provenance: test
  source: services-platform:modules/metadata/tests/integration/services/persistence/custom-fields.rules.integration.spec.js#refuses
    the archive naming each dependent prop
- id: INV-DOM-PFM-MTD-002-09
  statement: A rule write advances the workspace query-description stamp in its transaction.
  enforced_at:
  - database
  provenance: test
  source: services-platform:modules/metadata/tests/integration/db/custom-field-rules.table.integration.spec.js#moves
    the tenant’s query description stamp when a rule is w
physical:
- platform.custom_fields
- platform.custom_field_rules
- platform.file_uploads
evidence:
- 'services-platform at ad3f6a65: modules/metadata/services/persistence/custom-fields.ts; services/field-lifecycle/archive-field.ts;
  db/custom-fields.table.ts, custom-field-rules.table.ts; modules/files/services/uploads/issue-upload.ts, services/persistence/file-uploads.ts,
  db/file-uploads.table.ts; docs/metadata.md; docs/files.md; docs/custom-objects.md §2 and §3'
- 'react-console at baf6dae3: lib/configuration/fields/live-adapter.ts, field-rules.ts'
---

# Custom Field

## What It Models

A custom field defines information a workspace adds to an eligible category. For custom objects, these definitions provide the record properties themselves.

## Identity and Attributes

The key and value type form a durable contract. Labels, compatible displays and widening settings may change; rules are replaced as a complete set.

## Relationships

A field belongs to one record category. Its values stay with the application owning the records, and object naming selects from the property definitions.

## Invariants

Validation protects existing values, reserved keys and coherent conditions. The custom-record store supplies the uniqueness check and maintains value reservations.

## How It Is Stored and Shared

Metadata writes definitions and rules. Owning services read those definitions while validating their own saves; Files separately records issued upload locations.

## Open Design Questions

Native-field uniqueness, organization defaults and HTML handling have unresolved limits already recorded in planning. The upload ledger also needs a retention decision.
