Uh oh!
There was an error while loading. Please reload this page.
feat(plugin-security): register the packaged-permission-set lock at the metadata door - #12292
Conversation
…he metadata door The pre-persistence authoring-gate seam carried an 'object' registration only, so a metadata-door save targeting a package-declared permission set reached persistence whenever the ADR-0005 tier gate was open for the type (the documented OS_METADATA_WRITABLE operator hatch) — and the resulting overlay won at read. Per the maintainer ruling of 2026-08-25 (option B: keep NARROW, move the lock), the seam now carries a 'permission' registration that consults the same classifyPackagedPermissionSet classifier and throws the same PackagedPermissionSetLockedError the sys_permission_set data door already uses — one spelling of "package-declared", two doors, one refusal. Hatch writes to non-packaged names keep the documented behavior, and runtime-shadow (ADR-0070 / ADR-0094 D5-R) sets stay editable. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UQgPSniH1GFM9ZDeGyuGUa
…tadata-door-permission-lock
…act ledger The gate's own remedy for new pinned coverage — the fake in packaged-permission-set-lock-gate.test.ts routes both write verbs through the shared dispatch predicates and the RETAINED ledger now holds the file. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UQgPSniH1GFM9ZDeGyuGUa
📓 Docs Drift Check3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 14 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ab889174800e53b618d42deac7003f21924a312c && git checkout ab889174800e53b618d42deac7003f21924a312c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8649b398b8cfb748b60222f4964729a35bc9e245 e7dab85944c8554fe176506bd91382e3ac937be8 && git checkout -B drift-repro 8649b398b8cfb748b60222f4964729a35bc9e245 && git merge --no-ff e7dab85944c8554fe176506bd91382e3ac937be8
node scripts/docs-audit/affected-docs.mjs --json 8649b398b8cfb748b60222f4964729a35bc9e245 |
os-trump
commented
Aug 25, 2026
Contract-review verdict: PASS (#11843)Reviewed by the contract-review chain at Reviewed: the gate file, plugin wiring and vitest-alias patches in full, and the ruling record on #11843. Conformance to ruling B (maintainer 2026-08-25, verbatim 「11843 同意」 — keep NARROW, move the lock), checked against the ruling's own acceptance oracle:
CI note: the Landing completed by the chain in the same stroke per the maintainer's standing instruction (2026-08-25, verbatim: 「审核通过你应该直接负责合并吧,还要等原始的项目经理吗」): clearing Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#11843
What
The pre-persistence authoring-gate seam in
@objectstack/plugin-security— which carried an'object'registration only — now also carries a'permission'registration (registerPackagedPermissionSetLockGate, new filepackaged-permission-set-lock-gate.ts, wired insecurity-plugin.tsbeside the object posture gate) that consults the existingclassifyPackagedPermissionSetclassifier frompackaged-permission-set-lock.tsthrough the existingassertPermissionSetNotPackageDeclaredwrite-door assertion, throwing the lock's existing error classes. Ruled by the maintainer on 2026-08-25 (「11843 同意」 — option B: keep NARROW, move the lock): one spelling of "package-declared", two doors, one refusal. No second refusal was authored.The door, the layer, the refusal (the probe's legs are the acceptance oracle)
saveMetaItem) previously had no pre-write gate forpermission; the feat(security): lock package-declared permission sets at the save door; clone to customize #11702 lock's only enforcement point was thesys_permission_setdata door (probe report on the card, 04:43Z / 04:45Z).OS_METADATA_WRITABLE=permissionhatch open, a package-less save targeting a package-declared set is now refused with the lock's own error class —PackagedPermissionSetLockedError,NOT_OVERRIDABLE/ 403 — and zerosys_metadatarows land. The pin asserts the row count, not only the throw — the defect was a write that landed.packaged-permission-set-lock.test.tsis unmodified and green in the suite run.package_id: null, organization_id: null. An ADR-0070 / ADR-0094 D5-R runtime-shadow set (definition living only insys_metadata) stays editable: the classifier'ssys_metadatasentinel exclusion carries over unchanged.PackagedPermissionSetProvenanceUnknownError(same code/status), exactly as the data door does.package-authorchannel is exempt by the seam's own contract (#4463 运行时发布门在 host-config 拓扑(environmentId 未绑定)上整体不跑——#5086 只把 code-only 拒绝移出了同一条短路 #6710 / owd-save-gate: the ADR-0090 D11 authoring gate never runs on any host-config deployment (keyed on environmentId, not authoringChannel) #7674), so package publishes never meet the gate. Draft saves are gated like publish saves (both minting paths).The 39 pins
sys-metadata-repository.package-writability.test.ts— 39/39 green on the final head (e7dab8594), file untouched. They cover non-packaged names and remain authoritative per the ruling.The new pin was red on the accepting behaviour (dissolution verification)
From the committed fix, the accepting behaviour was restored on disk (mutation verified by anchored grep: injected marker count 1, removed call-site count 0), and the pin went 4 failed | 3 passed:
Restored via
git checkout HEAD --on the gate file; disk == index == HEAD verified by blob hash (0dadd6a9…on all three); re-run 7/7 green. No rebuild leg applies to the mutation: the pin resolves both the mutated module (relative import) and the protocol (vitest source alias added in this PR, whose comment states why) from source, so nodist/sits in the loop.Verification (final head
e7dab8594; every heavy run throughscripts/pm/os-verify-lock.sh, exit codes captured before any pipe)pnpm --filter @objectstack/plugin-security test— 83 files / 1539 passed,os-verify-lock: VERDICT command-exit 0pnpm --filter @objectstack/plugin-security typecheck— clean, same VERDICT-0 chainnode scripts/pm/dispatch-gates.mjs, re-derived on the merged tree — list unchanged): changeset-gate-self-tests · objectui-changeset · override-consistency · published-files · slot-lookup · test-source-alias · type-source-resolution · adr-0087-registration · changeset-fixed · changeset-no-major · ci-filter-parity · empty-changeset · osv-exemptions · plugin-teardown-shape · cross-package-test-inputs · release-rehearsal-clone --self-test · docs-audit pair — all exit 0--writeand commit) · where-matcher · query-options-erasure · type-check-coverage · type-check-debt (--re-measureOK — none above its recorded number) · i18n (after building the CLI it requires) · i18n-stale-fill · nul-bytes — all exit 0pnpm lintand the rest of the 167 discovered families) is CI's run; the local scope is the derived list above plus the affected packages.Scope notes
@objectstack/plugin-securityminor — states plainly what stops working and for whom. Not major.packages/spec. Zeropackages/plugins/plugin-auth(declared family constraint with /admin/has-permission tells a platform admin 'no' — the vendor evaluates it on the legacy role scalar, so it returns a wrong 200 answer rather than a 403 #11900 / PR fix(plugin-auth): answer /admin/has-permission from the platform-authz predicate #12210 — different package, no file overlap).@objectstack/metadata-protocoladded so the pin drives the real seam; runtime code keeps feature detection, and the dependency is dev-only in one direction (no cycle).packaged-permission-set-restore-leg.test.tsis unchanged at the middleware level (its stub-protocol pins stay green), while a fully wired kernel now refuses that leg's re-authoring at the metadata door.Generated by Claude Code