Skip to content

fix(app-shell): permission matrix sets and shows readScope / writeScope (#26187) - #26284

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-26187-permission-matrix-record-scope
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-26187-permission-matrix-record-scope

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #26187

Clause-②: no
Metadata-protocol: none

PR 1 of 2 for #26187: the matrix editor. PR 2 wires PermissionPreview read-only into the permission-set and position pages, so #26187 remains open after this merges.

What changes

  • Pickers in both renderers. Every object row in Setup's PermissionTable and in Studio Access's StudioObjectRow gains a Read scope and a Write scope picker. Each picker is the shared Select that the facet pickers use (objectui#11865). It lists "Not set" plus the five ObjectAccessScopeSchema values in the spec's order. A pick writes the key through a new mutator, updateObjectScope. "Not set" deletes the key. Every other key of the row rides through.
  • Hierarchy hint. own_and_reports, unit and unit_and_below show the hint "requires enterprise; otherwise owner-only" on their items, and stay selectable. Per the seat's ruling, the hint is unconditional: no client signal says whether hierarchy-security is licensed.
  • Greying. These are the A permission set accepts a hierarchy readScope beside viewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870 pairs, mirrored from the server and not decided by the client. View All disables the read picker. Modify All disables both pickers. Each disabled trigger carries the reason as its title.
  • Clearing rule. Granting View All drops readScope. Granting Modify All drops both scopes. One helper, dropScopesBypassedBySuperUserBits, applies the rule in the per-checkbox cascade and in the "All" bulk arm. On origin/main, both paths saved a row that the BASE schema refuses. Effective access does not change: the resolvers answer org on the super-user bit before they read a scope.
  • Types and vocabulary. ObjectPerm gains readScope? / writeScope?, typed with the spec's ObjectAccessScope. The boolean columns get their own type, ObjectPermFlag, so a checkbox can no longer address a scope key. OBJECT_ACCESS_SCOPES restates the five values with a type-only import. The reason is the one useTenancyPosture documents: the @objectstack/spec/security value entry costs about 237 KB of bundle. A test compares the list with ObjectAccessScopeSchema.options. HIERARCHY_SCOPES is typed with the spec's HierarchyScope.
  • Strings. The new strings live only in metadata-admin/i18n.ts (perm.scope.*, en + zh). The column headers reuse engine.permissionPreview.readScope / writeScope. No ui/packages/i18n locale pack is touched.

How the greying covers the two bulk paths (the card's question)

Evidence

All figures are from ui/ vitest on this branch, run under scripts/pm/os-verify-lock.sh.

  • Red first. The three implementation files (PermissionMatrixEditor.tsx, permission-slice.ts, i18n.ts) were set back to origin/main df8aac41 (hash-equal to the BASE blobs, 0 perm-scope- markers). The new and flipped suites then gave Tests 17 failed | 5 passed (22). All 15 behaviour pins of recordScope-26187 failed, and so did both "All" pins of bulkMergeKeys. The 5 that stayed green are expected: the BASE-schema refusal control, the "other column leaves the scopes alone" preservation pin, and the CRUD / R / None pins. The restore was proven by blob hash equal to HEAD and an empty git diff HEAD.
  • Ablation. The "All"-arm dropScopesBypassedBySuperUserBits(next); was deleted once with scripts/ablation-replace.mjs (anchor x1 → x0, blob b275ac54 → d01962e8). bulkMergeKeys then gave Tests 2 failed | 3 passed (5): both "All" pins failed and CRUD / R / None stayed green. Restored: blob == HEAD and git diff HEAD is empty.
  • Round trip at both doors. A picked value is saved, then reopened from the saved record, at the environment door (client.save with no draft mode). At the package door, the autosaved draft goes through mergePermissionSlice, and an out-of-scope row from another package comes back byte for byte. The saved rows after the View All and Modify All ticks are checked with the spec's real ObjectPermissionSchema.safeParse, not with a restatement of its rule.

Tests (local, each under scripts/pm/os-verify-lock.sh)

  • recordScope-26187, bulkMergeKeys and retiredLifecycleKeys: Test Files 3 passed (3), Tests 26 passed (26) at 93c9211e. The later merge of origin/main (c696351d) touched none of the seven files in this diff.
  • pnpm --filter @object-ui/app-shell type-check (both tsc programs) at c696351d, after turbo run build --filter='@object-ui/app-shell^...' (64/64 tasks): VERDICT command-exit 0.
  • The full app-shell suite (1,740 files at --maxWorkers=2) is NOT MEASURED. I stopped my own run after about 50 minutes without a verdict, because it was holding the shared lock. Declared narrowing: the 195 app-shell test files that import the editor, permission-slice or the metadata-admin i18n, plus the Studio Access suites. That set was still queued for the lock when this PR was opened, so its result goes in the os-dev-report comment on Permission matrix (Setup / Studio Access): readScope and writeScope cannot be set or seen, though ADR-0084 declares the record-scope column #26187. CI's Test UI runs its affected selection over this diff.

Gates at c696351d (dispatch-gates.mjs --commands, every command run and reconciled with --ran)

  • 35 families derived and recorded with exit codes: 34 exited 0, including check-sdui-manifest (0, the manifest is untouched), check:nul-bytes, check:cross-package-test-inputs, check:test-source-alias and check:type-check-coverage.
  • check:type-check-debt is NOT MEASURED (exit 3, PREREQUISITE NOT MET): its closure build timed out in the shared lock's queue. CI runs it in Lint & Repo Gates.
  • Changed lines: 666 (+624 / -42) over 7 files.

Acceptance notes


Generated by Claude Code

…pe (#26187)

Every object row in Setup's PermissionTable and Studio Access's
StudioObjectRow gains a read-scope and a write-scope picker over
ObjectAccessScopeSchema's values. The three hierarchy values carry the
enterprise hint, View All greys the read picker, and Modify All greys
both. Granting View All or Modify All drops the scope the BASE schema
refuses beside it, in the per-checkbox cascade and in the All bulk arm.

Claude-Session: https://claude.ai/code/session_01SMps7wq7rBTk53e8KTSEup
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tooling labels Oct 26, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

ACCEPT · seat 2 · 2026-10-26T19:19Z · PR #26284 head c696351d (#26187 PR 1 of 2)

check reading
scope 7 files, +624/−42, all app-shell/src/views/metadata-admin/ plus a root .changeset/; app-shell has no sdui record. One file outside the claim's list (retiredLifecycleKeys test: its header-strip slice moved by the two new columns), declared
behaviour each object row (Setup table and Studio Access) gets Read scope / Write scope pickers: Not set + the five ObjectAccessScopeSchema values in spec order; hierarchy values keep the "requires enterprise; otherwise owner-only" hint and stay selectable
spec refusals dropScopesBypassedBySuperUserBits drops readScope on View All and both scopes on Modify All, in the per-checkbox cascade and the All bulk arm; pickers grey with the reason as a title
red first editor, slice and i18n reverted: 17 of 22 red (the 5 green are the expected controls); ablation of the All-arm call turns exactly the two All pins red
tests the 195 app-shell files importing the editor / slice / i18n: 1629 green; full app-shell suite narrowed locally, Test UI 16/16 green in CI
gates / CI 35 derived, 35 run, exit 0 (check:type-check-debt green on re-run); CI at head 46 success, 8 skipped, 0 failure
governance NOT governed, 666 lines
landing git merge-tree against origin/main clean; #26259 and #26123 touched metadata-admin/ since the base without a textual conflict, and the queue's merge group re-runs CI on the combined tree

Rulings on the open questions (seat 2):

  1. A row stored before A permission set accepts a hierarchy readScope beside viewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870 with View All + readScope: A, leave it. Nothing strips stored data without an admin act, and the server already refuses that save loudly.
  2. OBJECT_ACCESS_SCOPES restated with a type-only import and a parity test against ObjectAccessScopeSchema.options: A, keep it (the useTenancyPosture pattern; drift turns CI red).
  3. The shared Radix Select, with the hint inline on items and as the trigger's title: A, keep it.

The PR body's Tests / Gates bullets predate the green re-runs above; this comment is the record. Seat's route: ready + auto-merge. Part of #26187: PR 2 (the read-only PermissionPreview) stays on the card, and #26191 (same metadata-admin/i18n.ts) is dispatched once this merges.

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/l tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants