Skip to content

feat(app-shell): Setup pages for position holders and permission-set assignment - #28310

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-hr3zwc
Oct 31, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-hr3zwc

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Part of #28241

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:Setup 新增两个页面——「岗位持有人」(选一个岗位,查看、添加、移除持有人)和「权限集分配」(选一个权限集,查看直接持有和经岗位持有的用户,分配或取消分配)。两页直接复用岗位详情页和权限集编辑器里现有的那两个区块,选中的项写进 URL。
  • 为什么改:给人分配岗位和权限集是管理员的日常工作,现在只能进元数据详情页里做;目录迁到 Studio(N-26)后,管理员需要在 Setup 里有自己的入口,不必为加一个持有人打开 Studio。
  • 风险与回滚:只新增页面和翻译,不改任何读写;谁能增删、服务端的拒绝(含委派管理员越界)都沿用区块原样,原文显示。详情页里的两个区块照常工作。回滚即 revert 本 PR。
  • 先看:ui/packages/app-shell/src/views/setup/CatalogAssignmentPage.tsx(目录读取失败时如实报错而非显示空列表)、ui/packages/app-shell/src/services/builtinComponents.tsx 新增的两个注册。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/setup/,预期全绿;或在本地 Console 打开 /apps/setup/component/security/position-holders,左侧列出岗位,点一个看到持有人。
  • 需要你做的:无,席位落地。注意 Setup 左侧导航里的两个入口属于服务端安全插件的导航贡献,不在本车道,已作为发现交席位另立;入口落地前,两页只能经 URL 打开。

What changes

Two Setup component pages, registered in services/builtinComponents.tsx the way Packaged Automation and Connector Credentials are:

  • Position holders (security:position-holders, /apps/setup/component/security/position-holders): the registry's position catalog on the left with a filter; the picked position's PositionHoldersSection on the right.
  • Permission set assignment (security:permission-set-assignment, /apps/setup/component/security/permission-set-assignment): the registry's permission catalog; the picked set's AssignedUsersSection, in the bordered box the permission set editor draws around it.

