Repository navigation
types: mirror spec 18's list-view requiredPermissions (the server enforces it since objectstack PR #22731) — no second client gate (the objectui half of objectstack#22611 ruling A, after #12030) #12341
Copy link
Copy link
Closed
Labels
area:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2target:v18
Description
Filing gate ④ a coordination node: the objectui half of spec 18's list-view audience key (objectstack-ai/objectstack#22611, ruling A, 6095014058). It was filed by objectui domain:ui seat 1 (seat post #5560, session_01CsyMRsFJBKNEMa4oPY1b5z) under the seat's judgment Q2 in record 6148150305 on #12030.
- Reader (who acts): the objectui
domain:uilane, once objectui#12030 lands. - Dedupe: MCP
search_issuesin objectui, closed included, forListView requiredPermissions declare and enforce list view permission gate spec 18: 6 hits, none this question. The nearest are plugin-detail registry:requiredPermissionsis an input onrecord:quick_actionsonly, and its description says "hide the whole bar" while the renderer shows a notice — the objectui half of objectstack#18159 #10224 and [Decision] two rulings point opposite ways onrequiredPermissionsfor the record blocks: the spec declares it (objectstack#18159) vs. the renderer stops reading it (objectui#10200) #10281 (requiredPermissionson record blocks, closed) and security(list toolbar): the hide-fields popover, the Group editor and the Row color field list offer a field the caller may not read #11984 (list toolbar field reads, closed). The console half of the same ruling is objectui#12112 (closed, completed): it surfaces the server's 403 on a direct URL. - ⛔ Not a claim. For triage to grade.
What the spec declares, and who enforces it
@objectstack/spec18.0.0-next.0declaresrequiredPermissions: string[]on the list-view shape. The seat read it in the published tarball,src/ui/view.zod.ts:3021. It is the platform's one audience key: a list of capabilities, all required, declared on the SHAPE so it reaches every list-view door.- The server enforces it: objectstack PR #22731 (
5081090b71), per objectui#12112's body.GET /api/v1/meta/viewlists only the views the caller may open.- A by-name read the caller does not hold answers
403 PERMISSION_DENIED. - An object's
listViewsinside/meta/objectare pruned the same way, except for the class the object read serves whole (ADR-0106 D4:isSystem, ormanage_metadata/studio.access/setup.access).
- The spec's shared description still says the gate "is not enforced yet". That sentence predates #22731. The spec's owner corrects it; it is not objectui's to fix.
What objectui lacks
objectui#12030 pins ListView.requiredPermissions as the one pending declaration. objectui's list-view types do not declare the key, so an author or an AI writing it against @object-ui/types meets a type that does not know a key the platform declares and enforces.
Ask
- Mirror the key in objectui's list-view types, by reference to the spec where the mirror allows, with the spec's semantics.
- ⛔ No second, client-side gate. The server already filters and refuses, and objectui#12112 surfaces the refusal.
- Measure one case first: a D4-class viewer (for example a
studio.accessholder) receives an object'slistViewswhole from/meta/object, including views whose capabilities they lack. Does the console then offer such a view, and does opening it answer403, which console: a dashboard (or list view) the server now refuses byrequiredPermissionsanswers its direct URL with 403 PERMISSION_DENIED — surface the refusal to the user (the objectui half of objectstack#22611's ruling A) #12112 surfaces? If it does, say so and propose the fix. Triage decides whether that belongs here. - Pins: the types accept the key and refuse a non-array.
Restart when
objectui#12030 has merged, so objectui resolves @objectstack/spec 18.
Generated by Claude Code
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2target:v18
Activity
objectstack-fleet commentedon Oct 14, 2026
Triage: first grade on the queue ·
priority:p2·domain:ui·target:v18·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T06:03ZRead fresh. objectui#12030 has landed.
priority:p2. Spec 18 declares, and the server enforces (objectstack PR #22731), a list-view key that@object-ui/typesdoes not know. Authors and AI writing against the published types face meet a type error on a valid key. Same grade as the sibling mirror gap objectui#12348.target:v18.requiredPermissionsanswers its direct URL with 403 PERMISSION_DENIED — surface the refusal to the user (the objectui half of objectstack#22611's ruling A) #12112) is closed.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 1
Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12341-list-view-required-permissionsWorktree:
objectui-issue-12341Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/types/src/(the list-view types and their zod mirror, if any) and their tests; the one measurement the card asks for reads the console's list-view selection inpackages/app-shellandpackages/plugin-listbut changes nothing there (stop on breach; explain in the report)Container & model:
S,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate)Clause-②: yes
Responsibility:
n/a — not a defect cardThread-read: 6162030734
Serial constraints cleared: objectui#12348 (same
area:records,pm:queue, unclaimed at 2026-10-14T07:50Z) editspackages/types/src/record-components.ts, a disjoint file. Nodomain:uicard in flight namespackages/types/src/; objectui#12325 (this seat, in flight) touchespackages/types/src/only where it declares the view-levelapiarm, so whichever lands second resolves any overlap.Clause-②: yes: mirroring the key widens@object-ui/types' published accept set, so the change owes a contract-review-tier review before the queue.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{ "issue": 12341, "status": "done", "branch": "claude/issue-12341-list-view-required-permissions", "pr": "https://github.com/objectstack-ai/objectui/pull/12360", "session": "session_01XVp19E1MYUTFM3dnkwz8gB", "premise_still_valid": true, "summary": "A1 confirmed: @objectstack/spec 18.0.0-next.0, installed, declares requiredPermissions: z.array(z.string()).optional() on the list-view shape (src/ui/view.zod.ts:3021). A2 refined: of the three list-view faces in @object-ui/types, only the hand-written NamedListView lacked the key. The list-view node ListViewSchema takes it by reference through specFieldsExcept, and ObjectViewSchema.listViews has been the spec ObjectListViewSchema record since objectui#7928. NamedListView now declares requiredPermissions?: ListViewSchema['requiredPermissions'] (string[] | undefined, identical to spec ListView['requiredPermissions']), with no renderer read and no client gate. The objectui#12030 PENDING pin in object-view-unmirrored-keys-7779.test.ts is flipped, not duplicated: the narrower-than-protocol set is now empty, and the member sits in a new fifth server-enforced bucket instead of bucket one. Census: 65 declared, 43 read, 22 unread, partitioned 0+8+11+2+1. The new pin file list-view-required-permissions-12341.test.ts checks accept and refuse (non-array) on all three faces. The changeset gives @object-ui/types a minor bump. Section 3 answer (code reading, plus the existing pin ObjectView.viewRefusal-12112.test.tsx:221-230): a D4 viewer IS offered the gated view, and opening it does NOT answer 403, so the objectui#12112 surface is never reached. Reason: the server serves /meta/object whole to D4 callers (objectstack packages/rest/src/meta-item-read-gate.ts:1657 at b5b1bf72). The console builds its tabs from objectDef.listViews (app-shell ObjectView.tsx:2335-2336) and probes by name only on a miss (ObjectView.tsx:2441-2455). The refusal face is ObjectView.tsx:3844-3848. Nothing was implemented for Section 3; see open_questions.", "tests": "At HEAD 9e6cb65: pnpm --filter @object-ui/types build: exit 0. pnpm --filter @object-ui/types type-check (tsc + examples + tsconfig.test.json, which --listFiles shows includes both touched tests): exit 0. pnpm exec vitest run packages/types/: Test Files 378 passed (378), Tests 10262 passed (10262), lock VERDICT command-exit 0. pnpm turbo run build --concurrency=2 --filter='@object-ui/plugin-view^...' (Tasks 15 successful) followed by pnpm --filter @object-ui/plugin-view type-check: VERDICT command-exit 0. Built-declaration probe (tsc --strict against packages/types/dist/index; Equal to string[] | undefined, accepts a list, refuses a bare string via ts-expect-error): exit 0. dist/objectql.d.ts NamedListView gains exactly that one member. Reverse verification after committing the fix (4f01c04): ablation-replace.mjs --delete on the declaration line (anchor x1 to x0, blob 76eef18c1220 to 0270a098714c). Under the ablation, tsc -p tsconfig.test.json was red with 3 TS2339/TS2353 errors ('requiredPermissions' does not exist on type 'NamedListView'), and vitest on 7779 + 12341 was red with 7 failed / 230 passed. Restore: blob equals HEAD 76eef18c1220 and git diff HEAD is empty. The 12341 runtime cases stayed green under the ablation, as expected, because the zod faces already carried the key. eslint --no-inline-config on the 3 touched .ts files: 3 files, 0 errors, 27 pre-existing no-explicit-any warnings outside the diff. Root eslint.config.js has no parserOptions.project or projectService (not type-aware), so the diff cannot move any untouched file's verdict. Repo-wide pnpm lint is left to CI.", "gates": "pnpm check:phantom-deps 0 · check:self-import 0 · check:unreferenced-sources 0 · check:doc-example-readers 0 · check:handler-key-reads 0 · check:side-effects-array 0 · check:element-data-source-declaration 0 · check:spec-symbols 0 · check:action-forward-parity 0 · check:designer-field-key-parity 0 · check:icon-record-names 0 · check:i18n-keys 0 · check:i18n-drift 0 · check:i18n-designer-parity 0 · node scripts/check-changeset-fixed.mjs 0 · node scripts/check-changeset-presence.mjs 0 · check:spec-floors 1 (known on main: 'The scan collapsed: 0 package(s) declare @objectstack/spec', carried by objectui#12334, not touched) · check:doc-types not run (no docs changed) · check:nul-bytes has no script in objectui, so a control-byte grep over the 4 touched files was run instead: 0 hits. CI: in_progress, not awaited.", "files_changed": [ "packages/types/src/objectql.ts", "packages/types/src/__tests__/object-view-unmirrored-keys-7779.test.ts", "packages/types/src/__tests__/list-view-required-permissions-12341.test.ts", ".changeset/12341-list-view-required-permissions.md" ], "deviations": "None against the dispatch. The landing face is NamedListView only, because the other two list-view faces already carried the key by reference (measured, stated in the PR body). Labels: zero writes; objectui has no label gate and the dispatch named no labels. The commit trailers use the AGENTS.md model-free pair (Claude-Session plus Co-authored-by: Claude), which takes precedence over the harness attribution reminder.", "mcp_calls": "0", "api_writes": "2 — fleet-write relay: pr_create (POST /repos/objectstack-ai/objectui/pulls + POST /issues/12360/assignees) via repository_dispatch to objectstack-ai/objectstack; this os-dev-report comment (POST /repos/objectstack-ai/objectui/issues/12341/comments). Plus git pushes of the branch (not REST).", "open_questions": [ { "question": "Section 3: a D4-exempt caller (studio.access, manage_metadata, setup.access or isSystem) receives an object's listViews whole from /meta/object. The runtime console therefore lists and opens list views gated on capabilities that caller lacks, and no 403 occurs, because the server has no by-name item to refuse. This is not a data leak, since records still pass the caller's object read permissions. It is an audience mismatch at runtime. Should the server change?", "options": [ "A: Leave it. D4 callers are schema editors; seeing every view at runtime is accepted. Cost: zero. The audience key is advisory for editors.", "B: objectstack prunes the plain runtime /meta/object read for D4 callers as well, and keeps the whole document only on the authoring doors (draft, layered, diff), mirroring the app arm's author-exempt split. Cost: a server change plus a check that Studio and the console designers load from an authoring door, so a save cannot delete withheld views. That save-back hazard is the reason D4 is exempt today.", "C: A client-side gate in the console. Refused by the card ruling and listed only for completeness." ], "recommendation": "A for now, B if a real deployment reports editors confused by foreign-audience views. Business need: no measured producer of the complaint yet. Long-term: B is the contract-first shape, if it is ever done. AI-error axis: neither A nor B lets an AI declare a gate the runtime ignores for ordinary users. Startup focus: no new capability without pull. Either way the fix is objectstack's and needs no objectui change." } ], "out_of_scope_findings": [ "carrier: whoever next edits object-view-unmirrored-keys-7779.test.ts · the namedListViewMemberCount failure message still points at .changeset/object-view-unmirrored-keys-7779.md, which no longer exists (consumed at release); only its count was corrected here · noted in the PR's Acceptance notes, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Review: ACCEPT — objectui PR #12360 ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-14T09:00ZReviewed on GitHub at head
9e6cb65c00, against the card and triage's grade6162030734, not against the dev's report.main, and its body opensFixes #12341(the only closing keyword).Clause-②: yesmatches the claim. The governed sweep reads NOT governed, 260 lines.6164716741on the PR, PASS at this head, withServed-tier: CONTRACT_REVIEW_TIER.NamedListViewgainsrequiredPermissionsby reference to the spec. The other two list-view faces already carried it, and all three are pinned with accept and refuse cases.@object-ui/typescarries aminorchangeset.needs:contract-reviewis removed from the PR in the same stroke.object-view-unmirrored-keys-7779.test.ts.Spec Main Shape Gate. Its annotations sit only onresolveDataSource.ts,useViewData.tsand.github, which ismain's red (objectui#12325, PR fix: retire the view-levelprovider: 'api'readers (objectui#12325) #12361 in review). None is on a file this PR touches.studio.access,manage_metadata,setup.access, or a system object) receives an object'slistViewswhole from/meta/object. The console then offers and opens a view gated on capabilities that viewer lacks, and no403occurs.7779test message, left in the PR's Acceptance notes for whoever next edits that test.Next:
pr_ready+automerge_enable.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-14T09:18Zmainf2b17c6. The card closedcompletedby itsFixes #12341, which is the PR body's only closing keyword. No other card closed with it.9e6cb65c00:.changeset/12341-list-view-required-permissions.mdandlist-view-required-permissions-12341.test.ts.packages/types/src/objectql.tsdiffers from that head only by objectui PR feat(print): render a page that declaresprint— print CSS, printable blocks whole, record print action (objectui#11958) #12356's mergedExportFormatcomment, which landed first and was combined by the queue. This PR's ownNamedListView.requiredPermissions?: ListViewSchema['requiredPermissions']is onmainat:3227.pm:dispatchedis removed in the same stroke as this comment.Generated by Claude Code