Skip to content

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 #26191

Closed

Description

@objectstack-fleet

Filing gate ②: a maintainer ruling, carried out as child 5 of the nine that #26059's ruling (Q3) names. ⛔ Not a claim.

An admin can configure a Delegated Admin Scope on a permission set and grant the set to a user. That user then opens Setup and gets "You don't have access to this app". To act, they also need:

  • the capability setup.access
  • read access to sys_user, sys_business_unit and sys_business_unit_member, so the lookups can pick people and units
  • CRUD on sys_user_position

Nothing in the scope block says so, and the docs page leaves out setup.access. The server is right to refuse. The gap is that the editor gives no hint and offers no fix.

Part of #26059
Ruling-ref: 6416233738 (#26059 (comment))
Ruling: batch #396 item 1, letter Q1 是 / Q2 不做 / Q3 分拆 / Q4 框架. This card is Q3 child 5 (ui + docs, p2): item 2. Brief 6415795834, item-by-item reading 6415971852 (item 2).
Filed by domain:ui seat 2 (domain:ui#2) · os-steve · session session_01SMps7wq7rBTk53e8KTSEup, first claimant of #26059. Domain, grade and route are triage's on first touch.

Business reason. Delegated administration lets a tenant hand one subtree's assignments to a regional manager. As shipped, the admin who sets it up gets a delegate who cannot open Setup and has to work out four further grants alone, by trial and error.

Reader who acts. The domain:ui execution seat, dispatch step: one os-dev, one PR on ui/packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tsx (with its i18n strings) and the docs page content/docs/permissions/delegated-administration.mdx in the same PR.

Spec contract. None changes and no key is new. The grants go into the set's existing systemPermissions (packages/spec/src/security/permission.zod.ts:1115) and objects (:1109).

Duplicate check. A local scan of the 1,962 newest issues (all states, #22168–#26181) for setup\.access.{0,200}(delegat|adminScope) and the reverse order found 1 hit, not this (#24135, ADR-0090 D12 wording). #23323 (closed) fixed another sentence on the same docs page.

Ruled scope (ruling Q3 item 5)

  • The Delegated Admin Scope block lists the companion grants:
    • setup.access
    • sys_user / sys_business_unit / sys_business_unit_member read
    • sys_user_position CRUD
  • It offers a one-click "grant these" that adds them to the same set: one save, no new key.
  • delegated-administration.mdx gains setup.access.
  • Not a server change.

Anchors (re-read on origin/main 83a26c38)

  • packages/spec/src/security/capabilities.ts:49: { name: 'setup.access', label: 'Setup Access', description: 'Enter the Setup app shell.' }. Holds.
  • Docs file path verified: content/docs/permissions/delegated-administration.mdx.
    • The authoring example now spans :20–40, and its object grants are :33–38 (sys_user_position CRUD, sys_business_unit / sys_business_unit_member / sys_user read).
    • The ruling cited :26–36, which has drifted. Corrected.
    • setup.access appears nowhere on the page.
  • ui/packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tsx:
    • The scope state and the objectui#9464 business-unit gate are at :404–433 (the ruling's :405–420 falls inside), with scopeBlocksSave at :444–445.
    • The block renders at :677 ("Delegated Admin Scope" FacetSection). Holds.

Expected / acceptance

  • The Delegated Admin Scope block shows the five companion grants. Each is marked present or missing on the set being edited.
  • "Grant these" adds only the missing ones to the same draft (systemPermissions gains setup.access; objects gains the reads and the sys_user_position CRUD), never removes or narrows an existing grant, and is saved with the set's normal Save.
  • The hint and the action are hidden or inert while no scope is configured, matching the block's existing gate.
  • delegated-administration.mdx's example carries systemPermissions: ['setup.access'], with one sentence explaining why a delegate needs it.
  • New strings exist in every locale the block's existing strings are in. Tests cover the present/missing state and the merge.

Activity

  1. added
    enhancementNew feature or request
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    on Oct 26, 2026
  2. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — as filed · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-26T16:01Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 6 · 2026-10-26T19:22Z
    Session: session_01SMps7wq7rBTk53e8KTSEup
    Account: os-steve
    Branch: claude/issue-26191-delegate-companion-grants
    Worktree: objectstack-issue-26191
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: ui/packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tsx (the Delegated Admin Scope block), ui/packages/app-shell/src/views/metadata-admin/i18n.ts (new strings, en + zh, after #26284 merges), tests beside them, content/docs/permissions/delegated-administration.mdx (add setup.access), root .changeset/. ⛔ PermissionMatrixEditor.tsx / permission-slice.ts (#26284), the server, packages/spec, locale packs under ui/packages/i18n, ui/.github/**, root ci.yml.
    Container & model: M, mode:subagent, model: default tier, effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: a delegate given an Admin Scope who still cannot enter Setup or pick people | the set's existing systemPermissions and objects keys (permission.zod.ts :1109, :1115) and capabilities.ts setup.access | every admin who configures delegated administration
    Thread-read: 6420718254
    Premise: holds 2026-10-26T19:22Z at origin/main be531f685e. PermissionAdvancedFacets.tsx edits adminScope with no hint about the companion grants, and delegated-administration.mdx never names setup.access.
    Serial constraints cleared: #26187 PR 1 (#26284) shares metadata-admin/i18n.ts and is in the merge queue. This card develops now, merges main once #26284 has MERGED, and opens its PR only after that.
    Writes: PR only via the relay op pr_create; comments only via the relay write tools; ⛔ no GitHub MCP writes.

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26191,
      "status": "done",
      "branch": "claude/issue-26191-delegate-companion-grants",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26340",
      "head": "8ecd73449fa2a8b689f335d5c5a9b577cc774a2c",
      "session": "session_01SMps7wq7rBTk53e8KTSEup",
      "premise_still_valid": true,
      "summary": "PermissionAdvancedFacets' Delegated Admin Scope block now lists the five companion grants (setup.access; read on sys_user, sys_business_unit, sys_business_unit_member; CRUD on sys_user_position) once the scope names a business unit. Each is marked granted or not on the set being edited. A 'Grant the missing ones' button adds only the missing grants in one setDraft call (systemPermissions and objects, no new key, nothing narrowed). Under a package binding the list is shown unmarked, with a note and without the button. The binding comes from a new optional packageBound prop, which defaults to only !== undefined because PermissionMatrixEditor.tsx is off this claim's surface. The docs page gains setup.access in its example and a paragraph on the five grants, and nine en+zh strings were added. #26284 had MERGED (merged=true) before the PR opened, and main was merged in twice with merge commits (c3066c2b, 8ecd7344).",
      "files_changed": [
        ".changeset/26191-delegate-companion-grants.md",
        "content/docs/permissions/delegated-administration.mdx",
        "ui/packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.companionGrants-26191.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/i18n.ts"
      ],
      "line_budget": "+469 / -1 over 5 files (under the 3000 human-merge threshold)",
      "tests": "RED at c3066c2b (test committed, component unchanged), lock VERDICT command-exit 1: new file 9 failed / 1 passed. The pass is 'hidden until a business unit is named', which is true before and after; the failures were Unable to find data-testid admin-companions and similar. GREEN at 8ecd7344, lock VERDICT command-exit 0: cd ui; pnpm exec vitest run (254 app-shell test files importing PermissionAdvancedFacets or views/metadata-admin/i18n, the new file included) --maxWorkers=2 gave Test Files 254 passed (254), Tests 2578 passed (2578). pnpm --filter @object-ui/app-shell type-check (tsc --noEmit plus tsc -p tsconfig.test.json, so tests are compiled too), after building the @object-ui/app-shell^... closure: CLOSURE_EXIT=0, TYPECHECK_EXIT=0, lock VERDICT command-exit 0. eslint on the 3 changed source files: exit 0, 0 errors, 5 warnings, of which 2 are new no-explicit-any in the test harness's setDraft updater, matching the prop's own any. Full app-shell suite not run locally, per the card; CI runs it. No ablation: the red run is the before/after proof and no tested behaviour was otherwise mutated.",
      "gates": "Derived at 8ecd7344 with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 68 commands. Each was run with its exit code captured before any pipe, and --ran reconciled them as 68 derived, 66 run (all exit 0), 2 NOT-MEASURED, 0 UNRUN. NOT MEASURED (exit 3, PREREQUISITE NOT MET): pnpm --filter @objectstack/spec run check:skill-examples, because packages/client-react/dist was not built; the edited docs block carries no type-check marker. Also NOT MEASURED: pnpm check:type-check-debt, whose closure build needs the shared lock and timed out in the queue. CI's Lint & Repo Gates runs both. The spec-family gates were re-run after a spec rebuild on the merged tree: 7 exit 0 plus skill-examples exit 3, as above. node scripts/check-sdui-manifest.mjs exit 0 (app-shell has no sdui record). node scripts/pm/check-governed-merges.mjs --test over the 5 paths exit 0, NOT governed (content/docs is not in the register). PR check-runs: in_progress at report time, not awaited per the contract.",
      "dogfood_battery": "n/a: no ! in the PR title and Clause-2 is no with no (narrowing) arm",
      "deviations": [
        "The package-binding signal is read from the facet's own only prop (optional packageBound prop, default only !== undefined), not from packageId. PermissionMatrixEditor.tsx is on the claim's no-touch list, so packageBound={!!packageId} is not wired. Today the two coincide: packageId reaches the editor only from AccessPillar, always with studio chrome, which is what sets only.",
        "Under a package binding the five rows are listed WITHOUT a granted/missing mark (the seat's route said each shows whether the set grants it). The package door's draft is sliced to the package's own objects, so a missing sys_* row there says nothing about the stored set, and a mark would mislead.",
        "The target test list is broader than the facet's direct importers. 254 files import views/metadata-admin/i18n, about 14% of app-shell's 1746 test files, and all were run (about 18 minutes under the lock).",
        "Lock contention: several queue-timeout (exit 99) attempts before each locked run, all re-queued with OS_VERIFY_LOCK_SLOT=issue-26191-dev. None of them is counted as a result."
      ],
      "mcp_calls": "0",
      "api_writes": "2: (1) relay op pr_create through scripts/pm/fleet-write/dispatch.mjs, run 40407600102: POST /repos/objectstack-ai/objectstack/pulls (draft) plus POST /repos/objectstack-ai/objectstack/issues/26340/assignees [os-steve]; the body read back identical (6810 bytes). (2) this os-dev-report comment, via scripts/pm/post-stamped.mjs: POST /repos/objectstack-ai/objectstack/issues/26191/comments. git push is not counted.",
      "open_questions": [
        {
          "question": "Wire packageBound explicitly from PermissionMatrixEditor.tsx (packageBound={!!packageId} at both facet mounts) and drop the only-based default?",
          "options": [
            "A: a follow-up card, or a seat-authorized widening of this claim's surface, that adds the one-line prop pass and removes the default",
            "B: keep the only-based default (correct for today's composition: AccessPillar is the only packageId host)"
          ],
          "recommendation": "A, because explicit composition beats an inferred default, and #26284, the reason for the no-touch, has merged; the change is two props and one default removal."
        }
      ],
      "out_of_scope_findings": []
    }
  5. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · seat 2 · 2026-10-26T23:22Z

    probe reading
    PR #26340 MERGED 2026-10-26T22:50Z, merge commit e14d9ce8, head 8ecd7344 (+469/−1, seat record on #26340)
    origin/main contains it (git merge-base --is-ancestor exit 0)
    queue branch gh-readonly-queue/main/pr-26340-*: none left (consumed)
    card closed (completed) by Fixes #26191 on #26340

    Status: the Delegated Admin Scope block lists the five companion grants with granted/missing marks and a one-click grant of the missing ones; under a package binding it lists them unmarked. Explicit packageBound wiring is #26349. pm:dispatched removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:uienhancementNew feature or requestpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions