Repository navigation
Commit 5cb1c1a
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 #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)
- **"All"** grants View All and Modify All, so it now drops both scopes,
the same as the per-checkbox cascade. After the click the row holds
Modify All, so both pickers are greyed. `allowExport` and any other
unauthored key still ride through, as #6605 requires.
`PermissionMatrixEditor.bulkMergeKeys.test.tsx` flips its two "All" pins
(environment door and package door) from "keeps `readScope: 'own'`" to
"drops both scopes".
- **"None"** is unchanged: it replaces the row with `{}`, scopes
included (the #6605 fence). No super-user bit remains, so both pickers
are open and show "Not set".
- **"CRUD" / "R"** reset View All and Modify All, and the scopes ride
through. Those pins are kept as they were.
## 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 #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
- **File surface.**
`PermissionMatrixEditor.retiredLifecycleKeys.test.tsx` pinned the header
strip as `slice(1, -1)`. The two new columns made that pin false, so it
now reads `slice(1, -3)` and also pins the two scope headers. The
claim's file surface did not list this file.
- **Stored pre-#16870 pair (not changed here).** A row stored before
#16870 can hold View All plus `readScope`. 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 on `origin/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.
- **"Only granted" and the granted count (observation, not filed).**
Both count a row with any truthy key as granted
(`Object.values(perm).some(Boolean)`). A row that carries only a scope
is therefore listed. This was already true on `origin/main` for
`allowExport` and 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-manifest` exits 0.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01SMps7wq7rBTk53e8KTSEup)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent d3bdf43 commit 5cb1c1a
7 files changed
Lines changed: 624 additions & 42 deletions
File tree
- .changeset
- ui/packages/app-shell/src/views/metadata-admin
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | | - | |
14 | | - | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
15 | 24 | | |
16 | 25 | | |
17 | 26 | | |
| |||
145 | 154 | | |
146 | 155 | | |
147 | 156 | | |
148 | | - | |
| 157 | + | |
149 | 158 | | |
150 | 159 | | |
151 | 160 | | |
| |||
155 | 164 | | |
156 | 165 | | |
157 | 166 | | |
158 | | - | |
159 | | - | |
160 | | - | |
161 | | - | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
162 | 172 | | |
163 | | - | |
164 | | - | |
165 | | - | |
| 173 | + | |
166 | 174 | | |
167 | 175 | | |
168 | 176 | | |
| |||
234 | 242 | | |
235 | 243 | | |
236 | 244 | | |
237 | | - | |
| 245 | + | |
238 | 246 | | |
239 | 247 | | |
240 | 248 | | |
| |||
251 | 259 | | |
252 | 260 | | |
253 | 261 | | |
254 | | - | |
255 | | - | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
256 | 265 | | |
257 | 266 | | |
258 | 267 | | |
| |||
0 commit comments