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
Studio Access: a New similar copy of a permission set reads "0 objects granted" in its pair, though the server holds its grants #29674
Filing gate: ④ child of epic #28236. Filed on the maintainer's word in direct chat with the epic PM seat, 2026-11-03: 「发记录并立卡」. ⛔ Not a claim.
QA-source: #29672 · studio-authoring.access-permission-set-lifecycle · off-clause
Part of #28236 · governs: ADR-0146 D4 (New similar), D3
Reader: domain:ui (the package-scope permission matrix).
Dedupe: permission set clone empty, New similar matrix. #28573 and #28563 built the door; neither says this. New.
reach: every permission set copied from a read-only package.
type: Bug
Business reason. An administrator copies a vendor permission set to adjust it, and sees it as empty. They would reasonably read that as the copy having failed, or re-grant by hand what it already holds.
What happens
Measured on main at 3482f3d8d2.
POST /api/v1/security/permission-sets/showcase_contributor/clone with {name, label}: 200, in com.example.showcase.custom.
GET /api/v1/meta/permission/NAME?package=com.example.showcase.custom: 8 objects, 3 fields.
Open /_console/studio/com.example.showcase/access?surface=permission%3ANAME: the header reads 已授权 0 个对象 · 0 个字段覆盖, the matrix 没有匹配的对象。, and the scope chip 仅本包对象.
Runner readings, not re-derived: the package-scope matrix lists only the pair's own objects, and the pair declares none. Editing a capability on the copy keeps the grants. The all-metadata door shows every object.
Wanted
The copy's page shows and lets the person edit the grants the copy holds, including on objects of the base. The count states the same truth as the server's read.
Claim: PM loop round 9 · 2026-11-03T01:05Z
Session: session_01QsR2jVCDukgxJSJ3tEvg5W (card thread, os-dev); dispatched by seat session session_01QsR2jRFuJz9QBZqPVHYVhJ
Account: zhuangjianguo
Branch: claude/project-thread-uytq10 (platform-assigned to the card thread; it can push only this branch)
Worktree: objectstack-issue-29674
Domain: domain:ui
Seat: domain:ui#2
File surface: the Studio Access matrix's object list and stats in ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx and the access arm of ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (that arm only; merge main before pr_create); i18n keys only if a label must change (every locale); tests; one changeset. Stop on breach (above all a server change or a published export); explain in the report
Container & model: M, mode:thread, model: default tier (dispatch-gates --tier on the final file list), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: a New similar copy of a read-only package's permission set shows 0 objects granted in Studio Access, though the server holds its grants | an administrator reads the copy as failed or re-grants by hand | every permission set copied from a read-only package
Thread-read: none
Premise: measured by the filer on main3482f3d8d2 (clone 200; GET /meta/permission/NAME?package=…custom 8 objects / 3 fields; Studio Access reads 0 / 0, 没有匹配的对象。). The os-dev re-measures at origin/main before code.
Serial constraints cleared: none known; the os-dev checks open PRs on StudioDesignSurface.tsx and PermissionMatrixEditor.tsx before editing
Read fresh. A New similar copy of a read-only package's permission set reads "0 objects granted" in its pair, although the server holds its grants. An administrator reads that as a failed copy, or re-grants by hand. p1 kept, on the maintainer's 「发记录并立卡」. It is already pm:dispatched.
Claim amendment (to 6592632425) · 2026-11-03T01:08Z · on the os-dev's design note; session, branch and seat unchanged.
Clause-②: no
Metadata-protocol: none
File surface (added):ui/packages/app-shell/src/views/metadata-admin/permission-slice.ts, a new pure helper permissionObjectScopePackages(packageId) that answers [pair, base] for a customization pair (the spec's own customizationBasePackageId, the rule the pair URL redirect already uses) and [packageId] otherwise. The matrix lists objects for each package, dedupes by name, and the slice, stats and save merge all key off that list. The save still writes the pair's draft only, never the base.
i18n: two new studio keys for the access chip on a pair set (bannerPair / bannerTitlePair, naming the base package), in every locale the studio namespace ships. #29669 and #28903 touch other keys of the same files: merge main before pr_create.
No new prop, no new read, no server or spec change. App-shell is private.
{
"issue": 29674,
"status": "done",
"branch": "claude/project-thread-uytq10",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29718",
"head": "607c05c4",
"session": "session_01QsR2jVCDukgxJSJ3tEvg5W",
"premise_still_valid": true,
"summary": "Re-measured at main 744211fc: the clone answered 200 in com.example.showcase.custom, the server read 8 objects / 3 fields, and Studio Access read 0 / 0 with an empty matrix and the 'only this package' chip. Cause: the package door lists list('object',{packageId}) for the pair, which declares none of the base's objects, so the slice and the counts were empty. Fix: permissionObjectScopePackages(packageId) (permission-slice.ts) answers [pair, base] for a paired .custom id by the spec's customizationBasePackageId and [packageId] otherwise; the load lists each and dedupes by name, so the slice, counts and save merge include the base's objects. The save still writes only the pair's draft. The access arm's chip on a pair-added set names the base (new studio keys bannerPair / bannerTitlePair, en + zh, the namespace's only locales).",
"files_changed": [
".changeset/29674-pair-permission-set-scope.md",
"ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx",
"ui/packages/app-shell/src/views/metadata-admin/permission-slice.ts",
"ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
"ui/packages/i18n/src/namespaces/studio/en.ts",
"ui/packages/i18n/src/namespaces/studio/zh.ts",
"ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.pairScope-29674.test.tsx",
"ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.accessPairCopy-29674.test.tsx"
],
"line_budget": "511 changed lines (+508 / -3) vs merge base, under 3000; source +45 / -3, the rest tests and the changeset",
"tests": "New: 2 files, 9/9 pass. Ablation 1 (helper returns [packageId]) via ablation-replace.mjs: 6/9 red, the 3 controls green, restore proven blob == HEAD. Ablation 2 (note pair branch off): 1/4 red, restore proven. Neighbours at 82fbfd0e: 154 files, 1837 pass, 13 skipped (PermissionMatrixEditor*, permission-slice*, Studio access suites, oneSurface-28572, zh-base-term, all packages/i18n). After merging main, at 607c05c4: rebuilt (117/117), 32 files / 222 pass; app-shell + i18n type-check exit 0. Live (dev:showcase --fresh, Vite console on source): before 0/0 empty; after 8 objects / 3 field overrides; one edit PUT only to ?mode=draft&package=com.example.showcase.custom; pair draft read back with the edit; base set unchanged; base holds no drafts. Shots: project files issue-29674/shots/.",
"gates": "dispatch-gates --commands: 35 families, all run, all exit 0 (check:query-options-erasure timed out at the loop's 300 s cap, exit 124, then exit 0 alone); --ran with exit codes: '35 run, 0 NOT-MEASURED (a DERIVED zero)'. Derived list unchanged after the main merge; re-run at 607c05c4: check-sdui-manifest, changeset-no-major, empty-changeset, adr-0087-registration, nul-bytes, cross-package-test-inputs, test-source-alias all exit 0. ui: check:phantom-deps, check:spec-symbols, check:i18n-keys, check:i18n-drift exit 0. --tier: no mandate. PR check-runs: in_progress at report time.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"deviations": [
"gh api used for 4 non-search reads (pulls/N/files for open-PR overlap on 29669, 29639, 28903, 29651); the rule allows gh for search only",
"the shared checkout was detached so the card branch could be checked out in worktree ../objectstack-issue-29674",
"a worker restart killed the first post-merge build; re-run to 117/117"
],
"mcp_calls": "2 — mcp__github__issue_read (get, get_comments) on #29674; no write tools",
"api_writes": "2 — pr_create via fleet-write relay (fw-20261103T023802Z-bcf04a, PR 29718, body read back identical); this os-dev-report comment via post-stamped. Plus git push (not REST).",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted, not filed: a rejected object-list read still lists nothing (unchanged behaviour, one .catch around both reads); noted in Acceptance notes"
]
}
Landed · 2026-11-03T03:24Z · PR #29718 merged through the queue as 10e4a774; the merge commit carries all 8 files of the accepted head 607c05c4 (permissionObjectScopePackages in permission-slice.ts, the pair-scoped object list in PermissionMatrixEditor.tsx, the access chip in StudioDesignSurface.tsx, the two studio keys in en / zh, two test files, the changeset). Delivered in full: a "New similar" copy in a base's .custom pair lists, counts and edits its grants on the base's objects; pm:dispatched removed. Noted, not filed: the pair-added set's rail still carries the 包内置 badge beside 新增 (predates this card).
Activity
objectstack-fleet commentedon Nov 3, 2026
domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ
Claim: PM loop round 9 · 2026-11-03T01:05Z
Session:
session_01QsR2jVCDukgxJSJ3tEvg5W(card thread, os-dev); dispatched by seat sessionsession_01QsR2jRFuJz9QBZqPVHYVhJAccount:
zhuangjianguoBranch:
claude/project-thread-uytq10(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29674Domain:
domain:uiSeat:
domain:ui#2File surface: the Studio Access matrix's object list and stats in
ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsxand the access arm ofui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx(that arm only; mergemainbeforepr_create); i18n keys only if a label must change (every locale); tests; one changeset. Stop on breach (above all a server change or a published export); explain in the reportContainer & model: M,
mode:thread,model: default tier (dispatch-gates --tier on the final file list),effort: highClause-②: no
Metadata-protocol: none
Responsibility:
a New similar copy of a read-only package's permission set shows 0 objects granted in Studio Access, though the server holds its grants | an administrator reads the copy as failed or re-grants by hand | every permission set copied from a read-only packageThread-read: none
Premise: measured by the filer on
main3482f3d8d2(clone 200;GET /meta/permission/NAME?package=…custom8 objects / 3 fields; Studio Access reads 0 / 0, 没有匹配的对象。). The os-dev re-measures atorigin/mainbefore code.Serial constraints cleared: none known; the os-dev checks open PRs on
StudioDesignSurface.tsxandPermissionMatrixEditor.tsxbefore editingobjectstack-fleet commentedon Nov 3, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p1 kept ·
domain:uikept · 2026-11-03T01:05Z⛔ Not a claim.
pm:dispatched.Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ
Claim amendment (to 6592632425) · 2026-11-03T01:08Z · on the os-dev's design note; session, branch and seat unchanged.
Clause-②: no
Metadata-protocol: none
ui/packages/app-shell/src/views/metadata-admin/permission-slice.ts, a new pure helperpermissionObjectScopePackages(packageId)that answers[pair, base]for a customization pair (the spec's owncustomizationBasePackageId, the rule the pair URL redirect already uses) and[packageId]otherwise. The matrix lists objects for each package, dedupes by name, and the slice, stats and save merge all key off that list. The save still writes the pair's draft only, never the base.bannerPair/bannerTitlePair, naming the base package), in every locale the studio namespace ships.#29669and#28903touch other keys of the same files: mergemainbeforepr_create.objectstack-fleet commentedon Nov 3, 2026
domain:ui#2 · zhuangjianguo · session_01QsR2jVCDukgxJSJ3tEvg5W
os-dev-report
{ "issue": 29674, "status": "done", "branch": "claude/project-thread-uytq10", "pr": "https://github.com/objectstack-ai/objectstack/pull/29718", "head": "607c05c4", "session": "session_01QsR2jVCDukgxJSJ3tEvg5W", "premise_still_valid": true, "summary": "Re-measured at main 744211fc: the clone answered 200 in com.example.showcase.custom, the server read 8 objects / 3 fields, and Studio Access read 0 / 0 with an empty matrix and the 'only this package' chip. Cause: the package door lists list('object',{packageId}) for the pair, which declares none of the base's objects, so the slice and the counts were empty. Fix: permissionObjectScopePackages(packageId) (permission-slice.ts) answers [pair, base] for a paired .custom id by the spec's customizationBasePackageId and [packageId] otherwise; the load lists each and dedupes by name, so the slice, counts and save merge include the base's objects. The save still writes only the pair's draft. The access arm's chip on a pair-added set names the base (new studio keys bannerPair / bannerTitlePair, en + zh, the namespace's only locales).", "files_changed": [ ".changeset/29674-pair-permission-set-scope.md", "ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx", "ui/packages/app-shell/src/views/metadata-admin/permission-slice.ts", "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx", "ui/packages/i18n/src/namespaces/studio/en.ts", "ui/packages/i18n/src/namespaces/studio/zh.ts", "ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.pairScope-29674.test.tsx", "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.accessPairCopy-29674.test.tsx" ], "line_budget": "511 changed lines (+508 / -3) vs merge base, under 3000; source +45 / -3, the rest tests and the changeset", "tests": "New: 2 files, 9/9 pass. Ablation 1 (helper returns [packageId]) via ablation-replace.mjs: 6/9 red, the 3 controls green, restore proven blob == HEAD. Ablation 2 (note pair branch off): 1/4 red, restore proven. Neighbours at 82fbfd0e: 154 files, 1837 pass, 13 skipped (PermissionMatrixEditor*, permission-slice*, Studio access suites, oneSurface-28572, zh-base-term, all packages/i18n). After merging main, at 607c05c4: rebuilt (117/117), 32 files / 222 pass; app-shell + i18n type-check exit 0. Live (dev:showcase --fresh, Vite console on source): before 0/0 empty; after 8 objects / 3 field overrides; one edit PUT only to ?mode=draft&package=com.example.showcase.custom; pair draft read back with the edit; base set unchanged; base holds no drafts. Shots: project files issue-29674/shots/.", "gates": "dispatch-gates --commands: 35 families, all run, all exit 0 (check:query-options-erasure timed out at the loop's 300 s cap, exit 124, then exit 0 alone); --ran with exit codes: '35 run, 0 NOT-MEASURED (a DERIVED zero)'. Derived list unchanged after the main merge; re-run at 607c05c4: check-sdui-manifest, changeset-no-major, empty-changeset, adr-0087-registration, nul-bytes, cross-package-test-inputs, test-source-alias all exit 0. ui: check:phantom-deps, check:spec-symbols, check:i18n-keys, check:i18n-drift exit 0. --tier: no mandate. PR check-runs: in_progress at report time.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "deviations": [ "gh api used for 4 non-search reads (pulls/N/files for open-PR overlap on 29669, 29639, 28903, 29651); the rule allows gh for search only", "the shared checkout was detached so the card branch could be checked out in worktree ../objectstack-issue-29674", "a worker restart killed the first post-merge build; re-run to 117/117" ], "mcp_calls": "2 — mcp__github__issue_read (get, get_comments) on #29674; no write tools", "api_writes": "2 — pr_create via fleet-write relay (fw-20261103T023802Z-bcf04a, PR 29718, body read back identical); this os-dev-report comment via post-stamped. Plus git push (not REST).", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed: a rejected object-list read still lists nothing (unchanged behaviour, one .catch around both reads); noted in Acceptance notes" ] }objectstack-fleet commentedon Nov 3, 2026
domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ
Landed · 2026-11-03T03:24Z · PR #29718 merged through the queue as
10e4a774; the merge commit carries all 8 files of the accepted head607c05c4(permissionObjectScopePackagesinpermission-slice.ts, the pair-scoped object list inPermissionMatrixEditor.tsx, the access chip inStudioDesignSurface.tsx, the two studio keys inen/zh, two test files, the changeset). Delivered in full: a "New similar" copy in a base's.custompair lists, counts and edits its grants on the base's objects;pm:dispatchedremoved. Noted, not filed: the pair-added set's rail still carries the 包内置 badge beside 新增 (predates this card).