Skip to content

A deleted runtime object is still served by SchemaRegistry.getObject — the registry heal reaches the metadata map but never objectContributors #6808

Description

@os-zhuang

Recording only, per Prime Directive #10 — found while implementing #6621 (PR #6807), on a different function than the one that card touches, and pre-existing on both callers.

The fact (measured on origin/main @ 4bb6f01af)

deleteMetaItem ends its repository delete with restoreArtifactRegistryView — the #6687 three-tier heal. That walk operates entirely on SchemaRegistry's generic metadata map: removeRuntimeShadow, registerItem, and removeOverlayEntry all address this.metadata.get(type).

An object lives in two places. applyRegistryWriteThroughapplyObjectRegistryMutation writes both:

this.engine.registry.registerItem(request.type, request.item, 'name');   // metadata map
this.engine.registry.registerObject({ ...item, _provenance: 'org' }, pkg); // objectContributors

The heal only undoes the first. Measured with the real SysMetadataRepository over an in-memory engine, a runtime-created object saved and then deleted:

BEFORE delete: metadata['object'] keys -> ["myapp_invoice"] | objectContributors -> ["myapp_invoice"]
AFTER  delete: metadata['object'] keys -> []                | objectContributors -> ["myapp_invoice"]

registry.getObject('myapp_invoice')      -> STILL SERVED
registry.getItem('object','myapp_invoice') -> STILL SERVED   (getItem special-cases the
                                                              object type back to getObject)

getObject is the surface data CRUD dispatches on, so the half that survives is the load-bearing half: the sys_metadata row is gone and the object is still resolvable, syncable and writable for the life of the process. The half that is cleaned (metadata['object']) backs listing, which is the less consequential of the two.

Why there is no one-line fix

SchemaRegistry has no per-(type, name) object unregister at all — the only removal verb is unregisterObjectsByPackage(packageId, force) (packages/objectql/src/registry.ts:1339), which is package-wide and throws when an owner still has extenders. So closing this is a registry API change (a name-addressed unregisterObject honouring the ADR-0029 single-owner/extender rules), not a protocol-side call.

Reachability

Live on the ordinary Studio path: delete a runtime-created object, and the running process keeps dispatching it. Both delete-shaped callers are affected identically, because both run the same heal:

Not the same as #6725

#6725 is the write side of the same map-vs-contributors asymmetry (MetadataFacade.register('object', …) writing where its own reads do not look), and is dormant in-tree. This one is the removal side, on the shipped protocol path, and is reachable today. Same underlying split in SchemaRegistry, two different seams — a fix for either does not close the other, though a name-addressed object unregister would be the natural place to settle both.

Related: #6687, #5079, #4521, #6621 (PR #6807), #6725, ADR-0029.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions