You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[finding] DELETE /packages/:id through the dispatcher removes the package from the live registry, then refuses with TENANT_SCOPE_REQUIRED: a 400 that leaves the uninstall half applied #20492
Filing gate: ① a defect with a named landing site: packages/runtime/src/domains/packages.ts, the DELETE /packages/:id branch of the dispatcher's /packages domain. Finding class (a). reach: was measured at a public door, dispatch(), by the #20477 dev with a throwaway probe on PR #20491's head 1e0553b2. The probe used a real SchemaRegistry and the real ObjectStackProtocolImplementation. That PR does not touch this branch.
Filed by the domain:cli execution seat (#6024, session local_1d2a197c-c20e-4e90-9be8-413d4d432289) from the #20477 round. ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.
What happens (measured)
An authenticated caller holding manage_metadata, with no active organization, sends DELETE /api/v1/packages/:id through dispatch():
the answer is 400 TENANT_SCOPE_REQUIRED (「Refusing to uninstall … with no organization scope」);
but the package is already gone from the live registry: GET /packages/:id answered 200 before and 404 RESOURCE_NOT_FOUND after, and the package list was empty;
and the stored rows are kept.
So a refused request left the running process without the package, while its persisted state says it is installed. The mismatch lasts until a restart re-seeds the registry.
Why (origin/mainfc0db22bc, read at source)
In the DELETE branch, the ADR-0070 read-only gate (requireWritablePackage, #7560) runs first. Then registry.uninstallPackage(id) runs, which drops the package's objects, namespace, items and record. Only after that does protocol.deletePackage run its org-scope refusal. The refusal can fire after the registry mutation has already happened.
Who reaches it
It predates PR #20491, for every session with no active organization. PR #20491 (#20477, p0) makes the dispatcher read the vetted organization, which adds members removed from an organization to that population. The fix there is right: the ex-member no longer deletes the left organization's rows. This refusal path, though, now fires for them too.
Related, and why this is not a duplicate
#7970 (closed, PR #8105) fixed the same shape INSIDE SchemaRegistry.uninstallPackage, where the namespace release sat before the verb that could refuse. This card is the door's own ordering: the whole registry uninstall runs before the persisted delete's org-scope refusal. #7560 (closed) put the read-only gate before uninstallPackage; the org-scope refusal was never moved there.
Duplicate check
Board search, open and closed, taken in the act that filed this card:
Filing gate: ① a defect with a named landing site:
packages/runtime/src/domains/packages.ts, theDELETE /packages/:idbranch of the dispatcher's/packagesdomain. Finding class (a).reach:was measured at a public door,dispatch(), by the #20477 dev with a throwaway probe on PR #20491's head1e0553b2. The probe used a realSchemaRegistryand the realObjectStackProtocolImplementation. That PR does not touch this branch.Filed by the
domain:cliexecution seat (#6024, sessionlocal_1d2a197c-c20e-4e90-9be8-413d4d432289) from the #20477 round. ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.What happens (measured)
An authenticated caller holding
manage_metadata, with no active organization, sendsDELETE /api/v1/packages/:idthroughdispatch():TENANT_SCOPE_REQUIRED(「Refusing to uninstall … with no organization scope」);GET /packages/:idanswered 200 before and 404RESOURCE_NOT_FOUNDafter, and the package list was empty;So a refused request left the running process without the package, while its persisted state says it is installed. The mismatch lasts until a restart re-seeds the registry.
Why (
origin/mainfc0db22bc, read at source)In the
DELETEbranch, the ADR-0070 read-only gate (requireWritablePackage, #7560) runs first. Thenregistry.uninstallPackage(id)runs, which drops the package's objects, namespace, items and record. Only after that doesprotocol.deletePackagerun its org-scope refusal. The refusal can fire after the registry mutation has already happened.Who reaches it
It predates PR #20491, for every session with no active organization. PR #20491 (#20477, p0) makes the dispatcher read the vetted organization, which adds members removed from an organization to that population. The fix there is right: the ex-member no longer deletes the left organization's rows. This refusal path, though, now fires for them too.
Related, and why this is not a duplicate
#7970 (closed, PR #8105) fixed the same shape INSIDE
SchemaRegistry.uninstallPackage, where the namespace release sat before the verb that could refuse. This card is the door's own ordering: the whole registry uninstall runs before the persisted delete's org-scope refusal. #7560 (closed) put the read-only gate beforeuninstallPackage; the org-scope refusal was never moved there.Duplicate check
Board search, open and closed, taken in the act that filed this card:
/packagesdomain takes the organization from the raw session claim (resolveActiveOrganizationId, 9 sites), bypassing ruling B on #15409: a session naming a left organization may read and write it #20477 is the parent round; [finding]protocol.deletePackagehas no declared spec shape — three hand-rolled types that disagree, and the runtime twin reaches it throughas any#9960, Product question: an uninstall with no organizationId deletes EVERY organization's rows for that package (measured 5 of 5, including a foreign org's) #7780 and [finding] package-routes.ts re-declares getMetaItems/deletePackage as a narrower local structural type instead of reading the spec's #9846 are closed and about other questions.uninstallPackageunregisters the namespace BEFORE the verb that can refuse — a rejected uninstall leaves the package half-mutated #7970 is the registry-internal sibling above;MetadataFacade.unregisterPackageremoves only object contributors — every non-object item the package shipped stays registered #7221, [Decision] the package boot seed set is never updated by enable/disable, so a flag-absent re-install durably reverts an operator later enable once #18752 lands — and the obvious producer fix re-opens #18058 F1 pins #18877, The ADR-0070 read-only gate is missing on package lifecycle:PATCH /packages/:id/disableandDELETE /packages/:idboth answer 200 on platform packages, removing them from the running deployment #7560 and [Decision]applySystemFieldsinjects platform anchors intoexternalobjects the platform provisions no storage for — three consumers have now independently re-derived "that column is not really there" #7865 are closed and different.Query terms for later deduplication:
uninstall refused registry already removed,TENANT_SCOPE_REQUIRED half-applied uninstall,uninstallPackage before deletePackage,DELETE packages registry before persistence.