Both share views/setup/CatalogAssignmentPage.tsx:

  • The picked item rides a page-scoped query parameter (?position= / ?set=, constants in views/setup/peopleAssignments.ts), written with replace, so a shared or refreshed link opens the same item (ui AGENTS.md Convert to monorepo with scoped packages #8).
  • The catalog is read through the metadata store's ensureType. The store answers a failed list with an empty one and records the failure on the type's status, so the page reads getTypeStatus and says "Could not read the positions." with a retry (refresh(type), past the store's failure cooldown), never "No positions".
  • A URL naming an item the catalog does not hold says so, and reads no holders for it.
  • The store is read through a ref, so no read keys on the context value's identity (ui AGENTS.md chore: version packages #10).
  • The section is keyed by the item's name, so its local state (an open confirm, an inline refusal) belongs to that item and is not carried to the next one.

The two sections are imported, not moved or rewritten. Who may add or remove (the section's grant check on sys_user_position / sys_user_permission_set), the member-only picker and every server refusal stay the section's own. The item pages keep rendering them until N-28 retires those routes.

20 new keys under setupAssignments in all ten packs, one contiguous block per pack after connectorCredentials. The de pack's notFound adds one correctly paired „…“ span, so de-quote-pairing-3876.test.ts counts 102 instead of 101, with the reason line in its ledger comment.

Changeset: .changeset/28241-setup-people-assignment-pages.md, @object-ui/app-shell and @object-ui/i18n patch.

Tests

  • views/setup/PositionHoldersPage.test.tsx (8): lists the catalog and reads no holder before a pick; picking writes ?position= and lists holders; add round-trip (create by name, list reads the new holder back); remove round-trip (confirm, delete by row id, list reads it gone and shows the empty line); a delegated administrator's out-of-scope refusal (DELEGATION_SCOPE_VIOLATION, 403) shown as the server's sentence, verbatim; a failed catalog read shown as a failure, then a retry through refresh('position'); an unknown ?position=; the filter.
  • views/setup/PermissionSetAssignmentPage.test.tsx (5): the same for sets, including that a holder through a position stays when a direct grant is unassigned.
  • views/setup/peopleAssignments.navContribution.test.tsx (4): both refs are registered by the production registration module, address component/security/…, and mount through the component route.
  • views/setup/peopleAssignments.test.ts (3): catalogEntries and filterCatalogEntries.
  • CONTROL, unchanged and green: ResourceEditPage.setupCatalog-7611.test.tsx and PermissionMatrixEditor.setupCatalog-7611.test.tsx (the sections still render on the item pages), plus every PositionHoldersSection* / AssignedUsersSection* test and setupHoldersGrant-12082.test.tsx.
  • Ablation (one-off, on the head before the literal-key commit, which does not touch that file; restored by scripts/ablation-replace.mjs, blob back to HEAD): deleting the getTypeStatus(type) === 'error' branch in CatalogAssignmentPage.tsx turns exactly "says a failed catalog read failed, never "no positions", and retries past the cache" red (1 failed, 7 passed); restored, 8 passed.

Acceptance notes

  • The Setup navigation entries are not in this PR. Setup's nav is server metadata: group_people_org is an empty anchor in packages/platform-objects/src/apps/setup.app.ts, and the Positions / Permission Sets entries are the navigationContributions of packages/plugins/plugin-security/src/security-plugin.ts (group group_access_control). The two entries (type: 'component', componentRef: 'security:position-holders' and 'security:permission-set-assignment', group group_people_org) belong there, outside this ui lane. Hence Part of; the card's "both pages reachable from Setup nav" closes when that entry lands. Until then the pages open only by URL.
  • "Refused verbatim" reads the sentence as the console shows every write refusal: the server's text without the [Security] machine prefix (extractWriteErrorMessage). The tests pin that exact string.
  • The pages list the audience anchors (everyone, guest) like any other position, as the item pages do; the page adds no rule of its own about them.
  • The section components still carry their own inline English / Chinese copy (useMetadataLocale); only the page chrome uses the ten-language packs.

Verification

Local, against head eab3e80a, on a shared box:

Tests and type-check
  • pnpm --dir ui exec vitest run packages/app-shell/src/views/setup/ …item-page and section tests… packages/i18n/ → 119 files, 1691 passed, before the de pin and literal-key fixes; after them packages/i18n/ packages/app-shell/src/views/setup/ → 105 files, 1577 passed, 13 skipped, exit 0.
  • pnpm --dir ui exec vitest run scripts/__tests__/check-i18n-call-site-keys.test.ts scripts/__tests__/check-i18n-dead-keys.test.ts packages/app-shell/src/views/setup/ → 14 files, 427 passed, exit 0.
  • pnpm --filter @object-ui/i18n run type-check && pnpm --filter @object-ui/app-shell run type-check → exit 0.
  • pnpm --dir ui check:new-line-citations → 0 new citations, exit 0.
Derived gates (dispatch-gates --commands, 34 lines)
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran LIST → 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. The ui scripts suite read 169 files passed, 2 skipped (4901 tests).


Generated by Claude Code

…assignment

Two Setup component pages under People & organization: pick a position or
a permission set from the registry catalog and manage who holds it through
the same section the item page renders. The picked item lives in the URL.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibvdtrVLjcNWmcBoHnP
…s it; count the new de quote pair

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibvdtrVLjcNWmcBoHnP
…rally at each call site

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibvdtrVLjcNWmcBoHnP
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-tesla · session_01L9qibz8HEj53Wx2uphTo2o

维护者速读

席位意见:页面只负责挑选,读写和权限沿用既有区块,不重复判定;侧栏入口拆给服务端卡,本 PR 是 Part of,可直接落地。

席位核对

ACCEPT · seat domain:ui#3 · head eab3e80a · 2026-10-31T04:29Z

  1. Shape: Part of #28241, draft, base main; 21 files, +1140/−3: CatalogAssignmentPage (registry catalog list by label, filter, picked item in ?position= / ?set=, load failure via getTypeStatus === 'error' with retry past the cache), PositionHoldersPage / PermissionSetAssignmentPage rendering the unmoved PositionHoldersSection / AssignedUsersSection keyed by item, refs security:position-holders / security:permission-set-assignment registered in builtinComponents.tsx, setupAssignments.* keys in all ten packs, four test files (20 tests), de-quote-pairing-3876 pin 101 → 102, changeset @object-ui/app-shell + @object-ui/i18n patch. Seat read of the diff.
  2. Premise measured: the sections render only inside item pages on main (PositionHoldersSection in the position page, AssignedUsersSection under PermissionMatrixEditor); ablation of the failure branch reds 1 (dev).
  3. Gates / check-runs: on head eab3e80a every latest check-run is success or skipped (44 / 11, 0 failure), mergeable_state clean. Dev: 34/34 derived gates exit 0, --ran 0 NOT-MEASURED, 0 UNRUN; app-shell + i18n type-check 0; item-page controls green (os-dev-report 6538865255); writes relay 2, MCP 0.
  4. Accepted deviations: builtinComponents.tsx registration block and the de-quote-pairing pin, both forced by the change and outside no sibling region.
  5. Out of scope / card state: the Setup nav entries are server metadata; carried to domain:server as Setup nav: People & Organization has no entries for the new Position holders and Permission set assignment pages (security:position-holders, security:permission-set-assignment) #28316 (Refs Setup nav: People & Organization has no entries for the new Position holders and Permission set assignment pages (security:position-holders, security:permission-set-assignment) #28316). Setup: position holders and permission-set assignment as people-and-organization pages #28241 stays open until Setup nav: People & Organization has no entries for the new Position holders and Permission set assignment pages (security:position-holders, security:permission-set-assignment) #28316 lands. Skills-docs: none — Metadata-protocol: none.
  6. Landing: check-governed-merges --pr 28310 NOT governed, 1143 changed lines ≤ 3000; no contract review owed (Clause-②: no, no packages/spec path); changeset present. The seat readies it and arms auto-merge under the maintainer's 「按 pm-dispatch 审过的pr你可以自主落地」.

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/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant