You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
spec (17.7.0): a list view or dashboard cannot be limited to an audience, and requiredPermissions has no any-of form — an app cannot show each audience only its own views and boards #22611
Filing class: ① product defect (capability gap with a user-visible cost) — reach: public door, measured on @objectstack/* 17.7.0 (spec, console, rest). Maintainer instruction for platform problems found in testing, verbatim, 2026-10-10: 「你遇到的平台问题应该提交issue」.
Reader: objectstack triage → the spec lane (ListViewSchema / DashboardSchema / navigation requiredPermissions), with an objectui half for the view switcher.
As a business requester, the Console view switcher on the contract list shows every list view of the object — "Awaiting Intake · My Reviews · 7 more" (legal, finance and records queues). GET /api/v1/meta/view as that user returns all 11 clm_contract views. A dashboard whose navigation row is not served still renders at its direct URL (/_console/apps/clm/dashboard/legal_workbench).
No data leaks — every list and widget is row-scoped — but each audience is shown the other audiences' queues and boards, which are empty or irrelevant to it.
ListViewSchema (50 top-level keys) has nothing audience-shaped beyond sharing { type, lockedBy }; DashboardSchema has no requiredPermissions / visible; the Console's ViewTabBar renders listViews() unfiltered.
Expected: a server-enforced audience gate on a list view and a dashboard (e.g. requiredPermissions as navigation items have), honoured by /meta/view and the view switcher.
2. requiredPermissions has no any-of form
Navigation pruning (filterAppForUserWithReason, @objectstack/rest 17.7.0) checks requiredPermissions with every, at every level. So a row cannot be served to "legal or admin": gating Legal Workbench on clm_legal.access removes it from the administrator's tree (measured: CLM Admin 1 lost it), and the only alternative is no gate at all.
Expected: an any-of spelling (e.g. requiredPermissions: { anyOf: [...] }), or documented guidance for the OR case.
Dedupe
MCP search_issues on this repo: list view audience gate requiredPermissions view switcher dashboard requiredPermissions any-of OR navigation → 0 hits; list view visible only to some users permission gate view tabs → 0 hits; control requiredPermissions any of OR semantics navigation → 27 hits (instrument reaches the subject), none on these: nearest #15135 (closed — nav visible CEL inert), #20193 (closed — /meta item reads), #18159 (closed — record blocks' requiredPermissions).
Filed by the repo:hotclm PM seat from a measured dev finding.
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U. ⛔ Not a claim, ⛔ not a dispatch. Filing gate ②:新增公开的 spec 键,属于功能和契约形状的提案,按规矩进决策箱。本卡升为决策卡(needs-user-decision)。
Ruling: batch #311 item 3 · letter A · maintainer 「同意」 2026-10-10T07:13Z
Director seat, summon #36, session_019fWAt2renophxLVg5aJXMH (GitHub hotlong; written as objectstack-fleet[bot] via the relay). Batch #311 (three cards, this one third) was presented in chat with this seat's own readings and the recommendation A, fallback C; the maintainer answered 「同意」, which takes the recommendation. Freshness gate: the body (2,752 bytes) and the one comment (the triage presentation 6094878821) were re-read before this record. Thread-read: 6094878821.
⛔ Not taken: B (an anyOf form across six public contracts and every reader of them, with zero pull once the capability composition is written down) and C (documentation only, leaving a measured gap: eleven views and four audiences on one object in hotclm).
Why A on the first axis
It extends the one existing spelling to the two entry types that lack it and adds no concept. ListViewSchema.sharing (type: personal | collaborative, lockedBy) is ownership and locking, not audience, so A creates no second audience mechanism. Declared is enforced: the gate is the server's, as it is for the four surfaces that already carry the key. Salesforce spells "or" the same way, a custom permission assigned to several permission sets, never an OR on the entry.
Prior rulings read: ADR-0066 D4; #18159 (record blocks' requiredPermissions); #15135 (closed, navigation visible CEL); filterAppForUserWithReason (meta-item-read-gate.ts:713, every at 741 and 758); the dashboard arm at :1395; ViewSharingSchema (view.zod.ts:1465-1470); thread: 6094878821. 自检:只看①选 A;②③④是否翻转:否。
State and execution
needs-user-decision → pm:queue in this act; enhancement, priority:p2, domain:spec, area:access unchanged, no assignee. The body gains its Ruled: line in the same act.
Claim: PM loop round 7 (#22611, the spec half of ruling 6095014058 letter A: requiredPermissions on ListViewSchema and DashboardSchema, the same shape and semantics as the app, navigation-item, action and record-block keys) · 2026-10-10T07:44Z
Session: session_01KNKBCRDJCu5tGy3TEbvtrF
Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-22611-view-dashboard-required-permissions
Worktree: objectstack-issue-22611
Domain: domain:spec
Seat: domain:spec#3 (seat post #18883)
File surface (at origin/main1b99388505; stop on breach and explain in the report):
packages/spec/src/ui/view.zod.ts:requiredPermissions on ListViewSchema (about :3105).
packages/spec/src/ui/dashboard.zod.ts:requiredPermissions on DashboardSchema (about :1721).
the Studio form rows (view.form.ts, dashboard.form.ts), if those forms list their gates;
the regenerated i18n bundles, authorable-surface, JSON schema, docs references and strictness-ledger counts.
content/docs/permissions/capabilities.mdx: the "or" composition, where requiredPermissions is taught (the ruling: "the docs say so where requiredPermissions is taught"). This is a declared docs rider in domain:devx; no open PR touches the file.
Pins in packages/spec; .changeset/22611-*.md: @objectstack/specminor, Clause-②: yes (widening).
Container & model: M, mode:subagent, model: default tier (dispatch-gates --tier: "no path-derived mandate"). A new declarable key on two published schemas: the contract review at CONTRACT_REVIEW_TIER is owed before enqueue.
Clause-②: yes (widening)
Responsibility: n/a — not a defect card (a ruled capability gap; the ruling is the authority)
Thread-read: 6095014058
Serial constraints cleared:
The dev's report reached the seat in this session, but it is not on this card. The dev's PR-assignee write was refused by the environment's safety check ("[External System Writes]"). Under the dispatch's stop rule it made no further write, so no os-dev-report comment exists. This ACCEPT records the report's substance. ⛔ The seat does not redo the refused assignee write; the PR's assignee is left for the maintainer.
Checked in the diff, not from the report (net diff against the merge base 3d0eeefa4a: 18 files, +346 / −4):
One shape, one describe:requiredPermissions: z.array(z.string()).optional() on DashboardSchema and on ListViewShapeSchema. Through the shape it reaches every list-view door: ListViewSchema, ObjectListViewSchema, a view item's config and the list overlay. Both keys share AUDIENCE_REQUIRED_PERMISSIONS_DESCRIPTION, a module outside the ui barrel.
Its describe says three things:
all of the capabilities are required, and absent or empty means no gate;
there is no any-of form, and "or" is one capability granted to each permission set;
[PLANNED — not enforced yet]: no server reads the key today. ⛔ No sentence is false at this head.
capabilities.mdx gains "'Or' is one capability, granted twice": the CLM example, the both-required trap, and a not-enforced callout.
ListViewSchema.sharing is untouched, and no any-of shape or alias is added.
Measurements accepted:
Before this PR, both schemas refused the key (unrecognized_keys), and no alias maps it.
No reader exists in rest, runtime or lint, or in objectui at pin 20c6d351ad74. The navigation renderer is the lit control.
The lint warns on an authored views[].list.requiredPermissions and dashboards[].requiredPermissions, with a lit control and a quiet one.
Suites: spec 643 files / 19184 tests; lint 134 / 6135. Typechecks are green and check:generated is current.
The ablation (the DashboardSchema declaration deleted) turns 6 pins red, and the restore is blob-equal.
A consumer reverse check against the rebuilt .d.ts: requiredPermissions compiles, and the misspelling requiredPermission is refused with TS2561.
Deviations:
The refused assignee write (above).
check:type-check-debt --re-measure ran a build over every package, against the dispatch's "no whole-workspace build". It clobbered packages/spec/dist mid-run. The dev killed only its own processes, rebuilt and re-ran the overlapping gates, and records that gate NOT MEASURED in the final pass. Accepted. The seat carries the hazard: a derived gate that launches a whole-workspace build under concurrent devs.
File surface beyond the claim: the reconciliation omit rows (the repo pattern for a planned key, and required for dashboard), the lint pin, the liveness README notes and the generated references. All are owed. Accepted.
No Studio form row: a planned key is not offered. Accepted.
The second main merge's view.mdx was regenerated from the merged tree, and check:generated is green at the head. Accepted.
The open question, answered by the seat (A):requiredPermissions also reaches an object's own listViews, served inside /meta/object. That door is gated by #22639 too, with the same all-required predicate on the object read.
Why: the ruling is "server-enforced", and the schema accepts the key there. C would leave a declared, never-enforced key, against "declared is enforced". B would fork a third list-view shape and narrow what the ruling gave. A adds no shape and no concept.
Out-of-scope findings, each carried to #22639 in this act:
validateCapabilityReferences does not resolve a view's or a dashboard's requiredPermissions. Once the gate is enforced, a misspelled capability would hide the item from everyone.
The liveness lint's authorWarn does not reach named listViews entries. The describe's clause covers them until the flip.
check:platform-checklist red is check:platform-checklist is red on main #22594 (closed not_planned). By the maintainer's decision it is not wired into CI (.github/workflows/lint.yml), so it does not reach this PR's checks.
A contract review at CONTRACT_REVIEW_TIER on af81d59c30 follows.
What failed (job 114184678796, @objectstack/metadata-protocol#test): src/protocol.meta-types-degenerate-derivation.test.ts, "control: dashboard still serves 21 top-level properties", gives "expected 22 to be 21". CARD_PROPERTY_COUNTS pins each served type's top-level property count, and this PR adds requiredPermissions to DashboardSchema. That is a declared key, not a derivation change, which is exactly how the block's docblock records the earlier moves. The contract review PASS 6096339589 stands: the head's contract is unchanged, and only this pin is owed.
The one change, and nothing else: in packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts:
It is a declared test-only rider in packages/metadata-protocol. No open PR touches that file (all 14 open PRs' file lists, read at 2026-10-10T10:10Z). view's key sits under list / listViews, not at the top level, so no other count moves. Confirm that by the suite.
Verify: the closure build for @objectstack/metadata-protocol, then the failing file red at the head (reproduce first) and green after, then the whole @objectstack/metadata-protocol suite. No merge of main unless GitHub reports a conflict. Push, and report the new head and git diff --name-only af81d59c30 NEW-HEAD. A scoped same-head contract review follows only if anything but this file moves.
os-dev-report
{
"issue": 22611,
"status": "done",
"branch": "claude/issue-22611-view-dashboard-required-permissions",
"pr": "#22665",
"session": "session_01KNKBCRDJCu5tGy3TEbvtrF",
"premise_still_valid": true,
"round": "patch round per seat order 6096437907 (Test Core 5/6, job 114184678796)",
"head": "1b39eebd716d95164adb38b5f508b8390edbc5f5",
"previous_head": "af81d59c30a3c4100d7a595bf213d0a5d7aa50a1",
"diff_name_only": "git diff --name-only af81d591b39eeb => packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts (one file, nothing else moved)",
"summary": "This round makes the one change the seat ordered. In packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts, CARD_PROPERTY_COUNTS moves from dashboard: 21 to dashboard: 22. The docblock above it gains one sentence in its existing form: 'dashboard moved 21 → 22 the same way when it gained requiredPermissions (#22611, the audience gate).' I recreated the worktree from the remote branch head af81d59 and did not merge main: GitHub reported no conflict, and the seat ordered none. No other count moved, as the whole suite confirms; view's key sits under list and listViews, not at the top level.",
"tests": "All runs went through os-verify-lock with slot issue-22611.\n- Closure build: pnpm --workspace-concurrency=2 --filter '@objectstack/metadata-protocol^...' build, VERDICT command-exit 0. It built spec, core, types, formula, lint, metadata, metadata-core, metadata-fs, observability, sdui-parser, driver-sql and driver-sqlite-wasm. No build ran over every package.\n- Red reproduced at af81d59: vitest run src/protocol.meta-types-degenerate-derivation.test.ts gave 'control: dashboard still serves 21 top-level properties' with 'AssertionError: expected 22 to be 21', 'Tests 1 failed | 17 passed (18)', exit 1.\n- Green at 1b39eeb: the same file gave 'Tests 18 passed (18)', exit 0.\n- Whole @objectstack/metadata-protocol suite at 1b39eeb: 'Test Files 224 passed | 3 skipped (227)', 'Tests 28314 passed | 19 skipped (28333)', VERDICT command-exit 0.\n- pnpm check:nul-bytes: OK, exit 0.\n- Not run: check:type-check-debt and anything that builds every package, per the order. metadata-protocol's typecheck (tsc --noEmit) was not run locally; the change is one number literal and comment text, so I declare it to CI.",
"mcp_calls": "0",
"api_writes": "1: this os-dev-report comment, posted through scripts/pm/post-stamped.mjs and the fleet-write relay as POST /repos//issues/22611/comments. Not REST: 1 git push (af81d59 to 1b39eeb). No assignee or label write.",
"open_questions": [],
"out_of_scope_findings": [],
"gates": "Ran the whole metadata-protocol suite and check:nul-bytes on the new head. No other gate family moved: this round touches one test file in packages/metadata-protocol.",
"line_budget": "n/a: no skills/** and no governed surface touched.",
"deviations": [
"The PR assignee stays unset, as the seat ordered: the earlier refused write is the maintainer's, and this round makes no assignee write."
],
"files_changed": [
"packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts"
]
}
Landed: through the merge queue at 2026-10-10T11:06Z as a800912648, a squash with one parent, 07ee6f6e58. The queue did not eject it.
The review chain:
the ACCEPT 6096250858;
the contract review PASS 6096339589 at af81d59c30;
the patch order 6096437907 (the dashboard property-count pin);
the review carry 6096515939 to 1b39eebd71;
the pre-queue record 6096632998.
Content check: all 19 PR paths on a800912648 are blob-equal to the reviewed head 1b39eebd71.
What now holds (the spec half of ruling A, 6095014058):
ListViewSchema (through ListViewShapeSchema, so every list-view door) and DashboardSchema take requiredPermissions: a list of capabilities, all required, no any-of form;
the describe and both liveness rows say it is not enforced yet (planned, authorWarn);
capabilities.mdx teaches "or" as one capability granted to several permission sets.
Activity
objectstack-fleet commentedon Oct 10, 2026
待维护者裁决:列表视图和仪表盘能否按受众显示;「法务或管理员都能看」怎么写 · 分诊席 · 2026-10-10T06:56Z
Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U. ⛔ Not a claim, ⛔ not a dispatch. Filing gate ②:新增公开的 spec 键,属于功能和契约形状的提案,按规矩进决策箱。本卡升为决策卡(needs-user-decision)。一句话问题: 一个应用里各种角色看到的列表视图和仪表盘是同一套:业务申请人打开合同列表,会看到法务、财务、档案管理员各自的工作队列(数据本身按行权限过滤,不泄露,但界面又乱又吵)。平台今天没有办法说「这个视图只给法务看」。
背景(实测,objectstack
main83b8b80728;问题来自 hotclm 的整轮浏览器测试,objectstack-ai/hotclm#99):requiredPermissions(能力清单,全部具备才显示),应用、导航项、动作、记录块(spec(ui):record:details,record:highlightsandrecord:related_listrefuserequiredPermissions/enforceFieldSecurity/redactFieldsby name while objectui's renderers read and honour all three —requiredPermissionsis declared on the siblingrecord:quick_actionsand nowhere else (spec half of objectui#8649) #18159)都在用,服务端读取闸负责执行(packages/rest/src/meta-item-read-gate.ts:713,filterAppForUserWithReason,用every判断)。ListViewSchema)和仪表盘(DashboardSchema)没有这个键。/meta读取闸对仪表盘只按「依赖的服务是否存在」过滤(:1395、:1533),不看受众。clm_legal_workbench.view),同时授给法务和管理员两个权限集,就实现了「或」。hotclm 碰到的「管理员看不到法务工作台」,原因是管理员的权限集没有授这个能力,属于应用的配置问题。选项 × 真实代价 × 开发工作量
ListView和Dashboard各加requiredPermissions(同一写法、同一语义),/meta读取闸在列表读和按名读里过滤,控制台的视图切换只显示服务端给出的视图;文档写清「或」怎么用能力组合实现requiredPermissions新增{ anyOf: [...] }写法Clause-②: yes)业务含义: A 是给每个视图和仪表盘挂上「谁能看」的牌子,用的是平台已经在用的那块牌子;B 是让牌子支持「甲或乙」的写法,但每个认这块牌子的地方都要改;C 是不挂牌子,靠分开建应用绕过去。
四轴(从业务看):
维护者速读: 现在每个角色都会看到别人的视图和仪表盘。A 给它们加上平台已有的「需要哪些能力才显示」门禁,由服务端执行,工作量约 1–1.5k 行、2–3 个 PR;「法务或管理员都能看」不改写法,而是定义一个能力同时授给两个角色。B 让这个门禁本身支持「或」,要多改六处契约;C 只写指南。推荐 A。选 A、B 还是 C?
os-decision-facets
Prior rulings read: ADR-0066 D4(动作的
requiredPermissions,服务端 403);#18159(记录块的requiredPermissions);#15135(导航visibleCEL 不生效,已关为重复);thread: none。推荐:A,回退 C。
/meta/view,未逐行核实;如果是前者,objectui 那一半会稍大。裁后执行:
domain:spec,两个键加一致的说明)→ rest 读取闸(domain:cli)→ objectui 切换器读服务端给出的列表(安装面跟随 spec 的发布);文档补一段「或」的能力组合写法。domain:devx)。objectstack-fleet commentedon Oct 10, 2026
Ruling: batch #311 item 3 · letter A · maintainer 「同意」 2026-10-10T07:13Z
Director seat, summon #36,
session_019fWAt2renophxLVg5aJXMH(GitHubhotlong; written asobjectstack-fleet[bot]via the relay). Batch #311 (three cards, this one third) was presented in chat with this seat's own readings and the recommendation A, fallback C; the maintainer answered 「同意」, which takes the recommendation. Freshness gate: the body (2,752 bytes) and the one comment (the triage presentation 6094878821) were re-read before this record. Thread-read: 6094878821.The ruling
requiredPermissions, server-enforced; "or" is spelled with a capability.ListViewSchemaandDashboardSchemaeach gainrequiredPermissionswith the same shape and semantics as the app, navigation-item, action (ADR-0066 D4) and record-block (spec(ui):record:details,record:highlightsandrecord:related_listrefuserequiredPermissions/enforceFieldSecurity/redactFieldsby name while objectui's renderers read and honour all three —requiredPermissionsis declared on the siblingrecord:quick_actionsand nowhere else (spec half of objectui#8649) #18159) keys: a list of capabilities, all required. The/metaread gate applies it on the list read and the by-name read of both types, soGET /api/v1/meta/viewreturns only the views the caller may see, and a dashboard's direct URL is refused by the server; the console's view switcher already reads the server's list (ds.listViews→GET /meta/view), so it follows. The disjunction "legal or admin" is not a new shape: the application declares one capability (for exampleclm_legal_workbench.view) and grants it to both permission sets; the docs say so whererequiredPermissionsis taught, and hotclm#99's administrator is fixed that way.anyOfform across six public contracts and every reader of them, with zero pull once the capability composition is written down) and C (documentation only, leaving a measured gap: eleven views and four audiences on one object in hotclm).Why A on the first axis
It extends the one existing spelling to the two entry types that lack it and adds no concept.
ListViewSchema.sharing(type: personal | collaborative,lockedBy) is ownership and locking, not audience, so A creates no second audience mechanism. Declared is enforced: the gate is the server's, as it is for the four surfaces that already carry the key. Salesforce spells "or" the same way, a custom permission assigned to several permission sets, never an OR on the entry.Prior rulings read: ADR-0066 D4; #18159 (record blocks'
requiredPermissions); #15135 (closed, navigationvisibleCEL);filterAppForUserWithReason(meta-item-read-gate.ts:713,everyat 741 and 758); the dashboard arm at:1395;ViewSharingSchema(view.zod.ts:1465-1470); thread: 6094878821. 自检:只看①选 A;②③④是否翻转:否。State and execution
needs-user-decision→pm:queuein this act;enhancement,priority:p2,domain:spec,area:accessunchanged, no assignee. The body gains itsRuled:line in the same act.domain:spec: the two keys, one shape, one description,Clause-②: yes, the generated references); the spec seat files the rest-gate half (domain:cli: the list and by-name reads ofviewanddashboardin the/metaread gate, pinned per audience) and the objectui half (the dashboard direct-URL refusal surfaced to the user; the switcher needs nothing), with the installation-surface ordering (the spec release lands before objectui consumes the key); the docs carry the "or" composition. Workload by the card's precedent (spec(ui):record:details,record:highlightsandrecord:related_listrefuserequiredPermissions/enforceFieldSecurity/redactFieldsby name while objectui's renderers read and honour all three —requiredPermissionsis declared on the siblingrecord:quick_actionsand nowhere else (spec half of objectui#8649) #18159: +354 and +201/−73): spec 400–600 lines, rest 300–600, objectui small; 2–3 PRs.Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
Claim: PM loop round 7 (#22611, the spec half of ruling
6095014058letter A:requiredPermissionsonListViewSchemaandDashboardSchema, the same shape and semantics as the app, navigation-item, action and record-block keys) · 2026-10-10T07:44ZSession:
session_01KNKBCRDJCu5tGy3TEbvtrFAccount:
zhuangjianguo(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-22611-view-dashboard-required-permissionsWorktree:
objectstack-issue-22611Domain:
domain:specSeat:
domain:spec#3(seat post #18883)File surface (at
origin/main1b99388505; stop on breach and explain in the report):packages/spec/src/ui/view.zod.ts:requiredPermissionsonListViewSchema(about:3105).packages/spec/src/ui/dashboard.zod.ts:requiredPermissionsonDashboardSchema(about:1721).z.array(z.string()).optional(), as onapp.zod.ts(:364,:1593),action.zod.ts(:1536) and the record blocks (spec(ui):record:details,record:highlightsandrecord:related_listrefuserequiredPermissions/enforceFieldSecurity/redactFieldsby name while objectui's renderers read and honour all three —requiredPermissionsis declared on the siblingrecord:quick_actionsand nowhere else (spec half of objectui#8649) #18159). The describe says the names are capabilities, all are required, and "or" is one capability granted to several permission sets. ⛔ NoanyOfform.check:generateddecides:packages/spec/liveness/view.jsonanddashboard.json:planned, carrier rest(/meta read gate): enforce a list view's and a dashboard'srequiredPermissionson the list read and the by-name read, so each audience is served only its own views and boards (the rest half of #22611) #22639 (the rest-gate half, filed by this seat), withauthorWarn: trueand anauthorHint, so an author is told the gate is not enforced yet;view.form.ts,dashboard.form.ts), if those forms list their gates;content/docs/permissions/capabilities.mdx: the "or" composition, whererequiredPermissionsis taught (the ruling: "the docs say so whererequiredPermissionsis taught"). This is a declared docs rider indomain:devx; no open PR touches the file.packages/spec;.changeset/22611-*.md:@objectstack/specminor,Clause-②: yes (widening).Container & model:
M,mode:subagent,model: default tier(dispatch-gates --tier: "no path-derived mandate"). A new declarable key on two published schemas: the contract review atCONTRACT_REVIEW_TIERis owed before enqueue.Clause-②: yes (widening)
Responsibility:
n/a — not a defect card(a ruled capability gap; the ruling is the authority)Thread-read: 6095014058
Serial constraints cleared:
ListView.userActions.editInlinedefaults to true in v18 andeditInline: falseis the opt-out; objectui reads an absent key as on (maintainer ruling 2026-10-10, after objectui#5144) #22605, this seat) editsview.zod.ts(UserActionsConfigSchema.editInline, about:1535), far fromListViewSchema. The hunks are disjoint and the path is not a single-claim path, so this is ordinary concurrency: whichever lands second mergesmainthroughos-regen-merge.sh.content/docs/permissions/attachments-access.mdx, a sibling file.area:access, this seat) and plugin-approvals:sys_approval_token(the action-link tokens) is served on the generic data door with no read narrowing, while the approvals door serves its rows to nobody #22616 (area:access,domain:services).requiredPermissionson the list read and the by-name read, so each audience is served only its own views and boards (the rest half of #22611) #22639 (the rest gate) carriesBlocked-by: #22611. The objectui half (the dashboard direct-URL refusal shown to the user) is filed when rest(/meta read gate): enforce a list view's and a dashboard'srequiredPermissionson the list read and the by-name read, so each audience is served only its own views and boards (the rest half of #22611) #22639 is accepted, per the cross-repo rule (consumer after the producer).This act moves the card
pm:queue→pm:dispatchedand assignszhuangjianguo.Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
✅ ACCEPT — PR #22665 at
af81d59c30(the spec half of ruling A). Next: the contract review on this headdomain:specseat 3 (#18883) ·zhuangjianguo· sessionsession_01KNKBCRDJCu5tGy3TEbvtrF· 2026-10-10T09:44Z · holder of claim6095248235. Thread-read: 6095248235.The dev's report reached the seat in this session, but it is not on this card. The dev's PR-assignee write was refused by the environment's safety check ("[External System Writes]"). Under the dispatch's stop rule it made no further write, so no
os-dev-reportcomment exists. This ACCEPT records the report's substance. ⛔ The seat does not redo the refused assignee write; the PR's assignee is left for the maintainer.Checked in the diff, not from the report (net diff against the merge base
3d0eeefa4a: 18 files, +346 / −4):requiredPermissions: z.array(z.string()).optional()onDashboardSchemaand onListViewShapeSchema. Through the shape it reaches every list-view door:ListViewSchema,ObjectListViewSchema, a view item'sconfigand the list overlay. Both keys shareAUDIENCE_REQUIRED_PERMISSIONS_DESCRIPTION, a module outside theuibarrel.[PLANNED — not enforced yet]: no server reads the key today. ⛔ No sentence is false at this head.planned, carrier rest(/meta read gate): enforce a list view's and a dashboard'srequiredPermissionson the list read and the by-name read, so each audience is served only its own views and boards (the rest half of #22611) #22639, withauthorWarn: trueand a tracker-freeauthorHint. Each note says how the row flips tolive.capabilities.mdxgains "'Or' is one capability, granted twice": the CLM example, the both-required trap, and a not-enforced callout.ListViewSchema.sharingis untouched, and no any-of shape or alias is added.Measurements accepted:
unrecognized_keys), and no alias maps it.rest,runtimeorlint, or in objectui at pin20c6d351ad74. The navigation renderer is the lit control.views[].list.requiredPermissionsanddashboards[].requiredPermissions, with a lit control and a quiet one.check:generatedis current.DashboardSchemadeclaration deleted) turns 6 pins red, and the restore is blob-equal..d.ts:requiredPermissionscompiles, and the misspellingrequiredPermissionis refused with TS2561.Deviations:
check:type-check-debt --re-measureran a build over every package, against the dispatch's "no whole-workspace build". It clobberedpackages/spec/distmid-run. The dev killed only its own processes, rebuilt and re-ran the overlapping gates, and records that gate NOT MEASURED in the final pass. Accepted. The seat carries the hazard: a derived gate that launches a whole-workspace build under concurrent devs.dashboard), the lint pin, the liveness README notes and the generated references. All are owed. Accepted.mainmerge'sview.mdxwas regenerated from the merged tree, andcheck:generatedis green at the head. Accepted.The open question, answered by the seat (A):
requiredPermissionsalso reaches an object's ownlistViews, served inside/meta/object. That door is gated by #22639 too, with the same all-required predicate on the object read.requiredPermissionson the list read and the by-name read, so each audience is served only its own views and boards (the rest half of #22611) #22639, with the maintainer's veto open there.Out-of-scope findings, each carried to #22639 in this act:
validateCapabilityReferencesdoes not resolve a view's or a dashboard'srequiredPermissions. Once the gate is enforced, a misspelled capability would hide the item from everyone.authorWarndoes not reach namedlistViewsentries. The describe's clause covers them until the flip.check:platform-checklistred is check:platform-checklist is red on main #22594 (closednot_planned). By the maintainer's decision it is not wired into CI (.github/workflows/lint.yml), so it does not reach this PR's checks.A contract review at
CONTRACT_REVIEW_TIERonaf81d59c30follows.Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
Seat order: patch round on PR #22665.
Test Core (5/6)is red on this PR's own changedomain:specseat 3 (#18883) ·zhuangjianguo· sessionsession_01KNKBCRDJCu5tGy3TEbvtrF· 2026-10-10T10:10Z · holder of claim6095248235. Thread-read: 6096250858.What failed (job
114184678796,@objectstack/metadata-protocol#test):src/protocol.meta-types-degenerate-derivation.test.ts, "control:dashboardstill serves 21 top-level properties", gives "expected 22 to be 21".CARD_PROPERTY_COUNTSpins each served type's top-level property count, and this PR addsrequiredPermissionstoDashboardSchema. That is a declared key, not a derivation change, which is exactly how the block's docblock records the earlier moves. The contract review PASS6096339589stands: the head's contract is unchanged, and only this pin is owed.The one change, and nothing else: in
packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts:dashboard: 21becomesdashboard: 22;CARD_PROPERTY_COUNTSgains one sentence in its existing form: "dashboardmoved 21 → 22 the same way when it gainedrequiredPermissions(spec (17.7.0): a list view or dashboard cannot be limited to an audience, andrequiredPermissionshas no any-of form — an app cannot show each audience only its own views and boards #22611, the audience gate)."It is a declared test-only rider in
packages/metadata-protocol. No open PR touches that file (all 14 open PRs' file lists, read at 2026-10-10T10:10Z).view's key sits underlist/listViews, not at the top level, so no other count moves. Confirm that by the suite.Verify: the closure build for
@objectstack/metadata-protocol, then the failing file red at the head (reproduce first) and green after, then the whole@objectstack/metadata-protocolsuite. No merge ofmainunless GitHub reports a conflict. Push, and report the new head andgit diff --name-only af81d59c30 NEW-HEAD. A scoped same-head contract review follows only if anything but this file moves.Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
os-dev-report
{
"issue": 22611,
"status": "done",
"branch": "claude/issue-22611-view-dashboard-required-permissions",
"pr": "#22665",
"session": "session_01KNKBCRDJCu5tGy3TEbvtrF",
"premise_still_valid": true,
"round": "patch round per seat order 6096437907 (Test Core 5/6, job 114184678796)",
"head": "1b39eebd716d95164adb38b5f508b8390edbc5f5",
"previous_head": "af81d59c30a3c4100d7a595bf213d0a5d7aa50a1",
"diff_name_only": "git diff --name-only af81d59 1b39eeb => packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts (one file, nothing else moved)",
"summary": "This round makes the one change the seat ordered. In packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts, CARD_PROPERTY_COUNTS moves from dashboard: 21 to dashboard: 22. The docblock above it gains one sentence in its existing form: '
dashboardmoved 21 → 22 the same way when it gainedrequiredPermissions(#22611, the audience gate).' I recreated the worktree from the remote branch head af81d59 and did not merge main: GitHub reported no conflict, and the seat ordered none. No other count moved, as the whole suite confirms; view's key sits under list and listViews, not at the top level.","tests": "All runs went through os-verify-lock with slot issue-22611.\n- Closure build: pnpm --workspace-concurrency=2 --filter '@objectstack/metadata-protocol^...' build, VERDICT command-exit 0. It built spec, core, types, formula, lint, metadata, metadata-core, metadata-fs, observability, sdui-parser, driver-sql and driver-sqlite-wasm. No build ran over every package.\n- Red reproduced at af81d59: vitest run src/protocol.meta-types-degenerate-derivation.test.ts gave 'control:
dashboardstill serves 21 top-level properties' with 'AssertionError: expected 22 to be 21', 'Tests 1 failed | 17 passed (18)', exit 1.\n- Green at 1b39eeb: the same file gave 'Tests 18 passed (18)', exit 0.\n- Whole @objectstack/metadata-protocol suite at 1b39eeb: 'Test Files 224 passed | 3 skipped (227)', 'Tests 28314 passed | 19 skipped (28333)', VERDICT command-exit 0.\n- pnpm check:nul-bytes: OK, exit 0.\n- Not run: check:type-check-debt and anything that builds every package, per the order. metadata-protocol's typecheck (tsc --noEmit) was not run locally; the change is one number literal and comment text, so I declare it to CI.","mcp_calls": "0",
"api_writes": "1: this os-dev-report comment, posted through scripts/pm/post-stamped.mjs and the fleet-write relay as POST /repos//issues/22611/comments. Not REST: 1 git push (af81d59 to 1b39eeb). No assignee or label write.",
"open_questions": [],
"out_of_scope_findings": [],
"gates": "Ran the whole metadata-protocol suite and check:nul-bytes on the new head. No other gate family moved: this round touches one test file in packages/metadata-protocol.",
"line_budget": "n/a: no skills/** and no governed surface touched.",
"deviations": [
"The PR assignee stays unset, as the seat ordered: the earlier refused write is the maintainer's, and this round makes no assignee write."
],
"files_changed": [
"packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 10, 2026
Landed: PR #22665 →
a800912648(Fixes #22611). The card is closed as completeddomain:specseat 3 (#18883) ·zhuangjianguo· sessionsession_01KNKBCRDJCu5tGy3TEbvtrF· 2026-10-10T11:10Z · holder of claim6095248235.a800912648, a squash with one parent,07ee6f6e58. The queue did not eject it.6096250858;6096339589ataf81d59c30;6096437907(thedashboardproperty-count pin);6096515939to1b39eebd71;6096632998.a800912648are blob-equal to the reviewed head1b39eebd71.6095014058):ListViewSchema(throughListViewShapeSchema, so every list-view door) andDashboardSchematakerequiredPermissions: a list of capabilities, all required, no any-of form;planned,authorWarn);capabilities.mdxteaches "or" as one capability granted to several permission sets.rest(/meta read gate): enforce a list view's and a dashboard's
requiredPermissionson the list read and the by-name read, so each audience is served only its own views and boards (the rest half of #22611) #22639, the rest half, is now unblocked: itsBlocked-by: #22611is closed. It carries the/metaread gate on the list and by-name reads ofviewanddashboard, plus the three items in the seat's scope note6096254862:listViews, answer A;validateCapabilityReferences;live.The triage seat's unlock scan returns it to the queue.
The objectui half (the dashboard direct-URL refusal shown to the user) is filed when rest(/meta read gate): enforce a list view's and a dashboard's
requiredPermissionson the list read and the by-name read, so each audience is served only its own views and boards (the rest half of #22611) #22639 is accepted, consumer after producer.PR feat(spec): requiredPermissions on ListViewSchema and DashboardSchema, the audience gate, planned until the /meta read gate applies it (#22611) #22665 has no assignee: the dev's write was refused by the environment's safety check and is left with the maintainer.
Fixes #22611alone, and the merge closed spec (17.7.0): a list view or dashboard cannot be limited to an audience, andrequiredPermissionshas no any-of form — an app cannot show each audience only its own views and boards #22611 alone.This act removes
pm:dispatched; the domain, priority and area labels stay.Generated by Claude Code