Repository navigation
fix(app-shell): permission matrix sets and shows readScope / writeScope (#26187) - #26284
Merged
objectstack-fleet[bot] merged 3 commits intoOct 26, 2026
Merged
objectstack-fleet[bot] merged 3 commits into
objectstack-fleet[bot] merged 3 commits into
Conversation
…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>
#26187) Claude-Session: https://claude.ai/code/session_01SMps7wq7rBTk53e8KTSEup Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SMps7wq7rBTk53e8KTSEup Co-authored-by: Claude <noreply@anthropic.com>
Contributor
Author
|
ACCEPT · seat 2 · 2026-10-26T19:19Z · PR #26284 head
Rulings on the open questions (seat 2):
The PR body's Tests / Gates bullets predate the green re-runs above; this comment is the record. Seat's route: ready + auto-merge. |
objectstack-fleet
Bot
deleted the
claude/issue-26187-permission-matrix-record-scope
branch
October 26, 2026 20:10
This was referenced Oct 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #26187
Clause-②: no
Metadata-protocol: none
PR 1 of 2 for #26187: the matrix editor. PR 2 wires
PermissionPreviewread-only into the permission-set and position pages, so #26187 remains open after this merges.What changes
PermissionTableand in Studio Access'sStudioObjectRowgains a Read scope and a Write scope picker. Each picker is the sharedSelectthat the facet pickers use (objectui#11865). It lists "Not set" plus the fiveObjectAccessScopeSchemavalues 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.own_and_reports,unitandunit_and_belowshow 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 whetherhierarchy-securityis licensed.readScopebesideviewAllRecords: 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 itstitle.readScope. Granting Modify All drops both scopes. One helper,dropScopesBypassedBySuperUserBits, applies the rule in the per-checkbox cascade and in the "All" bulk arm. Onorigin/main, both paths saved a row that the BASE schema refuses. Effective access does not change: the resolvers answerorgon the super-user bit before they read a scope.ObjectPermgainsreadScope?/writeScope?, typed with the spec'sObjectAccessScope. The boolean columns get their own type,ObjectPermFlag, so a checkbox can no longer address a scope key.OBJECT_ACCESS_SCOPESrestates the five values with a type-only import. The reason is the oneuseTenancyPosturedocuments: the@objectstack/spec/securityvalue entry costs about 237 KB of bundle. A test compares the list withObjectAccessScopeSchema.options.HIERARCHY_SCOPESis typed with the spec'sHierarchyScope.metadata-admin/i18n.ts(perm.scope.*, en + zh). The column headers reuseengine.permissionPreview.readScope/writeScope. Noui/packages/i18nlocale pack is touched.How the greying covers the two bulk paths (the card's question)
allowExportand any other unauthored key still ride through, as type-alias-convention.pin.test.ts 的分节注释写着 "717 isomorphic aliases",实际 822 —— 唯一没被计数断言盖住的数字 #6605 requires.PermissionMatrixEditor.bulkMergeKeys.test.tsxflips its two "All" pins (environment door and package door) from "keepsreadScope: 'own'" to "drops both scopes".{}, scopes included (the type-alias-convention.pin.test.ts 的分节注释写着 "717 isomorphic aliases",实际 822 —— 唯一没被计数断言盖住的数字 #6605 fence). No super-user bit remains, so both pickers are open and show "Not set".Evidence
All figures are from
ui/vitest on this branch, run underscripts/pm/os-verify-lock.sh.PermissionMatrixEditor.tsx,permission-slice.ts,i18n.ts) were set back toorigin/maindf8aac41(hash-equal to the BASE blobs, 0perm-scope-markers). The new and flipped suites then gaveTests 17 failed | 5 passed (22). All 15 behaviour pins ofrecordScope-26187failed, and so did both "All" pins ofbulkMergeKeys. 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 emptygit diff HEAD.dropScopesBypassedBySuperUserBits(next);was deleted once withscripts/ablation-replace.mjs(anchor x1 → x0, blobb275ac54→d01962e8).bulkMergeKeysthen gaveTests 2 failed | 3 passed (5): both "All" pins failed and CRUD / R / None stayed green. Restored: blob == HEAD andgit diff HEADis empty.client.savewith no draft mode). At the package door, the autosaved draft goes throughmergePermissionSlice, 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 realObjectPermissionSchema.safeParse, not with a restatement of its rule.Tests (local, each under
scripts/pm/os-verify-lock.sh)recordScope-26187,bulkMergeKeysandretiredLifecycleKeys:Test Files 3 passed (3),Tests 26 passed (26)at93c9211e. The later merge oforigin/main(c696351d) touched none of the seven files in this diff.pnpm --filter @object-ui/app-shell type-check(both tsc programs) atc696351d, afterturbo run build --filter='@object-ui/app-shell^...'(64/64 tasks):VERDICT command-exit 0.--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-sliceor the metadata-admini18n, plus the Studio Access suites. That set was still queued for the lock when this PR was opened, so its result goes in theos-dev-reportcomment on Permission matrix (Setup / Studio Access): readScope and writeScope cannot be set or seen, though ADR-0084 declares the record-scope column #26187. CI'sTest UIruns its affected selection over this diff.Gates at
c696351d(dispatch-gates.mjs --commands, every command run and reconciled with--ran)check-sdui-manifest(0, the manifest is untouched),check:nul-bytes,check:cross-package-test-inputs,check:test-source-aliasandcheck:type-check-coverage.check:type-check-debtis NOT MEASURED (exit 3,PREREQUISITE NOT MET): its closure build timed out in the shared lock's queue. CI runs it inLint & Repo Gates.Acceptance notes
PermissionMatrixEditor.retiredLifecycleKeys.test.tsxpinned the header strip asslice(1, -1). The two new columns made that pin false, so it now readsslice(1, -3)and also pins the two scope headers. The claim's file surface did not list this file.readScopebesideviewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870 pair (not changed here). A row stored before A permission set accepts a hierarchyreadScopebesideviewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870 can hold View All plusreadScope. Such a row loads with the read picker greyed and showing the stored value. Saving it unchanged sends the pair, and the server refuses it, as it does onorigin/main. The matrix does not strip the key on load, because that would delete stored data without the admin acting. To clear it, untick View All, set "Not set", then tick View All again. This is in the report's open questions.Object.values(perm).some(Boolean)). A row that carries only a scope is therefore listed. This was already true onorigin/mainforallowExportand for scopes set through the API. It is noted, not filed. Carrier: none.scripts/sdui-manifest.*is untouched (app-shell is outside the sdui registry closure).check-sdui-manifestexits 0.Generated by Claude Code