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
Filed unassigned by the domain:services execution seat while running the #11725 probe. No pm:queue — grading is triage's field. Triage on #11725 was explicit that this fix "wants its own review, do not fold it into the probe PR without reporting first", so it is recorded here rather than implemented there.
What the probe measured
createPermissionSetWriteThrough has four write legs. insert and update open with assertPermissionSetNotPackageDeclared (the 2026-08-24 "lock the base, clone to customize" ruling, PR #11702). The restore leg does not: it re-authors the restored record's body into metadata with no provenance check, and it catches rather than throws, because it runs after the engine has already un-trashed the row.
That gap was named by #11702's own author and accepted as a residual, not a defect. The probe confirms both halves:
The residual is real. On an identical fixture the update leg refuses and the restore leg re-authors. Measured, not read.
Why it is worth closing anyway rather than leaving fenced
Two gates would sit in front of this leg if restore ever dispatched, and each has a documented blind spot on exactly the shape the lock exists for:
security-plugin.ts's assertPackageManagedWriteGate admits restore into its package-row refusal, but keys on the managed_bycolumn — which packaged-permission-set-lock.ts's own header records as measurably the wrong fact, too narrow in precisely the provenance_skip direction (permission-set-drift.ts): a genuinely package-declared set whose column does not read 'package'.
So "something else already covers it" is not an answer, and the coverage question should be settled while it is cheap rather than on the day #3146 lands.
The shape of the work, and the reason it is not a copy-paste
⚠️#11702's author gave the reason the lock is not simply repeated here: a throw after the record is restored strands the caller with a healthy-looking row and no signal, which is why this leg and its neighbours catch rather than throw. So the fix has a real design question in it — refuse before the un-trash, refuse after and compensate, or report on the durability channel — and that is what wants a review rather than a mechanical edit.
Tripwire already in place
PR for #11725 lands packages/plugins/plugin-security/src/packaged-permission-set-restore-leg.test.ts, whose MEASURED RESIDUAL case pins today's behaviour deliberately. Whoever takes this card inverts that case in the same PR — that inversion is the signal that the lock actually reached this leg, and the file's header says so.
Refs: #11725 (the probe, and where this was measured) · #11513 / PR #11702 (the ruling and the save-door lock) · #3146 (the parked recycle bin that would make this reachable) · #7809 (the dispatch-vocabulary weld) · #11843 stays open and asks a different door.
Filed unassigned by the
domain:servicesexecution seat while running the #11725 probe. Nopm:queue— grading is triage's field. Triage on #11725 was explicit that this fix "wants its own review, do not fold it into the probe PR without reporting first", so it is recorded here rather than implemented there.What the probe measured
createPermissionSetWriteThroughhas four write legs.insertandupdateopen withassertPermissionSetNotPackageDeclared(the 2026-08-24 "lock the base, clone to customize" ruling, PR #11702). Therestoreleg does not: it re-authors the restored record's body into metadata with no provenance check, and it catches rather than throws, because it runs after the engine has already un-trashed the row.That gap was named by #11702's own author and accepted as a residual, not a defect. The probe confirms both halves:
updateleg refuses and therestoreleg re-authors. Measured, not read.restoreis not a member of the engine's middleware dispatch vocabulary (objectql's [finding] The bulk AST write path passesopCtx.operationraw, sopurge/transfer/restorederive no row scope whileupdate/deletenow do (#7665 asymmetry) #7809 weld, which drives a real engine through every public method), and it never derives as an API operation (API_METHOD_DERIVATION.restore.flagis permanentlyfalsesinceenable.trashwas retired in [11.0][A2] Remove dead author-facing metadata properties (ADR-0049 enforce-or-remove) #2377 / ADR-0049). A real recycle bin is parked at Implement soft delete (recycle bin):enable.trash/softDeleteare spec-only with zero runtime readers #3146.Why it is worth closing anyway rather than leaving fenced
Two gates would sit in front of this leg if
restoreever dispatched, and each has a documented blind spot on exactly the shape the lock exists for:security-plugin.ts'sassertPackageManagedWriteGateadmitsrestoreinto its package-row refusal, but keys on themanaged_bycolumn — whichpackaged-permission-set-lock.ts's own header records as measurably the wrong fact, too narrow in precisely theprovenance_skipdirection (permission-set-drift.ts): a genuinely package-declared set whose column does not read'package'.saveMetaItemis what the documentedOS_METADATA_WRITABLE=permissionoperator hatch switches off — the same hatch that motivated Studio save of a package-declared permission set forks it into a silent, undiscoverable overlay #11513 / feat(security): lock package-declared permission sets at the save door; clone to customize #11702 in the first place.So "something else already covers it" is not an answer, and the coverage question should be settled while it is cheap rather than on the day #3146 lands.
The shape of the work, and the reason it is not a copy-paste
Tripwire already in place
PR for #11725 lands
packages/plugins/plugin-security/src/packaged-permission-set-restore-leg.test.ts, whose MEASURED RESIDUAL case pins today's behaviour deliberately. Whoever takes this card inverts that case in the same PR — that inversion is the signal that the lock actually reached this leg, and the file's header says so.Refs: #11725 (the probe, and where this was measured) · #11513 / PR #11702 (the ruling and the save-door lock) · #3146 (the parked recycle bin that would make this reachable) · #7809 (the dispatch-vocabulary weld) · #11843 stays open and asks a different door.