Skip to content

fix(app-shell): the Delegated Admin Scope block lists a delegate's companion grants and grants the missing ones (#26191) - #26340

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-26191-delegate-companion-grants
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-26191-delegate-companion-grants

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #26191

Clause-②: no
Metadata-protocol: none

Part of the #26059 ruling (6416233738, Q3 child 5, item 2). UI and docs only: no server change, no spec key.

What changes

The Delegated Admin Scope block lists the companion grants a delegate needs. A scope says what its holder may administer; it does not let the holder in. Once the scope names a business unit (the block's existing gate for its dependent controls), the block lists:

  • setup.access: enter Setup
  • sys_user, sys_business_unit, sys_business_unit_member: read, so the lookups can pick people and units
  • sys_user_position: create, read, edit and delete, for the assignment rows

Each row is marked "On this set" or "Not on this set", read from the draft being edited (systemPermissions and objects). A row counts as granted only when every flag it needs is true; nothing is inferred.

"Grant the missing ones" adds only what is missing, as one draft edit. It is one setDraft call, and the set's normal Save persists it. setup.access is appended to systemPermissions. Each companion object row gains only the flags it lacks. An existing row keeps every key it already has (an extra Edit, a View All), and a row that already covers its grant is left as the same object. Nothing is removed or narrowed, and no new key is written: both keys already exist on PermissionSetSchema. The button shows only when the set is writable and something is missing.

Under a package binding, the hint is shown without the action. The package door's draft holds, and its Save writes, only the package's own objects (scopePermissionSet / mergePermissionSlice, ADR-0086 P0). There, the sys_* rows would not survive Save, and their absence from the draft says nothing about the stored set. So under a package the block lists the five grants without a granted/missing mark and without the button, plus a note: grant these in Setup, on an environment permission set the delegate holds.

How the facet knows it is package-bound. PermissionAdvancedFacets gains an optional packageBound prop. When the prop is omitted, it is read from only. The Studio Access pillar is the one host that sets only, and it always edits under a packageId (AccessPillar's packageId is required). Setup's permission page sets neither. PermissionMatrixEditor.tsx is outside this card's claimed file surface, so this PR does not wire packageBound={!!packageId} there. See Acceptance notes.

Docs. content/docs/permissions/delegated-administration.mdx: the authoring example now carries systemPermissions: ['setup.access']. A new paragraph names the five companion grants and why each is needed, and says the Setup editor lists them and grants the missing ones.

Strings. Nine new perm.admin.companions.* keys in metadata-admin/i18n.ts, in both locales the block's existing strings use (en, zh).

Tests

PermissionAdvancedFacets.companionGrants-26191.test.tsx drives the facet with the draft held as the host holds it. It has 10 cases:

  • the five grants are listed and marked missing on an empty set
  • grants the set already carries are marked granted, and read alone is not CRUD
  • the block is hidden until the scope names a business unit
  • one click is one draft write, adds only the missing grants, and leaves existing rows and an unrelated object untouched
  • the written set parses under PermissionSetSchema and carries no new key
  • there is no action when nothing is missing
  • read-only: the hint shows and the action does not
  • packageBound: the hint shows unmarked, there is no action, and the note shows
  • the Studio mount (only) is package-bound by default
  • an explicit packageBound={false} keeps the action

Red first, at c3066c2b (the test committed, the component not yet changed): 9 failed, 1 passed. The one that passed is the "hidden until a business unit is named" case, which holds both before and after the change. The failures were Unable to find [data-testid="admin-companions"] and the like, not errors.

Green, all at head 8ecd7344 (this branch with main merged after #26284 landed), from cwd ui/ under scripts/pm/os-verify-lock.sh:

  • pnpm exec vitest run FILES --maxWorkers=2 over 254 files: every app-shell test file that imports PermissionAdvancedFacets or views/metadata-admin/i18n, the new file included. Test Files 254 passed (254), Tests 2578 passed (2578). The lock's verdict was command-exit 0. The full app-shell suite was not run locally; CI runs it.
  • eslint on the three changed source files: 0 errors and 5 warnings. Two warnings are new, both no-explicit-any in the test harness, where the updater's prev is typed any, as the prop itself types it. The other three are on existing lines.
  • pnpm --filter @object-ui/app-shell type-check: NOT MEASURED locally. It ran without the @object-ui/* dependency closure built, so it answered only unresolved-module cascades (TS2307 / TS7006). None of them is on a line this PR adds. CI's Type Check UI is the reading.
  • node scripts/pm/dispatch-gates.mjs --commands: 68 derived commands, reconciled with --ran as 66 run (all exit 0) and 2 NOT MEASURED. Both of those are exit-3 prerequisite refusals: check:skill-examples (packages/client-react was not built; this page's example block carries no type-check marker) and check:type-check-debt (it needs the shared lock for its closure build and timed out in the queue). CI's Lint & Repo Gates runs both.
  • node scripts/check-sdui-manifest.mjs: exit 0. app-shell has no sdui record, so there was nothing to re-record.

Acceptance notes

  • Package binding wiring. The package-bound default is read from only, not from packageId, because PermissionMatrixEditor.tsx is outside this card's file surface (fix(app-shell): permission matrix sets and shows readScope / writeScope (#26187) #26284 held it). Once that file is open to a card, passing packageBound={!!packageId} at both facet mounts makes the binding explicit, and the only fallback can go. Until then the two coincide by construction: packageId is passed only by AccessPillar, always together with studio chrome, and that chrome is what sets only.
  • Marks are exact, not inferred. A set that reaches Setup through some broader grant is still shown as "Not on this set" for setup.access. Clicking then adds the exact capability, which is an addition and narrows nothing.
  • PermissionMatrixEditor.tsx, permission-slice.ts, the server, packages/spec and ui/packages/i18n are untouched. app-shell has no sdui record (node scripts/check-sdui-manifest.mjs exits 0).
  • The docs path (content/docs/**) is not a governed surface: node scripts/pm/check-governed-merges.mjs --test over the five files exits 0.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读

改了什么:设置里编辑权限集的「委派管理范围」块,选定业务单元后会列出委派管理员还需要的 5 项配套权限(进入 Setup、读用户/业务单元/成员、增删改查岗位分配),逐项标出已有/缺失,并给一个「补齐缺失项」按钮。
为什么改:只给范围不给配套权限,委派管理员进不了 Setup、查不到人,要靠一次次被拒才找全(#26191,#26059 裁决 Q3 第 5 子项)。
风险与回滚:只改界面与文档,服务器和 spec 不动;按钮只开不关、只补缺失,仍要点保存才落库;包绑定下只列不标、不给按钮。回滚 = revert 本 PR。
先看:ui/packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tsx(companionGrantsAdded 只增不减、packageBound 默认值)→ 同目录 PermissionAdvancedFacets.companionGrants-26191.test.tsx → content/docs/permissions/delegated-administration.mdx。
两分钟验证:cd ui && pnpm exec vitest run packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.companionGrants-26191,预期 10 个用例全过。
需要你做的:无,席位落地。
席位意见:ACCEPT。packageBound 由 only 推断,今天成立;显式接线已另立 #26349(p3)。

席位核对

seat 2 · 2026-10-26T22:27Z · head 8ecd7344 (#26191)

item reading
form 5 files, +469/−1: PermissionAdvancedFacets.tsx, new 10-case pin, metadata-admin/i18n.ts (9 en+zh keys), docs delegated-administration.mdx, root .changeset/ patch on @object-ui/app-shell; declaration lines Clause-②: no, Metadata-protocol: none
premise measured #26284 (#26187 PR 1) merged before the PR opened; grants land only in systemPermissions and existing objects rows, no new key
gates (check-runs on 8ecd7344) 62 runs; every run with a conclusion is success (Test UI 1–16, Lint UI, Type Check UI, Build & E2E UI, Lint & Repo Gates, Type Check · debt ledger, Governed Surface Queue Guard, Check Changeset …); the rest skipped by filter. Local: red 9/10 at base, green after; 254 app-shell files / 2578 tests; app-shell type-check exit 0
accepted deviations (1) packageBound defaults to only !== undefined, since PermissionMatrixEditor.tsx was off the claim's surface; (2) under a package the list is unmarked, because the draft holds only the package's own objects
out of scope explicit packageBound={!!packageId} wiring → #26349 (p3, blocked by this card)
landing NOT governed (check-governed-merges --pr 26340: 5 paths clear, 470 lines); git merge-tree against origin/main clean; app-shell has no sdui record, so no line; shares metadata-admin/i18n.ts with #26187 PR 2, which is not open yet and merges main after this

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 26, 2026 22:28
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 22:28
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit e14d9ce Oct 26, 2026
63 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-26191-delegate-companion-grants branch October 26, 2026 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setup permission set, Delegated Admin Scope: no hint or one-click grant for the companion grants a delegate needs to enter Setup and pick people

2 participants