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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
approval record page: a row read through the approvals door is re-filtered against /auth/me/permissions, so a non-admin approver sees a blank request page (18.1.0 release blocker, objectstack#25228) #13324
Filed by the triage seat (objectstack #6015, session_01AavokzJ5DndAwitDXvKy4U). This is the objectui half of objectstack-ai/objectstack#25228, found by the 18.1.0 pre-release P0 run objectstack-ai/objectstack#25227 that the maintainer asked for before merging the Version Packages PR. ⛔ Not a claim.
Cross-repo rule: "正文抽掉 objectstack 还成立 ⇒ 转仓". The defect and its fix are objectui's. The objectstack card carries only the console pin move after this lands.
What happens (reproduced 8 times by two runners, stock showcase)
A non-admin pending approver opens a request from the account app's Approvals inbox. The page is blank: the heading and a chip, empty Details and Timeline tabs, and no decision actions. The admin sees the full page. The inbox's in-row Approve and Reject still work.
Mechanism (objectui cd14d8eeffe1, read by the runner)
approvalRequestsDataSource.ts (routeApprovalRecordReads) routes the record read to the approvals door, which answers 200 with viewer.can_act: true.
RecordDetailView.tsx folds that row through withoutDeniedFields against /auth/me/permissions (readablePageRecord = readableRow(...)).
MePermissionsProvider.checkField answers false for an object missing from the permissions answer. The persona has no sys_approval_request entry, so every field but id is dropped, viewer and status included.
Every action's visible predicate on record.viewer.can_act fails. Title, highlights and details are empty.
Causal check: granting read on the two objects in the persona's /auth/me/permissions response, in the browser, renders the page fully.
Why the tests stayed green:AppContent.approvalRecordReads.test.tsx stubs MePermissionsProvider, so nothing is stripped.
Regression:#12484 (a91f6bbb1) replaced the side sheet with the served record page. 18.0.0 shipped the side sheet and is unaffected.
Fix direction
objectstack keeps data-door read on sys_approval_request closed to non-admins on purpose: ADR-0103, request-read-gate.ts. So the page must not re-filter a row that the approvals door already filtered.
For rows read through that door, skip the readableRow fold and the details / highlights field-security fold.
Or have the data source mark such rows as already filtered and honour that mark.
⛔ Not by asking objectstack to grant read on the object.
Pin: render the page under a realMePermissionsProvider with a permissions answer lacking sys_approval_request. The fields, the viewer block and the actions must render for a non-admin approver.
After landing
objectstack-ai/objectstack#25228 moves the console pin past this fix. Then the P0 clause (approvals.account-app-entry acceptance[3]) is re-run.
Claim: card thread, coordinator-dispatched (18.1.0 release blocker)
Session: session_016ZEJXR398Mp5xcwuMncpK9
Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-13324-approval-door-reads
Worktree: objectui-issue-13324
Domain: domain:ui
Seat: none (domain:ui has no open seat post; this project thread was dispatched by the project coordinator on the maintainer's decision recorded on objectstack-ai/objectstack#25228, 2026-10-22 comment "Maintainer decision (2026-10-22): wait for the objectui fix, then move the pin")
File surface: packages/permissions/src/ (a field-read scope over the enclosing permission answer, with its tests), packages/permissions/README.md, apps/console/src/AppContent.tsx (ApprovalRecordReads) and the console's approval-page tests, content/docs/guide/ (the permissions guide page, if it lists the provider family), .changeset/ (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default judgment tier (dispatch-gates --tier names no path-derived mandate for this surface), effort: xhigh (p1)
Clause-②: yes
Responsibility: objectui's record page re-filters a row the approvals door already served against the data door's /auth/me/permissions answer | the approvals routes (GET /approvals/requests/:id and /actions) already apply the request read gate server-side; nothing on the client path honours that | every non-admin approver reaches it from the account app's Approvals inbox today; 18.1.0 would ship it
Thread-read: none
Serial constraints cleared: none (open objectui PRs read at claim time: none touches packages/permissions/src/, AppContent.tsx or approvalRequestsDataSource*; the release PR touches only packages/permissions/CHANGELOG.md and package.json)
Plan, stated so the review can hold it to it: the console already routes a request's findOne and its timeline read to the approvals routes for every record surface below ApprovalRecordReads. The same provider will also scope the client's field-READ gate for sys_approval_request and sys_approval_action to the server that served the rows, so withoutDeniedFields and every checkField(..., 'read') fold stop stripping a row the door already filtered. Write/create verdicts, object verdicts and every other object keep the enclosing /auth/me/permissions answer. ⛔ No change asks objectstack to grant read on either object. Clause-②: yes because the scope is a new export of @object-ui/permissions. The pin renders the served page under a real MePermissionsProvider whose answer omits both objects.
Seat correction · domain:ui seat 1 · session_01WJozybDmRH8z2c5SD7vJkj · 2026-10-22T15:00Z · ⛔ not a new claim, ⛔ not a state change.
The claim 6343187310 reads Seat: none because no domain:ui seat 1 was sitting when it was written. The seat sat afterwards (objectui#5560) and adopts this card: read the claim's seat as domain:ui#1. The claim's session (session_016ZEJXR398Mp5xcwuMncpK9, a card session of the same project) stays the implementer and keeps the assignee. This seat owns the contract-review record of the Clause-②: yes declaration on PR #13326 and the landing (ready, auto-merge, follow to MERGED).
merged through the merge queue at 2026-10-22T17:00:36Z, merge commit 0d12e95361b20b1eb9012f97d2d36e5fa7f14374 (re-queued once after a Test-gate ejection; the gate is objectui#13327)
origin/main content
apps/console/src/AppContent.tsx at main mounts <ServerReadScope 1 time(s) (0 at 1aa88519)
State: the card closed completed on the merge; pm:dispatched comes off in this stroke. The objectstack half (the console pin move and the non-admin re-test, objectstack-ai/objectstack#25228) stays with the card session session_016ZEJXR398Mp5xcwuMncpK9.
Activity
objectstack-fleet commentedon Oct 22, 2026
Claim: card thread, coordinator-dispatched (18.1.0 release blocker)
Session:
session_016ZEJXR398Mp5xcwuMncpK9Account:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-13324-approval-door-readsWorktree:
objectui-issue-13324Domain:
domain:uiSeat: none (
domain:uihas no open seat post; this project thread was dispatched by the project coordinator on the maintainer's decision recorded on objectstack-ai/objectstack#25228, 2026-10-22 comment "Maintainer decision (2026-10-22): wait for the objectui fix, then move the pin")File surface:
packages/permissions/src/(a field-read scope over the enclosing permission answer, with its tests),packages/permissions/README.md,apps/console/src/AppContent.tsx(ApprovalRecordReads) and the console's approval-page tests,content/docs/guide/(the permissions guide page, if it lists the provider family),.changeset/(stop on breach; explain in the report)Container & model:
M,mode:thread,model: default judgment tier (dispatch-gates --tier names no path-derived mandate for this surface),effort: xhigh(p1)Clause-②: yes
Responsibility:
objectui's record page re-filters a row the approvals door already served against the data door's /auth/me/permissions answer|the approvals routes (GET /approvals/requests/:id and /actions) already apply the request read gate server-side; nothing on the client path honours that|every non-admin approver reaches it from the account app's Approvals inbox today; 18.1.0 would ship itThread-read: none
Serial constraints cleared: none (open objectui PRs read at claim time: none touches
packages/permissions/src/,AppContent.tsxorapprovalRequestsDataSource*; the release PR touches onlypackages/permissions/CHANGELOG.mdandpackage.json)Plan, stated so the review can hold it to it: the console already routes a request's
findOneand its timeline read to the approvals routes for every record surface belowApprovalRecordReads. The same provider will also scope the client's field-READ gate forsys_approval_requestandsys_approval_actionto the server that served the rows, sowithoutDeniedFieldsand everycheckField(..., 'read')fold stop stripping a row the door already filtered. Write/create verdicts, object verdicts and every other object keep the enclosing/auth/me/permissionsanswer. ⛔ No change asks objectstack to grant read on either object.Clause-②: yesbecause the scope is a new export of@object-ui/permissions. The pin renders the served page under a realMePermissionsProviderwhose answer omits both objects.objectstack-fleet commentedon Oct 22, 2026
Seat correction ·
domain:uiseat 1 ·session_01WJozybDmRH8z2c5SD7vJkj· 2026-10-22T15:00Z · ⛔ not a new claim, ⛔ not a state change.The claim
6343187310readsSeat: nonebecause nodomain:uiseat 1 was sitting when it was written. The seat sat afterwards (objectui#5560) and adopts this card: read the claim's seat asdomain:ui#1. The claim's session (session_016ZEJXR398Mp5xcwuMncpK9, a card session of the same project) stays the implementer and keeps the assignee. This seat owns the contract-review record of theClause-②: yesdeclaration on PR #13326 and the landing (ready, auto-merge, follow to MERGED).objectstack-fleet commentedon Oct 22, 2026
LANDED ·
domain:uiseat 1 ·session_01WJozybDmRH8z2c5SD7vJkj· 2026-10-22T17:22Z0d12e95361b20b1eb9012f97d2d36e5fa7f14374(re-queued once after aTest-gate ejection; the gate is objectui#13327)origin/maincontentapps/console/src/AppContent.tsxatmainmounts<ServerReadScope1 time(s) (0 at1aa88519)State: the card closed
completedon the merge;pm:dispatchedcomes off in this stroke. The objectstack half (the console pin move and the non-admin re-test, objectstack-ai/objectstack#25228) stays with the card sessionsession_016ZEJXR398Mp5xcwuMncpK9.fix(console,permissions): a non-admin approver sees the whole approva…