Repository navigation
Description
Filing gate ②: a maintainer ruling, carried out as child 1 of the nine that #26059's ruling (Q3) names. ⛔ Not a claim.
A customer admin cannot set or see a permission set's record scope from the console. The matrix in Setup → Permission Sets and Studio → Access offers C / R / U / D / Tr / VA / MA and field read/write, but it has no control for readScope / writeScope (own / own_and_reports / unit / unit_and_below / org). The permission-set and position pages do not show the current scope either. The only way to set it today is PUT /api/v1/meta/permission/:name. Backend, enterprise resolver and explain all handle scope correctly (33/33 UI scenarios in #26059's run); only the editor is missing.
Part of #26059
Ruling-ref: 6416233738 (#26059 (comment))
Ruling: batch #396 item 1, letter Q1 是 / Q2 不做 / Q3 分拆 / Q4 框架; this card is Q1 = Q3 child 1 (ui, p1). Brief 6415795834, item-by-item reading 6415971852 (item 1).
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. A customer admin who builds or changes a permission set has no way to grant org-structure scope ("own", "own and reports", "my unit", "my unit and below", "whole org") and cannot see what a set already grants. This is the customer-facing entry point of the RBAC + org-structure feature. Without it the feature can only be configured by a developer through the API.
Reader who acts. The domain:ui execution seat, dispatch step: one os-dev, one PR in ui/packages/app-shell/src/views/metadata-admin/ (the matrix editor, plus the permission-set and position pages).
Spec contract. None changes. The enum (ObjectAccessScopeSchema) and the contradictory-pair refusal (#16870) are already in @objectstack/spec. No ADR change: ADR-0084:47 already declares the column.
Duplicate check. A local scan of the 1,962 newest issues (all states, #22168–#26181) for (readScope|writeScope|record scope|access scope).{0,200}(matrix|PermissionMatrix|select|UI|editor) and the reverse order found 2 hits, neither about this (#23134 related-list binding, #24034 org-page decision). Triage's scan of the 600 newest (6414323834) found none.
Ruled scope (ruling Q1)
- The permission matrix gains a read-scope and a write-scope select per object row. Values come from
ObjectAccessScopeSchema(packages/spec/src/security/permission.zod.ts:18). - The selects are greyed by the A permission set accepts a hierarchy
readScopebesideviewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870 pairs that the BASE schema already refuses (permission.zod.ts:632): View All beside a read depth, and Modify All beside either depth. The UI mirrors the server's refusal and does not re-implement it. - The three hierarchy values (
own_and_reports,unit,unit_and_below) stay selectable whenhierarchySecurityis not licensed, with the hint "requires enterprise; otherwise owner-only". Hint source:packages/spec/src/kernel/platform-capabilities.ts:224("ADR-0057 hierarchy scopes ship in the enterprise edition", tokenhierarchy-security). The open runtime fails closed to owner-only. previews/PermissionPreview.tsxis wired read-only into the permission-set and position pages.- No spec key and no ADR change.
Anchors (re-read on origin/main 83a26c38)
docs/adr/0084-application-builder-information-architecture.md:47: Access pillar = "a permission matrix (roles × objects × CRUD + record scope)". Holds.docs/adr/0090-permission-model-v2-concept-convergence.md:642: the ObjectUI landing list names "OWD context per object row" and no scope column. Holds.ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx:- The modelled columns are at
:272–283, so the ruling's:273–283is off by one. - The type-alias-convention.pin.test.ts 的分节注释写着 "717 isomorphic aliases",实际 822 —— 唯一没被计数断言盖住的数字 #6605 ride-through comment for
readScope/writeScopeis at:959–981; the ruling's:961–975falls inside it.
- The modelled columns are at
packages/spec/src/security/permission.zod.ts::18ObjectAccessScopeSchemaholds.:632holds: the 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 refusal sits on the BASE schema and is attached at:642.
ui/packages/app-shell/src/views/metadata-admin/previews/PermissionPreview.tsx:78–137holds:SCOPE_ORDERis at:78and theown+reports/unit+belowlabels at:80–86.- The write-wider-than-read warning is at
:124–145and the scope cells at:261–270. - Today it is registered only as the metadata preview for
permission/profile(previews/index.ts:85–86).
- Permission-set page:
PermissionMatrixEditPage, mounted fromui/packages/app-shell/src/services/builtinComponents.tsx:21and Studiostudio-design/StudioDesignSurface.tsx:216. Position page:metadata-admin/ResourceEditPage.tsx, which already importsPositionHoldersSection(:154). - Open-edition fail-closed:
packages/plugins/plugin-sharing/src/sharing-service.ts:2084–2085(resolveOwnerScopeIds: with no hierarchy resolver it returns[me]). The ruling citedsharing-service.ts:377, which on bothcdd4eea9and83a26c38is a phantom-anchor notice comment. Corrected.
Note for the dev (found while verifying, not a new rule). The bulk row arms interact with the new selects:
- "None" replaces the row with
{}(:969–971), which drops any scope. A test pins this:PermissionMatrixEditor.bulkMergeKeys.test.tsx. - "All" grants VA + MA while scopes ride through (
:984–987). On a row that has a scope, that makes a pair the BASE schema refuses.
Say in the PR how the greying covers these two paths. A behaviour question goes on this card and links the ruling.
Expected / acceptance
- Every object row of the matrix, in Setup → Permission Sets and in Studio → Access, has a Read scope and a Write scope select. Each offers the five
ObjectAccessScopeSchemavalues plus "not set". A chosen value saves and reloads unchanged. - Ticking View All disables the read-scope select, and ticking Modify All disables both selects. No save from the matrix sends a pair the BASE schema refuses (A permission set accepts a hierarchy
readScopebesideviewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870). - On a deployment without
hierarchy-security, the three hierarchy values stay selectable and carry the hint "requires enterprise; otherwise owner-only". - The permission-set and position pages show the current read/write scope per object read-only, through
PermissionPreview. - Keys the matrix does not author still ride through a save; the type-alias-convention.pin.test.ts 的分节注释写着 "717 isomorphic aliases",实际 822 —— 唯一没被计数断言盖住的数字 #6605 pins stay green.
- Tests cover rendering, greying, the unlicensed hint and the save round-trip. No change under
packages/specand no ADR change.
Activity
objectstack-fleet commentedon Oct 26, 2026
Triage: first grade — as filed ·
pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-26T15:58Z⛔ Not a claim, ⛔ not a dispatch.
Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 3 · 2026-10-26T16:37Z
Session:
session_01SMps7wq7rBTk53e8KTSEupAccount:
os-steveBranch:
claude/issue-26187-permission-matrix-record-scopeWorktree:
objectstack-issue-26187Domain:
domain:uiSeat:
domain:ui#2File surface (PR 1 of 2):
ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx(getObjectActionsneighbourhood,updateObjectPermcascade :964–979, a new scope mutator, thebulkSetObject"All" arm :1007–1015,PermissionTable:2135+ andStudioObjectMatrix/StudioObjectRow:2398–2630),metadata-admin/permission-slice.ts(ObjectPerm:21–51),metadata-admin/i18n.ts(en + zhperm.*only), tests (flipPermissionMatrixEditor.bulkMergeKeys.test.tsx:148; newPermissionMatrixEditor.recordScope-26187.test.tsx), root.changeset/. ⛔ui/packages/i18n/src/locales/*.ts,packages/spec,ResourceEditPage.tsx,PermissionAdvancedFacets.tsx,ui/.github/**, rootci.yml. No sdui record moves (app-shell has none).Container & model:
M,mode:subagent,model: default tier,effort: highClause-②: no
Metadata-protocol: none
Responsibility:
the permission matrix, which cannot author or show the record scope ADR-0084 declares for it|ObjectAccessScopeSchema (permission.zod.ts:18) and the #16870 BASE-schema refusal (:632, rule :288–310)|every admin setting record scope in Setup or Studio AccessThread-read: 6420661163
Premise: corrected 2026-10-26T16:26Z — on
origin/maindf8aac41f2the matrix authors no scope (readScope|writeScopehit only comments :985–999; controlviewAllRecords:969); anchors moved +15–25 after #26010 (7111e14). Three corrections, ruled by the seat within ruling 6416233738 Q1:Hint: shown ALWAYS on the three hierarchy values ("requires enterprise; otherwise owner-only"), as the ruling words it. No client signal says whether hierarchy-security is licensed (0 hits in ui; discovery has no key), and a licence-conditional hint would need a new spec key, which is outside the ruling.
VA / MA vs scope (restores an invariant): granting View All clears
readScope, granting Modify All clears both scopes, in the per-checkbox cascade AND the "All" bulk arm. Today both save the pair the BASE schema refuses. Effective access is unchanged (the resolver answers org on the super-user bit, :305–308). FlipbulkMergeKeys.test.tsx:148(which pinsreadScope: 'own'beside VA+MA), and keep the CRUD/R/None pins.Position page: one read-only
PermissionPreviewper named set, in declared order, with no client-side union. This lands in PR 2, after PR 1.Serial constraints cleared: no open PR touches these files (39 checked). 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 also adds
perm.*keys toi18n.tsand is held until this PR merges.Writes: PR only via the relay op
pr_create; comments only via the relay write tools; ⛔ no GitHub MCP writes.Ruling section: 6416233738 Q1 / Q3 item 1.
Seat's route (PR 1):
ObjectPermgainsreadScope?andwriteScope?(ObjectAccessScope), plus a string-valued scope mutator.PermissionTableand Studio AccessStudioObjectRow), with values fromObjectAccessScopeSchemaand a "not set" option. Labels reuseengine.permissionPreview.*where they fit.readScopebesideviewAllRecords: true, never reads it, and emits no diagnostic — the declaration materialises and a capability census counts it as coverage #16870 pair forbids it (VA → read select, MA → both), with the reason as a title.mergePermissionSlice).Part of #26187(PR 2 = previews on the set and position pages).fix(app-shell): permission matrix sets and shows readScope / writeSco…
test(app-shell): column-header pin counts the two record-scope columns (
objectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 26187, "status": "done", "branch": "claude/issue-26187-permission-matrix-record-scope", "pr": "https://github.com/objectstack-ai/objectstack/pull/26284", "head_sha": "c696351dda66081d63844a733db2b3c841145e13", "session": "session_01SMps7wq7rBTk53e8KTSEup", "premise_still_valid": true, "summary": "PR 1 of 2 (Part of #26187; #26187 remains open for PR 2, the read-only PermissionPreview on the permission-set and position pages). Every object row in Setup PermissionTable and Studio Access StudioObjectRow gains a Read scope and a Write scope picker (shared Radix Select) listing Not set plus the five ObjectAccessScopeSchema values in spec order. The three hierarchy values always carry the hint 'requires enterprise; otherwise owner-only' and stay selectable. View All greys the read picker and Modify All greys both, each with its reason as a title. A single helper, dropScopesBypassedBySuperUserBits, drops readScope on a View All grant and both scopes on a Modify All grant, in the per-checkbox cascade AND the 'All' bulk arm. The bulkMergeKeys 'All' pins (environment door and package door) flip; CRUD, R and None stay as they were. ObjectPerm gains readScope/writeScope (spec ObjectAccessScope type), and ObjectPermFlag narrows the checkbox keys. Strings are added only in metadata-admin/i18n.ts (perm.scope.*, en + zh).", "tests": "All runs under os-verify-lock. (1) RED FIRST: PermissionMatrixEditor.tsx, permission-slice.ts and i18n.ts restored to origin/main df8aac41 in the tree only (hash-equal to the BASE blobs, 0 perm-scope- markers), then recordScope-26187 + bulkMergeKeys: 'Tests 17 failed | 5 passed (22)'. Every recordScope behaviour pin and both 'All' pins failed. The 5 green are expected: the BASE-schema refusal control, the 'other column leaves scopes alone' preservation pin, and CRUD/R/None. Restore proven: blob == HEAD and git diff HEAD empty. (2) ABLATION, once: scripts/ablation-replace.mjs --delete of the All-arm 'dropScopesBypassedBySuperUserBits(next);' (anchor x1 -> x0, blob b275ac54 -> d01962e8). bulkMergeKeys gave 'Tests 2 failed | 3 passed (5)': both All pins red, CRUD/R/None green. Restored: blob == HEAD, git diff HEAD empty. No dist is involved (app-shell tests import src). (3) GREEN at 93c9211e: recordScope-26187 + bulkMergeKeys + retiredLifecycleKeys gave 'Test Files 3 passed (3) / Tests 26 passed (26)'. (4) pnpm --filter @object-ui/app-shell type-check (both tsc programs) at c696351d, after turbo build --filter='@object-ui/app-shell^...' (64/64): VERDICT command-exit 0. (5) Full app-shell suite (1,740 files at --maxWorkers=2): I stopped my own run after about 50 minutes without a verdict, since it held the shared lock, so the full suite is NOT MEASURED locally. Declared narrowing: the 195 app-shell test files that import the editor, permission-slice or metadata-admin i18n, plus the Studio Access suites. At c696351d that set gave 'Test Files 195 passed (195) / Tests 1629 passed (1629)', VERDICT command-exit 0. CI Test UI 16/16 shards passed on c696351d.", "gates": "Local, at c696351d (final head): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 35 commands, and each was run with its exit code captured before any pipe. 35 exited 0. check:type-check-debt first exited 3 (PREREQUISITE NOT MET: its closure build timed out in the lock queue behind my own suite); re-run in the foreground it exited 0 (re-measure: 26 raw errors, none above the ledger). check-sdui-manifest exited 0, and scripts/sdui-manifest.* is untouched. --ran reconciliation over the coded record: '35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN' (a DERIVED zero: all 35 carry an exit code). CI check-runs on c696351d: 54 total, every one success or skipped, none pending. The eight required contexts are Lint & Repo Gates success, TypeScript Type Check success, Test Core success, Build Core skipped (path-filtered), Governed Surface Queue Guard success, Lint UI success, Type Check UI success, Build & E2E UI success. Also Test UI 1-16/16 success, Check Changeset success, Dogfood Regression Gate success.", "dogfood_battery": "n/a: no '!' in the PR title, and the Clause-2 line is 'no' with no (narrowing) arm", "line_budget": "666 changed lines (+624 / -42) over 7 files vs the merge base, per dispatch-gates. That is under the 3000 human-merge threshold. Container M.", "files_changed": [ ".changeset/26187-permission-matrix-record-scope.md", "ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx", "ui/packages/app-shell/src/views/metadata-admin/permission-slice.ts", "ui/packages/app-shell/src/views/metadata-admin/i18n.ts", "ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.recordScope-26187.test.tsx", "ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.bulkMergeKeys.test.tsx", "ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.retiredLifecycleKeys.test.tsx" ], "deviations": [ "File surface: PermissionMatrixEditor.retiredLifecycleKeys.test.tsx is not in the claim's list. It pinned the header strip as slice(1, -1), and the two new columns made that pin false. It now reads slice(1, -3) and also pins the two scope headers.", "Declared narrowing: the full app-shell suite was stopped after about 50 minutes, holding the shared lock, and replaced by the 195-file importer set (green). CI Test UI ran 16/16 green.", "PR body: written once, at open. Its Tests section says the narrowed suite was still queued and check:type-check-debt was NOT MEASURED; both later came back green (above). If the seat wants the body current, the fix is to replace the third Tests bullet with the 195/1629 result and the second Gates bullet with 'exit 0 on re-run'.", "Commit trailers use the model-free pair AGENTS.md prescribes (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By. The pre-push trailer check passed.", "Two type-check launches started by mistake: a shell quoting error left the first background job running unseen. Both were my own; the second queue-timed out (99) behind the first. No result is affected." ], "mcp_calls": "0", "api_writes": "2 relay or REST writes. (1) pr_create through scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectstack/dispatches, which the relay executed as POST /repos/objectstack-ai/objectstack/pulls (draft) plus the assignee POST on #26284; read back identical, 7621 bytes. (2) This os-dev-report comment through scripts/pm/post-stamped.mjs: POST /repos/objectstack-ai/objectstack/issues/26187/comments. git push is not counted as a REST write.", "open_questions": [ { "question": "A row stored before #16870 can hold View All + readScope (or Modify All + a scope). The matrix shows the stored value in a greyed picker and saves it unchanged, so the server refuses that save, as on origin/main. Should the load path strip the refused scope?", "options": [ "A: leave it, as now. The admin clears it by unticking the bit, choosing Not set and re-ticking. No stored data is changed without an act.", "B: strip it on load and mark the draft dirty, with a notice naming the dropped key." ], "recommendation": "A, because B deletes stored data without an admin act, and the server already refuses the pair loudly." }, { "question": "The scope values are restated as OBJECT_ACCESS_SCOPES with a type-only spec import and a parity test against ObjectAccessScopeSchema.options, rather than importing the schema value. Is that the right trade?", "options": [ "A: keep the restatement plus the parity test (the useTenancyPosture pattern, which avoids about 237 KB of bundle).", "B: import ObjectAccessScopeSchema.options from @objectstack/spec/security at runtime." ], "recommendation": "A, because the test already turns any spec drift red in CI, and B pays for a five-value list in bundle size." }, { "question": "Implementation choice: the picker is the shared Radix Select (objectui#11865), not a native select. The hint is shown inline on the three hierarchy items and as the item title, and the trigger shows the bare label (its title carries the hint when a hierarchy value is chosen). OK?", "options": [ "A: keep.", "B: show the hint inline in the trigger as well." ], "recommendation": "A, because the trigger column stays readable at 9rem." } ], "out_of_scope_findings": [ "Observation, noted and not filed: the matrix's 'Only granted' filter and the 'objects granted' stat count any row with a truthy key as granted (Object.values(perm).some(Boolean)), so a row carrying only a scope is listed. This was already true on origin/main for allowExport and for API-set scopes. Recorded in the PR's Acceptance notes. carrier: none." ] }objectstack-fleet commentedon Oct 26, 2026
Landed · seat 2 · 2026-10-26T20:10Z · PR 1 of 2
5cb1c1a6, headc696351d(+624/−42, ACCEPT on #26284)origin/main5cb1c1a6, contains it (git merge-base --is-ancestorexit 0)gh-readonly-queue/main/pr-26284-*: none left (consumed)Part of #26187)Status: every object row in Setup → Permission Sets and Studio → Access now has Read scope / Write scope pickers. View All and Modify All grey them and drop the scope the spec refuses, in the checkbox and in the All bulk arm. Still owed: PR 2, the read-only PermissionPreview on the permission-set and position pages. It shares
metadata-admin/i18n.tswith #26191 (in development), so it is dispatched once #26191 has merged.pm:dispatchedstays.objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 6 · 2026-10-26T20:12Z · PR 2 of 2 (amends the claim for PR 1, which MERGED as #26284)
Session:
session_01SMps7wq7rBTk53e8KTSEupAccount:
os-steveBranch:
claude/issue-26187-position-permission-previewWorktree:
objectstack-issue-26187bDomain:
domain:uiSeat:
domain:ui#2File surface (PR 2):
ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx(the position branch besidePositionHoldersSection), a new position-permissions section component inmetadata-admin/if one is cleaner,metadata-admin/previews/PermissionPreview.tsxonly for a read-only / embedded prop if mounting needs one,metadata-admin/i18n.ts(en + zh, new keys only), tests beside them, root.changeset/. ⛔PermissionMatrixEditor.tsx/permission-slice.ts(PR 1 is done),PermissionAdvancedFacets.tsx(#26191),builtinComponents.tsx,packages/spec, locale packs,ui/.github/**, rootci.yml.Container & model:
M,mode:subagent,model: default tier,effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
the position page, which shows nothing of the access its permission sets grant|the permission sets a position names, read through the existing metadata client|every admin reviewing a position in SetupThread-read: 6425161193
Premise: holds 2026-10-26T20:12Z at
origin/main5cb1c1a684.PermissionPreview(previews/PermissionPreview.tsx) is registered only as the metadata preview forpermission/profile(previews/index.ts), and the position page (ResourceEditPage.tsx, besidePositionHoldersSection) shows no grant or scope.Seat ruling on the permission-set page: since PR 1, the set's own page shows each object's read and write scope in its matrix pickers. That meets the card's "show the current scope" line there, so PR 2 adds no second, read-only copy to the set page.
Serial constraints cleared: #26284 (PR 1) MERGED 2026-10-26T20:10Z. #26191 (in development) also adds keys to
metadata-admin/i18n.ts; whichever PR lands second mergesmainfirst. #26187 is p1, so this PR does not wait for #26191.Writes: PR only via the relay op
pr_create; comments only via the relay write tools; ⛔ no GitHub MCP writes.PermissionPreviewper named set, in declared order, with no client-side union.PermissionPreviewper permission set the position names, in declared order, with each set's name as its heading and a link to the set's page.Fixes #26187.test(app-shell): pin the position page's read-only permission-set pre…
fix(app-shell): the Setup position page previews each permission set …
chore(changeset): app-shell patch for the position page's permission-…
test(app-shell): mount the section under the console's metadata route (…
objectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 26187, "status": "done", "branch": "claude/issue-26187-position-permission-preview", "pr": "https://github.com/objectstack-ai/objectstack/pull/26360", "head_sha": "771f48f4ba54f8c948eeab5d8019fd0c868f8f95", "session": "session_01SMps7wq7rBTk53e8KTSEup", "premise_still_valid": true, "summary": "PR 2 of 2 (Fixes #26187). On the Setup position page (environment scope), a new PositionPermissionSetsSection renders one read-only PermissionPreview per permission set the position names in permissionSets, in declared order, each under an h3 with the set's name linking to the set's page (path-relative ../../permission/NAME, scope kept). Sets are read with client.get('permission', name) through the page's existing metadata client; no client-side union and no editing (no onPatch, no controls). A set the server does not serve shows one not-found line for that set only; a refused or failed read says so with the server's sentence instead of 'not found'; a position naming no sets shows an empty state. ResourceEditPage's ENVIRONMENT_SECTIONS now maps a type to an ordered list of sections handed {name, draft}: position = [PositionHoldersSection, PositionPermissionSetsSection]. 7 new engine.position.sets.* keys in en and zh. PermissionPreview.tsx is unchanged (it is already read-only without onPatch). Per the seat ruling, the permission-set page gets no second preview.", "tests": "All under scripts/pm/os-verify-lock.sh, from cwd ui/ with vitest --maxWorkers=2. (1) RED FIRST: ResourceEditPage.tsx and i18n.ts restored to origin/main 46255b19 in the tree only (blobs equal to the main blobs, 0 engine.position.sets keys) and the new component removed. ResourceEditPage.positionPermissionSets-26187: 'Tests 1 failed | 2 passed (3)', where the positive pin fails and its two absence controls pass. PositionPermissionSetsSection.test: the module does not resolve. Restore proven: blobs == HEAD, git diff HEAD empty. (2) ABLATION, once, via scripts/ablation-replace.mjs: the failed-read arm replaced with a 'missing' state (anchor x1 -> x0, blob 83ca6312 -> 60eba45d). PositionPermissionSetsSection.test gave 'Tests 1 failed | 5 passed (6)', the refusal pin red. Restored: blob == HEAD, git diff HEAD empty. App-shell tests import src, so no dist is involved. (3) GREEN at 771f48f4, the final head, after merging origin/main with #26340: closure turbo build --filter='@object-ui/app-shell^...' exit 0. The two new files: 'Test Files 2 passed (2) / Tests 9 passed (9)'. The 291 app-shell test files that import ResourceEditPage / PermissionPreview / PositionHoldersSection / the new section / metadata-admin i18n (resolved by real import path), plus #26191's companionGrants test: 'Test Files 292 passed (292) / Tests 2726 passed (2726)', exit 0. pnpm --filter @object-ui/app-shell type-check (both tsc programs) exit 0. The full app-shell suite was NOT run locally, per the dispatch; CI Test UI runs it.", "gates": "Local, at 771f48f4 (final head): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 35 commands. Each was run with its exit captured before any pipe, and all 35 exited 0. The 34 lock-free ones ran directly; check:type-check-debt ran nested in my verify-lock hold (re-measure OK, 0 raw tsc errors, none above the ledger). An earlier lock-free attempt exited 3 PREREQUISITE NOT MET because it queued behind my own hold, and it was re-run under the lock. node scripts/check-sdui-manifest.mjs exit 0 ('present, intact (sha256 1f6e1ccb925a…, 106 components)'); app-shell has no sdui record and scripts/sdui-manifest.* is untouched. --ran reconciliation: '35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN' (a DERIVED zero). CI check-runs on #26360: in_progress at report time; not waited on, per os-dev.", "dogfood_battery": "n/a: no '!' in the PR title, and the Clause-2 line is 'no' with no (narrowing) arm", "line_budget": "495 changed lines (+489 / -6) over 6 files vs merge base e14d9ce8, per dispatch-gates. Under the 3000 threshold. Container M.", "files_changed": [ ".changeset/26187-position-permission-preview.md", "ui/packages/app-shell/src/views/metadata-admin/PositionPermissionSetsSection.tsx", "ui/packages/app-shell/src/views/metadata-admin/PositionPermissionSetsSection.test.tsx", "ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx", "ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.positionPermissionSets-26187.test.tsx", "ui/packages/app-shell/src/views/metadata-admin/i18n.ts" ], "deviations": [ "Route refinement: the seat's route names one 'not found' line for a set that does not load. A non-404 failure (a refused or failed read) gets its own line with the server's sentence, because a refused read is not an absent set. Both are pinned.", "Main merged three times with merge commits (a5557a68, c0814d80, 771f48f4). The last merge came after #26340 (#26191) landed, as the seat asked. It was a clean merge with no i18n.ts conflict, and both key sets are present (engine.position.sets.* and perm.admin.companions.*). Because the incoming commits touch the same i18n.ts, the importer set was re-run in full on 771f48f4, not only the scoped re-check.", "PR body follows the rule on main from #26327 (merged during this run): two declaration lines, a six-line 维护者速读, then What changes / Tests / Acceptance notes / Verification, with log readings in details/summary blocks. The read-back was byte-identical and all 4 details blocks survived.", "One test fix after the first green attempt: the component test mounted without a Route, so the path-relative link resolved against '/'. It now mounts under the console's metadata/:type/:name route shape. The component itself did not change.", "Lock: several queue-timeouts (exit 99, NOT MEASURED) on a deep shared queue. I re-ran each with the same OS_VERIFY_LOCK_SLOT, in a bounded loop of at most 8 tries. Two of my own queued runs (a re-check and a gate loop on c0814d80) were stopped before they acquired, because #26340 was about to land. The long holds (25 and 30 minutes) are the 291-292-file importer set, which the dispatch named.", "Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By. The pre-push trailer check passed on every push." ], "mcp_calls": "0", "api_writes": "2 relay or REST writes. (1) pr_create through scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectstack/dispatches, which the relay executed as POST /pulls (draft) plus the assignee POST on #26360 (run 40419266015). Read back identical, 10869 bytes, assignee os-steve. (2) This os-dev-report comment through scripts/pm/post-stamped.mjs: POST /repos/objectstack-ai/objectstack/issues/26187/comments. git push is not counted.", "open_questions": [ { "question": "The section reads the names from the page's draft, so an unsaved edit to permissionSets previews the edited list, as PositionPreview does. Should it read the stored definition instead?", "options": [ "A: keep the draft (live, and consistent with the position preview above it).", "B: read the saved definition only, so the section shows what holders get now." ], "recommendation": "A, because a caller without manage_metadata gets a read-only page where draft equals stored, and an author editing the list sees what the edit will grant." }, { "question": "Implementation choice: the sets section sits after the holders section, both inside the existing 35vh scrolling strip under the designer. OK?", "options": [ "A: keep the order holders, then sets.", "B: put the sets first." ], "recommendation": "A, because it leaves the existing holders card where it was." } ], "out_of_scope_findings": [] }objectstack-fleet commentedon Oct 27, 2026
Landed · seat 2 · 2026-10-27T00:05Z
77a52cfe, head771f48f4(+489/−6, seat record on #26360)origin/maingit merge-base --is-ancestorexit 0)gh-readonly-queue/main/pr-26360-*: none left (consumed)Fixes #26187on #26360; PR 1 was #26284Status: the Setup position page now shows one read-only permission preview per set the position names, in declared order, each linked to its set; a missing set shows "not found" and a refused read shows the server's sentence. The permission-set page keeps PR 1's matrix scope picker and gets no second preview (seat ruling).
pm:dispatchedremoved.