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 (FieldSchema — name, 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.
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,saveMetaItemPhase 3a-destructive (currently around:13157):The
fieldhalf of that disjunction is inert, for two reasons that are independent of each other — either alone is sufficient.Reason 1 — a
fieldbody has nofieldsmap to diffdetectDestructiveObjectChanges(prev, next)(same file, currently around:3141) readsprev.fieldsandnext.fieldsand returns[]when neither is an object. Afieldmetadata item's body IS a single field definition (FieldSchema—name,type,label, …), not a container carrying afieldsmap. So forsingularType === 'field'the detector returns an empty array unconditionally and the gate never fires.Reason 2 —
fieldcannot be written through this door at allpackages/spec/src/kernel/metadata-plugin.zod.ts:733declares the type code-only:Measured through the real
saveMetaItemagainst a seededsys_metadatarow oftype: 'field', the write is refused before persistence with: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
fieldspelling 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
fielda 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 iffieldever becomes runtime-writable. No behaviour depends on the answer today.Measured on
origin/mainat490879ad0f.