Skip to content

feat(plugin-security): Setup People & Organization entries for position holders and permission-set assignment - #28332

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-28316-setup-people-org-entries
Oct 31, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-28316-setup-people-org-entries

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #28316

Clause-②: no
Metadata-protocol: none
Skills-docs: none changed. skills/** has zero hits for People & Organization Setup navigation text. content/docs/permissions/administrator-guide.mdx (lines 51, 75, 209-211) and content/docs/ui/apps.mdx (line 813, the group_people_org row) list the group's entries and do not yet name the two new pages; they are outside this claim's file surface and are worth a docs pass once the pages ship.

Lands after PR #28310 (the two app-shell pages). Before it merges, the entries are harmless: a component ref with no registration opens the console's not-found page.

维护者速读

  • 改了什么:Setup → 人员与组织 新增两个菜单项「岗位持有人」(security:position-holders) 和「权限集分配」(security:permission-set-assignment),由 plugin-security 贡献,四种语言标签齐全。
  • 为什么改:PR feat(app-shell): Setup pages for position holders and permission-set assignment #28310 新增的两个分配页面只能手敲网址进入;Setup 菜单是服务端元数据,必须由服务端声明入口,管理员才能点到。
  • 风险与回滚:极低,只加两条导航数据,不改任何权限或数据路径;回滚即 revert 本 PR。feat(app-shell): Setup pages for position holders and permission-set assignment #28310 合并前点击会看到“组件不存在”页。
  • 先看:packages/plugins/plugin-security/src/security-plugin.ts(新增的 group_people_org 贡献块)、packages/plugins/plugin-security/src/people-org-assignment-nav-contribution.test.ts、packages/platform-objects/src/apps/translations/zh-CN.ts。
  • 两分钟验证:pnpm --filter @objectstack/plugin-security exec vitest run src/people-org-assignment-nav-contribution.test.ts,预期 5 个测试全部通过。
  • 需要你做的:无,席位落地(等 PR feat(app-shell): Setup pages for position holders and permission-set assignment #28310 合并后再进队列)。

What changes

  • @objectstack/plugin-security contributes a second Setup navigation block, group: 'group_people_org', priority 200 (after platform-objects' base entries at 100), with two items:
    • nav_position_holders · type: 'component' · label "Position Holders" · componentRef: 'security:position-holders' · icon user-check
    • nav_permission_set_assignment · type: 'component' · label "Permission Set Assignment" · componentRef: 'security:permission-set-assignment' · icon key-round
  • Gate: copied from the plugin's Positions / Permission Sets entries, which carry none of their own (no requiredPermissions, requiresService, requiresObject or visible): the Setup app's requiredPermissions: ['setup.access'] and the ungated group anchor decide who sees them (ADR-0146 D7 names the pages as the scope of setup.access and the delegated-administrator gate). The writes the pages make keep their own server-side gates. Riding plugin-security's manifest, the entries disappear wherever the plugin is not composed, exactly like the catalog entries.
  • Labels (measured owner): plugin-security's own translation bundle has no apps section; every Setup nav label, plugin-security's catalog entries included, lives in @objectstack/platform-objects' hand-authored Setup bundles (apps.setup.navigation.ID.label). Both ids are added to all four locales that carry the group (en, zh-CN, ja-JP, es-ES), and each translated leaf's source digest is recorded in LOCALE.source-hashes.ts via collectSourceHashes(en) (control: the computed digest for nav_positions equals its recorded 517a2743f949b5f0). No *.generated.ts file is involved; pnpm check:i18n confirms all generated bundles stay in sync.
  • Changeset: @objectstack/plugin-security patch, @objectstack/platform-objects patch.

Before (measured on origin/main b339465): git grep "group: 'group_people_org'" outside tests and translations names one contributor, packages/platform-objects/src/apps/setup-nav.contributions.ts, whose block has 0 componentRef items; git grep for either ref outside ui/ returns 0 files (control: componentRef: 'automation:packaged' returns 1 source hit).

Tests

New packages/plugins/plugin-security/src/people-org-assignment-nav-contribution.test.ts (5 cases), modelled on plugin-audit's audit-nav-contribution.test.ts: it runs the plugin's real init() and folds the Setup app through the real SchemaRegistry.

  1. Both entries in one group_people_org contribution, with the exact componentRefs, as registry keys, and every contribution parses with NavigationContributionSchema.
  2. Same gate as nav_positions and nav_permission_sets: the four gate keys compare equal (control: both catalog entries exist).
  3. The rendered Setup nav serves both under People & Organization beside nav_users, the group anchor's gate equals Access Control's, and each ref opens in exactly one place.
  4. Without plugin-security, neither entry appears, exactly where nav_positions / nav_permission_sets are also absent (control: nav_users still served).
  5. Labels: ids are read off the manifest; every locale of the served SetupAppTranslations that carries group_people_org (measured: the four) labels both, en equals the declared label, and no translated locale serves the English string.

No existing test enumerates plugin-security's Setup entries; setup-nav-dead-key-tombstone.test.ts and app-nav-translation-parity.test.ts deliberately do not list Setup's contributed leaves (that is pnpm check:app-nav-i18n, which boots plugin-security and now judges 60 merged Setup ids, OK in 4 locales), so neither was extended.

Ablations (each reverted, red shown, restored; scripts/ablation-replace.mjs, restore proven by blob equal to HEAD and an empty git diff HEAD)

# Mutation Red
A1 delete both items from the plugin's contribution cases 1, 2, 3, 5 (4 failed, 1 passed)
A2 security:position-holders to security:positions cases 1, 3
A3 add requiredPermissions: ['manage_platform_settings'] to nav_permission_set_assignment case 2
A4 contribute the block to group_access_control cases 1, 3, 5
A5 delete the zh-CN nav_position_holders label (platform-objects rebuilt) case 5 (zh-CN nav_position_holders: expected undefined to be truthy)
A6 platform-objects itself contributes nav_position_holders (rebuilt; dist preflight: marker present in 4 built files) cases 3, 4
A7 es-ES nav_permission_set_assignment label set to the English string (rebuilt) case 5

After the platform-objects legs, the package was rebuilt from HEAD; dist/ carries the restored labels and the file reruns 5/5 green.

Acceptance notes

  • The ja-JP labels follow the Setup menu's own vocabulary ("ポジション", as the neighbouring Positions entry reads), not the page title PR feat(app-shell): Setup pages for position holders and permission-set assignment #28310 ships ("役職の保持者"); es-ES likewise follows "Posiciones". The zh-CN labels match the page titles.
  • scripts/ablation-dist-preflight.mjs --absent read the A5 / A7 markers as absent vacuously: esbuild escapes non-ASCII, so dist/ never carries the raw spelling. The red runs themselves prove those mutations reached dist/ (an undefined label; the English string served), and the restored escaped spellings were read back from dist/apps/index.mjs directly.

Verification

Head d1bf4821, tree clean:

  • pnpm --filter @objectstack/plugin-security vitest: Test Files 238 passed (238), Tests 5124 passed, 45 skipped. Exit 0.
  • @objectstack/platform-objects vitest: Test Files 104 passed (104), Tests 1474 passed. Exit 0.
  • pnpm --filter @objectstack/plugin-security typecheck exit 0; pnpm --filter @objectstack/platform-objects typecheck exit 0.
  • pnpm lint exit 0.
  • pnpm build (full, under the verify lock) exit 0.
  • node scripts/pm/dispatch-gates.mjs --commands: 62 families, each run with its exit code recorded before any pipe. Three first exited 3 (PREREQUISITE NOT MET: no built tree, or the lock queue) and were rerun after the full build: check:i18n exit 0 (10 packages, all bundles in sync), check:dual-build-cjs-loads exit 0, check:type-check-debt exit 0. --ran: "62 derived famil(ies) accounted for — 62 run, 0 NOT-MEASURED (a DERIVED zero — all 62 recorded an exit code and none of them is 3)".
  • Not derived, run as well: pnpm check:app-nav-i18n exit 0, "13 contributor(s), 4 locale(s), 2 app(s) — setup: 60 merged nav id(s) … every id labelled in every locale".

🤖 Generated with Claude Code

https://claude.ai/code/session_016WV4czVnUdQQCVKSW5J2uk

…on holders and permission-set assignment

Two type: 'component' entries under group_people_org, contributed by
plugin-security beside its catalog entries and gated the same way (no item
gate; Setup's setup.access and the ungated group anchor decide). Labels in
the four platform-objects Setup bundles, with recorded source digests.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4czVnUdQQCVKSW5J2uk
…es and their labels

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4czVnUdQQCVKSW5J2uk
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/platform-objects, @objectstack/plugin-security, touching 7 documentable anchor(s).

5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/permissions/field-level-security.mdx (via SecurityPlugin (symbol, a top-level class))
  • content/docs/permissions/index.mdx (via SecurityPlugin (symbol, a top-level class))
  • content/docs/plugins/packages.mdx (via SecurityPlugin (symbol, a top-level class))
  • content/docs/ui/apps.mdx (via group_people_org (literal, a string literal in SecurityPlugin))
  • content/docs/ui/forms.mdx (via SecurityPlugin (symbol, a top-level class))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/implementation-status.mdx (via SecurityPlugin (symbol, a top-level class))
  • content/docs/releases/v18/18-0.mdx (via SecurityPlugin (symbol, a top-level class))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see

Coarse fallback — 18 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 501c864dd5918a7bab1f3a59e1246b8a0ddff48c → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 28b3ad8816eb318c8e8bad6c18e4ad541e02f755 — the merge of head d1bf48213a5bb8a2f9a2f08977ec9ba64ee6ec6c into base 501c864dd5918a7bab1f3a59e1246b8a0ddff48c, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 28b3ad8816eb318c8e8bad6c18e4ad541e02f755 && git checkout 28b3ad8816eb318c8e8bad6c18e4ad541e02f755
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 501c864dd5918a7bab1f3a59e1246b8a0ddff48c d1bf48213a5bb8a2f9a2f08977ec9ba64ee6ec6c && git checkout -B drift-repro 501c864dd5918a7bab1f3a59e1246b8a0ddff48c && git merge --no-ff d1bf48213a5bb8a2f9a2f08977ec9ba64ee6ec6c

node scripts/docs-audit/affected-docs.mjs --json 501c864dd5918a7bab1f3a59e1246b8a0ddff48c

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 501c864dd5918a7bab1f3a59e1246b8a0ddff48c → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#1 · os-elon-musk · session_016WV4czVnUdQQCVKSW5J2uk

Seat ACCEPT · PR #28332 · head d1bf48213a5bb8a2f9a2f08977ec9ba64ee6ec6c · 2026-10-31T05:39Z

维护者速读

席位核对

  • Shape: draft → main, Fixes #28316. 10 files, +254/−0: one contribution block in security-plugin.ts, a new test file, labels plus recorded source digests in platform-objects' Setup bundles (en, zh-CN, ja-JP, es-ES), and one changeset (patch ×2). check-governed-merges --pr 28332 on the final file list: NOT governed.
  • Declarations: Clause-②: no, Metadata-protocol: none. Two navigation items naming existing component refs; nothing in packages/spec.
  • Diff read (seat): the items carry no gate of their own, the same as nav_positions / nav_permission_sets in the same plugin, and that is what the card asked for. Priority 200 keeps them after platform-objects' base entries. The labels live in the bundle that already owns the group's labels (plugin-security's bundle has no apps section).
  • Tests: 5 pins over the real init() and the real Setup fold: refs, gate parity, placement, absence without the plugin, and labels in every locale. 7 ablations, each red, restored. plugin-security 5124 passed, platform-objects 1474 passed, typecheck 0 for both.
  • Local (seat subagent, report 6539481261): 62/62 derived gate families exit 0 (0 NOT MEASURED after a full build); pnpm lint 0; check:app-nav-i18n 0.
  • Serial: the card lands after PR feat(app-shell): Setup pages for position holders and permission-set assignment #28310 (the two pages). feat(app-shell): Setup pages for position holders and permission-set assignment #28310 merged before this ACCEPT.
  • Noted, not filed: administrator-guide.mdx and ui/apps.mdx list People & Organization's entries without the two new pages. A docs pass can pick that up.
  • Gate check-runs on this head: 48, 35 success, 13 skipped, 0 failed. mergeable_state: clean.
  • Landing: non-governed, under the maintainer's landing grant for this seat term (2026-10-30 10:20Z, seat-1 thread). Ready + auto-merge through the relay.

ACCEPT

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 31, 2026 05:40
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 31, 2026 05:40
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 31, 2026
Merged via the queue into main with commit e2772b4 Oct 31, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-28316-setup-people-org-entries branch October 31, 2026 06:07
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 tests tooling

Projects

None yet

1 participant