Skip to content

[finding] saveMetaItem's Phase 3a-destructive gate declares a field limb that can never produce a finding — two independent reasons #11014

Description

@os-elon

Observed while inventorying the DESTRUCTIVE_CHANGE 409's wire faces for #10886. Not a defect anyone can trip — a declared limb with no enforcement behind it, filed so it is not rediscovered as a bug.

The declaration

packages/metadata-protocol/src/protocol.ts, saveMetaItem Phase 3a-destructive (currently around :13157):

if(!request.force&&(singularType==='object'||singularType==='field')){

The field half of that disjunction is inert, for two reasons that are independent of each other — either alone is sufficient.

Reason 1 — a field body has no fields map to diff

detectDestructiveObjectChanges(prev, next) (same file, currently around :3141) reads prev.fields and next.fields and returns [] when neither is an object. A field metadata item's body IS a single field definition (FieldSchemaname, type, label, …), not a container carrying a fields map. So for singularType === 'field' the detector returns an empty array unconditionally and the gate never fires.

Reason 2 — field cannot be written through this door at all

packages/spec/src/kernel/metadata-plugin.zod.ts:733 declares the type code-only:

{ type: 'field', label: 'Field', …, supportsOverlay: false, allowOrgOverride: false, allowRuntimeCreate: false, … }

Measured through the real saveMetaItem against a seeded sys_metadata row of type: 'field', the write is refused before persistence with:

code = NOT_CREATABLE
[not_creatable] Metadata type 'field' is code-only: the metadata-type registry declares
allowRuntimeCreate=false and allowOrgOverride=false, so it cannot be created through the
runtime metadata API …

So no caller can reach the destructive gate with singularType === 'field' even if reason 1 were fixed.

Why it is worth recording rather than silently deleting

The limb reads as coverage. #10886's inventory had to establish, per face, exactly which types can reach this gate, and the field spelling in the condition is the one thing that makes the answer look wider than it is — an inventory that trusted the condition would have had to chase a face population that does not exist.

Disposition is a judgement call and deliberately not made here: dropping the dead limb (Prime Directive #10's trim-it arm) versus giving field a real destructive diff (a type change on a standalone field IS a data-loss event in principle) point in opposite directions, and the second only makes sense if field ever becomes runtime-writable. No behaviour depends on the answer today.

Measured on origin/main at 490879ad0f.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions