Skip to content

[Registry] The overlay row's own package_id is erased before registerItem, so the registry cannot tell a customization from a name collision #12431

Description

@os-warren

The observation

Found while implementing #12027 (PR #12430 — making the [Registry] Collision warning
order-symmetric). Not a defect in that card's surface; recorded here because it is the
reason that card's new diagnostic cannot be narrower than it is.

sys_metadata rows carry a package_id column, and boot hydration reads it:
loadMetaFromDb passes record.package_id into hydrateOverlayIntoRegistry
(packages/metadata-protocol/src/protocol.ts). But that value is used ONLY to scope the
artifact lookup. The registration itself is package-less:

constartifact=this.lookupArtifactItem(type,name,options.packageId??undefined);registry.registerItem(type,mergeArtifactProtection(data,artifact),'name'asany);

mergeArtifactProtection then stamps the ARTIFACT's _packageId / _provenance /
_lock envelope onto the overlay body (ADR-0010 section 3.3 — correct, and load-bearing
for lock resolution). Consequence: by the time the row reaches SchemaRegistry.registerItem,
a row that is a deliberate customization of package P's item and a row that was authored
independently and merely collides with P's name are byte-identical — both arrive under the
bare key carrying P's _packageId and _provenance: 'package'.

Why it matters

The two populations have opposite operator meanings. A customization shadowing its own
package baseline is ADR-0005 working as designed. An independently authored row shadowing a
packaged name is an accident, and the packaged definition is dead on every boot from then on.

The existing artifact-second collision guard CAN separate them, and does — its
!dbOnly._packageId test narrows to a package-LESS row. The overlay-second direction
(#12027) cannot, so its message has to state both readings instead of naming the one that
happened. Measured on a real @objectstack/example-crm boot: one customized view produced
4 registrations that are indistinguishable at this seam from 4 accidental collisions.

The shape a fix would take, if it is ever wanted

Forward the row's own binding to the registry as an overlay-binding HINT — a new options
argument, deliberately NOT registerItem's existing 4th parameter, which selects the
composite storage key and would therefore move precedence. With it the diagnostic could say
"a row bound to no package is shadowing package P's item" (accident) versus "P's own
customization is in effect" (normal), and the normal one could stop printing.

Implementation-first, and probably not now: it widens a hot signature to sharpen one log
line, and the log line is already accurate about the consequence. Filed so the constraint
is written down rather than rediscovered by the next person who tries to narrow this
warning.

Read at

origin/maincdbd9204b6. Dedup search was NOT performed through the prescribed channel:
REST list endpoints answer 403 from this seat (#12123 — measured again during this task,
GitHub access is not enabled for this session) and MCP search_issues is unreliable for
known hits (#11835). A local grep of the tree found no existing note of this constraint.
Unassigned, not claimed.

Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions