Skip to content

MetadataFacade.unregisterPackage removes only object contributors — every non-object item the package shipped stays registered #7221

Description

@os-zhuang

Observation-class finding, filed per Prime Directive #10 while implementing #6725 (PR #7211). Unassigned, deliberately not queued. Deliberately kept OUT of #6725: that card is the register/read split for the object type, this is a different verb with a different root cause, and folding it in would have widened the card.

The fact (read on origin/main @ 55da611)

MetadataFacade.unregisterPackage (packages/objectql/src/metadata-facade.ts) is one line:

async unregisterPackage(packageName: string): Promise<void> {
  this.registry.unregisterObjectsByPackage(packageName);
}

SchemaRegistry.unregisterObjectsByPackage walks objectContributors only. So for a facade-backed metadata slot, "unregister all metadata from a package" leaves behind:

  1. Every non-object item the package shipped — its page, view, flow, app, api … entries all live in the generic metadata map under composite ${packageId}:${name} keys (registerItem), and nothing here touches that map. They stay resolvable through get, list, listNames and exists after the package is gone.
  2. The generic-map half of its objects. An object is written into two places (the invariant SchemaRegistry.unregisterObject's header states); this verb reaches one. Post-MetadataFacade.register('object', …) writes where neither of its own object reads look #6725 the facade's own register('object', …) writes both, so this leaves a genuine orphan rather than a theoretical one.

Item 1 is the load-bearing half: it is not a stale-cache nuisance, it is uninstall leaving a package's UI and API metadata installed.

Reachability

Same reachability as #6725, and it is the same argument: new MetadataFacade(...) appears nowhere on main outside packages/objectql's own tests, so nothing in-tree calls this. But MetadataFacade is exported from @objectstack/objectql's root and core entrypoints, and unregisterPackage is a declared (optional) member of IMetadataService — so a downstream host that installs the facade and uninstalls a package gets a half-uninstall, silently.

MetadataManager (packages/metadata) is the in-tree occupant of the slot and does not share this shape.

Why it is not a one-line fix

SchemaRegistry has no "remove every generic item owned by package X" verb. The composite key format (${packageId}:${name}) makes the scan mechanically easy, but two questions need a ruling before writing it:

  • Bare-key overlays. ADR-0005 runtime/DB rows are stored under the bare name with no package id. An uninstall must not take a tenant's overlay with it — but leaving it means the overlay outlives the item it overlays. unregisterObjectsByPackage's force parameter is the nearest precedent for who decides.
  • Where the verb belongs. On SchemaRegistry (shared by every registry-direct caller, including uninstallPackage) or private to the facade. The registry is the better home if uninstallPackage has the same gap — that wants measuring, not assuming.

Dispositions worth pricing (no recommendation forced)

  1. Add SchemaRegistry.unregisterItemsByPackage(packageId) and call it here alongside the object verb — closes it for every caller, needs the bare-key ruling.
  2. Scan-and-delete inside the facade only — narrower blast radius, but a second copy of the ownership rule, which is the drift A deleted runtime object is still served by SchemaRegistry.getObject — the registry heal reaches the metadata map but never objectContributors #6808 was about.
  3. Document the member as objects-only — cheapest, and honest, but IMetadataService names it "Unregister all metadata items from a specific package".

Refs #6725, PR #7211, #6808, ADR-0005.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions