Repository navigation
fix(metadata-protocol): duplicating a package copies a permission set that grants on an optional plugin's object not installed here - #30414
Conversation
… that grants on an optional plugin's object not installed here The package-duplicate door now passes the source row's stored grant keys as inheritedGrantObjects, as the permission-set clone door does. The unmanaged import keeps judging every grant: its package is request input. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CYSw4nSVJDr6XpcWLJ
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CYSw4nSVJDr6XpcWLJ
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CYSw4nSVJDr6XpcWLJ
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CYSw4nSVJDr6XpcWLJ
|
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL 维护者速读
席位核对 — ACCEPT · domain:server#2 · head
|
📓 Docs Drift CheckThis PR changes 1 package(s): ⛔ 1 release-owned page(s) name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 10 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 8b82348cac3ed90b31c5d180783052312ba44e0d && git checkout 8b82348cac3ed90b31c5d180783052312ba44e0d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6fa842af1e3137c93d7de2c823965c52fc5d524c c718acd35546ac254449a4966e3f9616a1e2e8b3 && git checkout -B drift-repro 6fa842af1e3137c93d7de2c823965c52fc5d524c && git merge --no-ff c718acd35546ac254449a4966e3f9616a1e2e8b3
node scripts/docs-audit/affected-docs.mjs --json 6fa842af1e3137c93d7de2c823965c52fc5d524c
|
Requested by Bill · project thread
domain:server#2 · os-bill · session_01Utp6CYSw4nSVJDr6XpcWLJ
Fixes #30319
Clause-②: no
Metadata-protocol: none
维护者速读
duplicatePackage)时,复制出的权限集把源权限集在库中存储的授权对象名作为inheritedGrantObjects交给保存门,与 main red: Dogfood Regression Gate (2/3) fails permission-set-clone-door after PR #30222 (member_default grants sys_comment) #30255 的权限集克隆门同一条规则。没装 plugin-audit 的部署里,授权sys_comment的权限集现在能随包一起复制;非托管导入按席位裁定 A 保持拒收,未改。failed[],整次复制回success: false,用户复制一个正常的包却丢了权限集。packages/metadata-protocol/src/protocol-package-commits.ts的复制门saveMetaItem调用(inheritedGrantObjects一行)与文件头的storedGrantObjectKeys;再看protocol.copy-doors-inherited-grants.test.ts的第二个用例(改名后的授权仍拒)。pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.copy-doors-inherited-grants.test.ts,预期 4 个用例全过。What changes
duplicatePackage(protocol-package-commits.ts): each copiedpermissionitem is saved withinheritedGrantObjects= the keys of the SOURCE row'sobjectsmap as stored (storedGrantObjectKeys(body), the raw parsed row, before conversion and rewrite). The save door's unknown-object refusal (metadata: a runtime permission-set save accepts a grant on an object that does not exist (200) — the build refuses the same grant (#18202), so a typo silently grants nothing #28990) already skips inherited keys since PR fix(runtime, metadata-protocol): the permission-set clone door copies a set that grants on an object this deployment does not install, and member_default's feed grants re-land #30310 (protocol-save.ts,unknownGrantObjectSaveRefusal), so a set granting on an optional plugin's object this deployment does not install (sys_comment/sys_activity,@objectstack/plugin-audit) is copied instead of landing infailed[].duplicatePackage's request has no such field, exactly as the clone door's (triage 6651966154: "server-derived from the SOURCE set as stored, never read from request input").leave_ticket→leave_copy_ticket) is not the source's key, so it is judged as before: when the copied object is not there, the set is still refused, naming that grant alone.storedGrantObjectKeysrestates the clone door'sinheritedGrantObjectsOf(two lines): that helper lives in@objectstack/runtime, which depends on this package, so it cannot be imported here.importUnmanagedPackage: unchanged in behaviour, with a comment at its save call. By the seat's ruling on Package duplicate and unmanaged import refuse a copied permission set that grants on an optional plugin's object not installed here (same 422 the clone door had, #30255) #30319 (option A), its package is the request's body, not a stored row, so its permission sets are new writes and every grant is judged; deriving inherited keys from the body would read request input and turn the refusal off for this door.patchfor@objectstack/metadata-protocol(Metadata-protocol: none). No published schema moves; a save that was refused now succeeds (Clause-②: no).Measured first (on
mainat870ebce3, before the fix)Harness: the stateful
sys_metadatastub of the new test file, registry registering no object (no plugin-audit), a source packagecom.example.leaveholding objectleave_ticketand setleave_staffgrantingleave_ticket+sys_comment.Duplicate door — refused (200, success: false, the set in failed[])
Unmanaged import — refused (422 INVALID_METADATA, rolled back whole)
Tests
packages/metadata-protocol/src/protocol.copy-doors-inherited-grants.test.ts, 4 cases:sys_comment(absent) and the source's own object is copied; the copy grantsleave_copy_ticket+sys_comment.leave_copy_ticket, its source row unparseable) is still refused — the exactfailed[]entry, one finding atobjects.leave_copy_ticket,sys_commentnot judged; nothing stored.other_gone, not renamed) is the source's and is copied.sys_commentis refused422 INVALID_METADATAatobjects.sys_comment(code, status, path, first sentence) and the import is rolled back (no row, no base) — pinned as intended by ruling A.Ablations (each through
scripts/ablation-replace.mjs, mutation proven on disk, restored to theHEADblob withgit diff HEADempty; the test imports./protocol.jsfrom source, so nodist/is involved):inheritedGrantObjectsargument at the duplicate doorsys_commentfinding)inheritedGrantObjectsat the import doorA first attempt at C was a no-op refused by the tool (replacement contained the anchor; anchor count 1 → 1); re-run with a distinct replacement.
Verification
All on this branch; the code under test is identical from
83a51faato the head (later commits: changeset wording, the engine-double ledger row, a merge ofmain).pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.copy-doors-inherited-grants.test.ts src/protocol.unknown-grant-object-refusal.test.tsonc718acd3(after mergingmainat7d7babb1): 23/23.pnpm --filter @objectstack/metadata-protocol testonc718acd3: Test Files 315 passed, 4 skipped; Tests 22410 passed, 26 skipped.pnpm --filter @objectstack/metadata-protocol typecheck: exit 0 (--listFilesincludes the new test file).pnpm --filter @objectstack/dogfood testunder the verify lock, closure built first, no shard): Test Files 419 passed, 3 skipped (422); Tests 3492 passed, 36 skipped (3528). Lock VERDICTcommand-exit 0, held 29m40s.dispatch-gates.mjs --commands, 67 families on4463c9f7): 66 exit 0, 1 NOT MEASURED —check:dual-build-cjs-loadsexit 3, PREREQUISITE NOT MET (studio, client-react and five plugin/service packages have nodist/in this worktree); left to CI.--ranreconciliation: "67 derived famil(ies) accounted for — 66 run, 1 NOT-MEASURED".check:engine-double-contractasked for the new test file's doubles to be recorded:--writeadded one seam row set, committed. After the merge,node scripts/check-engine-double-contract.mjsexit 0.pnpm lint: exit 0 on4463c9f7.Main's known red (
check-livenessquery/searchFields, #30367 / PR #30368) is not touched by this diff.Human Minimum
packages/specholds no runtime logic. (No spec change.)pnpm lintpasses.main, one pull request.packages/spectouched: n/a.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01Utp6CYSw4nSVJDr6XpcWLJ
Generated by Claude Code