Skip to content

org-override-registry-gate: the field overlay lock is not enforced — an artifact-backed field PUT is accepted 200 (and is inert) #7743

Description

@huangyiirene

Symptom

The registry declares field with allowOrgOverride=false, and the field is artifact-backed — yet the live route accepts the overriding write.

  • PUT /api/v1/meta/field/showcase_task.title {name:'title', label:'Tampered', type:'text'} with an admin bearer → 200 state:'active'. Reproduced on showcase_task.status.
  • The row is persisted and reads back with _diagnostics.valid=true.
  • And the accepted write is inert: GET /meta/object/showcase_task still reads title.label = 'Title'. A brand-new field written the same way never appears in the object's fields either.

So the door answers success twice over — accepted, and stored valid — for a write the registry forbids and the runtime ignores. The object, view, dashboard and job variants all behave correctly in the same run, and locked-override vs open-create were proven independent there.

Root cause (suspected — located, not proven by a fix)

The gate keys on isArtifactBacked(type, name), which resolves through registry.getArtifactItem(singular, name, currentPackageId) ?? registry.getArtifactItem(type, name, currentPackageId) (packages/metadata-protocol/src/protocol.ts:8678-8679). Fields are not registered as standalone field artifact items — they live inside the object — so getArtifactItem('field', 'showcase_task.title') misses. With the artifact lookup empty, the write is classified as a runtime-only create, and the registry entry for field carries allowRuntimeCreate: true:

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

packages/spec/src/kernel/metadata-plugin.zod.ts:629 on origin/main.

allowOrgOverride: false is never consulted because nothing on this path believes an artifact is being overridden.

Why the pin stayed green: overlay-precedence.test.ts (27 passing) pins the protocol-level denial. The live field route is not in its coverage, so the gate is proven where it is exercised and absent where it is used.

Stale-premise check: re-verified on objectstack origin/main (00e9196) — the isArtifactBacked resolution and the field registry row are both unchanged.

Reproduction

  1. Boot the showcase with writable runtime packages; obtain an admin bearer.
  2. PUT /api/v1/meta/field/showcase_task.title with {name:'title', label:'Tampered', type:'text'}200 state:'active'.
  3. GET /api/v1/meta/field/showcase_task.title → the row exists, _diagnostics.valid=true.
  4. GET /api/v1/meta/object/showcase_tasktitle.label is still 'Title' — the accepted write changed nothing.
  5. Contrast: the same override attempt against object / view / dashboard / job is correctly refused.

Note for whoever reproduces the object contrast: a trimmed object body hits DESTRUCTIVE_CHANGE first — the full body is needed to reach the registry gate.

Source

Extracted from the QA run #7695 (framework 92f26f7, console 09987b680).

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions