Blocked-by: #7705
> Status 2026-08-11 (engine-core seat): half (A) and the envelope part of half (B) LANDED via PR #7700 (8f1851ea7). What remains open on this card is the persistence half of (B) only, transferred to the metadata lane as #7705. This card closes when #7705 lands. See the ACCEPT comment below for the full verification record.
Two halves of one gap: a package lifecycle operation moves the registry/UI surfaces but never propagates to the metadata store or the data plane. Filed as one card because both are the same missing propagation, on the two operations that need it.
Symptom
(A) Disable is not an enforcement for objects. ✅ FIXED by PR #7700. With the owning package at status:'disabled':
GET /api/v1/data/<object> still answers 200 with all rows. → now 404 OBJECT_PACKAGE_DISABLED
/meta/objects still lists the object. → now dropped from the listing
- Nav and views are correctly dropped — so disable is honoured on some surfaces and not others.
- The console renders the live list too, so this is not an API-only artefact.
Reproduced over two disable/enable cycles.
Expected: a disabled package's objects are dropped from the metadata registry and refuse on the data plane, consistently with the nav/view surfaces that already honour the status.
(B) Uninstall leaves orphaned metadata. ⚠️ HALF fixed. DELETE /packages/:id answers 200, with:
persisted: { success: false, deletedCount: 0 }
The package disappears from /packages, but 7 sys_metadata rows survive and /meta/* keeps serving them. Reproduced with a second, single-view package.
Expected: either the persisted delete succeeds and the metadata rows go with the package, or the failed persistence is reported as a failure instead of being wrapped in a 200.
Honest negative, recorded so nobody re-derives it: disable destroys no data — 3 rows before, during and after the disabled window. Now pinned by packages/objectql/src/package-disable-enforcement.test.ts.
Root cause
Not located by the report beyond the symptoms above. Two facts it does pin down:
- The
persisted:{success:false,deletedCount:0} envelope means the delete's own persistence step reported failure and the route answered 200 anyway — the status code and the payload disagree.
- Disable is enforced at the nav/view layer but not at the registry or data layer, so the status is consulted in some readers and not others rather than being missing entirely.
Filed domain:engine-core because the surviving surfaces are registry-backed reads; re-route if the fix lands in the /packages route layer (packages/rest) or in the metadata repository instead.
> Measured answer to that last sentence: the disable asymmetry was listItems' early return for object/objects (registry.ts) plus the same exemption in getMetaItems (protocol.ts) — engine-core, as filed. The envelope was packages/runtime (cli lane, declared on #6024). The persistence is packages/metadata-protocol (#7705).
Related
Reproduction
(A)
- Install a package that ships an object with rows.
PATCH the package to status:'disabled'.
GET /api/v1/data/<object> → 200, all rows. GET /meta/objects → object still listed. Nav and views → correctly gone.
- Re-enable and repeat — reproduced over two cycles. Row count is 3 before, during and after.
(B)
- Install a package (repeated with a second, single-view package).
DELETE /packages/:id → 200 with persisted:{success:false,deletedCount:0}.
GET /packages → gone. Query sys_metadata → 7 rows survive. GET /meta/* → still serving them.
Source
Extracted from the QA run #7514 (framework a86db17, console 09987b68).
Blocked-by: #7705
> Status 2026-08-11 (engine-core seat): half (A) and the envelope part of half (B) LANDED via PR #7700 (
8f1851ea7). What remains open on this card is the persistence half of (B) only, transferred to the metadata lane as #7705. This card closes when #7705 lands. See the ACCEPT comment below for the full verification record.Two halves of one gap: a package lifecycle operation moves the registry/UI surfaces but never propagates to the metadata store or the data plane. Filed as one card because both are the same missing propagation, on the two operations that need it.
Symptom
(A) Disable is not an enforcement for objects. ✅ FIXED by PR #7700. With the owning package at
status:'disabled':GET /api/v1/data/<object>still answers 200 with all rows. → now404 OBJECT_PACKAGE_DISABLED/meta/objectsstill lists the object. → now dropped from the listingReproduced over two disable/enable cycles.
Expected: a disabled package's objects are dropped from the metadata registry and refuse on the data plane, consistently with the nav/view surfaces that already honour the status.
(B) Uninstall leaves orphaned metadata.⚠️ HALF fixed.
DELETE /packages/:idanswers 200, with:The package disappears from
/packages, but 7sys_metadatarows survive and/meta/*keeps serving them. Reproduced with a second, single-view package.Expected: either the persisted delete succeeds and the metadata rows go with the package, or the failed persistence is reported as a failure instead of being wrapped in a 200.
packages/runtime/src/domains/packages.ts, notpackages/rest/src/package-routes.tsas this card guessed — that REST door was already correct. The dispatcher now answers400 PACKAGE_DELETE_PARTIALon per-item failures, matching the other door including its zero-rows carve-out.protocol.deletePackagefinds 0 rows the data plane finds 3 of. Located with evidence, transferred todomain:metadataasprotocol.deletePackagefinds zerosys_metadatarows the data plane finds 3 of — uninstall leaves orphaned rows (persistence half of #7557) #7705, deliberately not patched from the consumer side. Note this half is invisible to the envelope fix by construction (failedCount: 0).Honest negative, recorded so nobody re-derives it: disable destroys no data — 3 rows before, during and after the disabled window. Now pinned by
packages/objectql/src/package-disable-enforcement.test.ts.Root cause
Not located by the report beyond the symptoms above. Two facts it does pin down:
persisted:{success:false,deletedCount:0}envelope means the delete's own persistence step reported failure and the route answered 200 anyway — the status code and the payload disagree.Filed
domain:engine-corebecause the surviving surfaces are registry-backed reads; re-route if the fix lands in the/packagesroute layer (packages/rest) or in the metadata repository instead.> Measured answer to that last sentence: the disable asymmetry was
listItems' early return forobject/objects(registry.ts) plus the same exemption ingetMetaItems(protocol.ts) — engine-core, as filed. The envelope waspackages/runtime(cli lane, declared on #6024). The persistence ispackages/metadata-protocol(#7705).Related
MetadataFacade.unregisterPackageremoves only object contributors — every non-object item the package shipped stays registered #7221 (open):MetadataFacade.unregisterPackageremoves only object contributors — every non-object item the package shipped stays registered. Same family as (B) at the in-memory registry layer; (B) here is the persisted layer (sys_metadatarows surviving a 200 delete). PR fix(engine-core): propagate package disable to metadata listings and the data plane (#7557) #7700's measured fix path did not run through it, so it stays untouched and distinct.objectis still served bySchemaRegistry.getObject— the registry heal reaches themetadatamap but neverobjectContributors#6808 (closed): a deleted runtime object still served bySchemaRegistry.getObject— the registry heal reached themetadatamap but neverobjectContributors. Closed before this build was cut, and the symptom here arrives through a package status rather than a delete, so this is a distinct path rather than a regression of it — worth reading for the shape of the heal. (Its lesson was applied in fix(engine-core): propagate package disable to metadata listings and the data plane (#7557) #7700: the new gate resolves names through the sameresolveObjectKeythe read path uses, so gate and read cannot disagree.)Reproduction
(A)
PATCHthe package tostatus:'disabled'.GET /api/v1/data/<object>→ 200, all rows.GET /meta/objects→ object still listed. Nav and views → correctly gone.(B)
DELETE /packages/:id→ 200 withpersisted:{success:false,deletedCount:0}.GET /packages→ gone. Querysys_metadata→ 7 rows survive.GET /meta/*→ still serving them.Source
Extracted from the QA run #7514 (framework a86db17, console 09987b68).