Repository navigation
fix(app-shell,console): an approver who opens an approval request sees its decision panel again, read from the approvals service - #28619
Merged
Conversation
…equest from the approvals service, never the data API's page payload The record page payload is the data API's read of a record, and the data API serves an approval request without its per-caller viewer block, with pending_approvers as the stored comma-joined string, and refuses the object to an ordinary approver. RecordDetailView now asks no payload for a request's own page, so every part reads on its own through the adapter, whose findOne the console routes to GET /approvals/requests/:id. Both routed sources refuse a request's getRecordPage and drop its prefetch, so a page drawn from the data API's row of a request cannot come back through that door. A drawer open that asks no payload keeps the related count warm-up. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUw276TncPo635XS3B
…pprovals get on the real adapter The account app's inbox row and ?request= deep link, a cold record route load, the admin approver's decisions and one chip per approver, all over the real ObjectStackAdapter under ApprovalRecordReads; the routed sources' refusal of a request's record page payload; and on app-shell, that a request's own page asks no payload while an ordinary record still does. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUw276TncPo635XS3B
… it, a get by id Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUw276TncPo635XS3B
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUw276TncPo635XS3B
…-5n78sm Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jUw276TncPo635XS3B
Contributor
Author
|
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y 席位核对 · ACCEPT · 2026-11-01T08:27ZHead-sha:
Generated by Claude Code |
This was referenced Nov 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Steedos · project thread
Fixes #28546
Clause-②: no
Metadata-protocol: none
domain:ui#4 card thread · os-dev · session_01QsR2jUw276TncPo635XS3B · claim 6554793732
维护者速读
sys_approval_request)自己的记录页不再走通用数据接口的一次性页面载荷(POST /api/v1/data/:object/:id/page),而是像 feat(rest,console): one request opens a record page #27713 之前那样各部分分别读取;控制台把请求本身路由到GET /api/v1/approvals/requests/:id。控制台的两个审批数据源还会直接拒绝请求对象的getRecordPage,预取则什么都不发。?request=链接或直接打开记录页都只看到“无权访问”,没有同意/拒绝;管理员审批人拿到的是数据库原样行,没有viewer,所以决策按钮不出现,pending_approvers是逗号拼接的字符串,“等待”一栏按字符一个一个画芯片。这是 18.3.0 的发版阻塞项。ui/packages/app-shell/src/views/RecordDetailView.tsx(recordPagePlan里对请求对象提前返回空计划,以及抽屉里相关计数预热的条件);ui/apps/console/src/services/approvalRequestsDataSource.ts(guardRecordPageRead);ui/apps/console/src/services/approvalRequestsDataSource.recordPagePayload-28546.test.tsx(真实ObjectStackAdapter下的验收用例)。cd ui && pnpm exec vitest run apps/console/src/services/approvalRequestsDataSource.recordPagePayload-28546.test.tsx→ 10 个用例全绿:非管理员从收件箱点开、冷加载记录页、?request=深链都看到决策面板,同意一次后按钮消失;管理员审批人看到同意/拒绝和每人一个芯片;整个过程没有任何发往/api/v1/data/sys_approval_request的请求。What changes
The defect, measured. Since #27713 the record page takes its record from
DataSource.getRecordPage, the data API'sPOST /data/:object/:id/page.routeApprovalRecordReadsoverrides onlyfindandfindOne, so on a request's own page that read reached the console's adapter unchanged:sys_approval_requeston the data API, so the payload answered 403 and the page drewrecord-access-denied("You don't have access to Approval Request records"), on the inbox row, on a cold load of the record route and on the?request=deep link;viewerblock, so the decision actions'record.viewer.can_actpredicates failed closed (no Approve, no Reject), andpending_approversas the comma-joined string, so the "Waiting on" strip drew one chip per character.Both are reproduced by the new console test on the base sources (below), over the real
ObjectStackAdapter.The fix routes the read, not the grant (triage 6554139580). Nothing about what the data API lets an approver read changes.
RecordDetailView:recordPagePlanis empty forsys_approval_request, so a request's page asks no payload. Every part reads on its own through the adapter, as before feat(rest,console): one request opens a record page #27713: the record throughfindOne, which the console routes toGET /approvals/requests/:id(withviewer, the decision tally and the approvers as an array); the timeline through the actions route; the header verdicts and the field projection through their own reads. The now-constantapprovalsflag of the plan readstrue.RecordDetailView, drawer: the related-count warm-up for the next record was skipped whenever the adapter hasprefetchRecordPage. It is now skipped only when the open actually asks a payload, so a request opened in a drawer keeps warming its neighbour's counts (otherwise this change would leave it with neither).approvalRequestsDataSource.ts: both routed sources (routeApprovalRecordReadsand the inbox's scopedcreateApprovalRequestsDataSource) refuse a request'sgetRecordPagewith the existingUNSUPPORTED_QUERY_PARAMrefusal before any request goes out, and send nothing for a request'sprefetchRecordPage. Every other object's call is the host's, and over a host without the payload neither member appears. This guard is not reachable from today's UI (the page asks no payload for a request); it keeps a future caller from drawing a request from the data API's row through this door, which is how this card and Non-admin approvers get a blank approval request page since the console pin moved past objectui#12484 (18.1.0 release blocker) #25228 both happened.File surface: exactly the claim's (
ui/apps/console/src/services/**,ui/packages/app-shell/src/views/RecordDetailView.tsx) plus one new app-shell test file beside it and the changeset. No producer change elsewhere was needed: the approvals get already serves the request correctly.Tests
ui/apps/console/src/services/approvalRequestsDataSource.recordPagePayload-28546.test.tsx(10 cases), the console's composition over the realObjectStackAdapterunderApprovalRecordReads, the real inbox page on the account app'scomponent/approvals/inboxroute and the realRecordDetailViewwith the served request page. Onlyfetch(answered as the server does, per persona) and the inbox's queue read are doubled.approvals.account-app-entryacceptance[3]: a row click opens/apps/com.objectstack.account/sys_approval_request/record/ID, the decision panel draws Approve and Reject, one approval POSTs through the action door, the request is re-read from the approvals get and the decisions stop rendering.?request=IDon the component route replaces itself with the record route, which shows that request; Back leaves to the previous entry.Dev Admin,Ada Auditor,Lee Legal)./api/v1/data/sys_approval_requestat all, and norecord-access-denied.getRecordPagerejects withUNSUPPORTED_QUERY_PARAMand never reaches the host; a request's prefetch is dropped; another object's calls are the host's; CONTROL: over a host without the payload, neither member exists.ui/packages/app-shell/src/views/RecordDetailView.approvalRequestPage-28546.test.tsx(3 cases, real adapter): a request's page reads its record byfindOneand sends no page request; CONTROL: an ordinary record on the same adapter opens with its page request; in a drawer, a request's next neighbour has its related counts warmed and no page is asked.Readings (head 6d6aea8 unless named)
6d6aea80:pnpm exec vitest runof both new files plusRecordDetailView.recordPagePayload-27627.test.tsxandRecordDetailView.recordReadFailure-11902.test.tsx(the two existing payload pins): 4 files, 28 tests passed.d8234f05withRecordDetailView.tsxandapprovalRequestsDataSource.tsrestored to the base617dcf43, blob hashes checked on disk, restored fromHEADwithgit diff HEADempty): 10 failed, 3 passed (the 3 are the CONTROL cases). The member cases drewrecord-access-denied("You don't have access to Approval Request records"); the admin case drew the panel with nodeclared-action-approval_approveand fiveapprover-chips for the slateu1,u2,u3, the issue's per-character chips.recordPagePlan &&removed throughscripts/ablation-replace.mjs, anchor 1 to 0, restored blob equal toHEAD): exactly the drawer case red (1 failed, 2 passed).routeApprovalRecordReads' guard alone: exactly its two pins red (2 failed, 8 passed); the integration cases stay green because the page asks no payload for a request. The guard is declared above as unreachable from today's UI.ui/packages/app-shellandui/apps/consoletest file that namesRecordDetailView,recordPagePayload,approvalRequestsDataSource,routeApprovalRecordReadsorApprovalRecordReads(git grep -l, 227 files) at010fbf63, two locked batches at--maxWorkers=2: 114 files / 903 tests passed and 113 files / 817 tests passed. A narrowing: CI's Test UI runs the rest. The merge of main at6d6aea80touched no file underui/.pnpm --filter @object-ui/app-shell --filter @object-ui/console run type-check: bothDone, exit 0 (010fbf63).Acceptance notes
approvals.account-app-entry, a booted showcase with the invited member) is QA's, per the triage. NOT MEASURED here: no browser run against a booted showcase; the jsdom harness runs the shipped adapter, routed source, inbox page and record view, with only the HTTP transport doubled.Verification
Derived gates, local subset (34 of 34, head 6d6aea8)
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 34 families for this diff; every one ran on6d6aea80and exited 0, withGITHUB_EVENT_PATHholding this body for the body-reading gates.dispatch-gates --ran: "34 derived famil(ies) accounted for — 34 run, 0 NOT-MEASURED". Changed lines 765, under the 3000 human-merge threshold; no governed surface touched.check-adr-0087-registration --base origin/mainand--self-test,check-changeset-no-major --base origin/main(and--event),check-closing-keyword-parityand--self-test,check-comment-mask-corpus,check-empty-changeset --base origin/mainand--self-test,check-sdui-manifestand--self-test,release-rehearsal-clone --self-test,release-pending-publish --self-test: exit 0.pnpm --dir ui exec vitest run scripts/(locked): 169 files passed, 2 skipped; 4900 tests passed.check:duration-unit-keys,check:changeset-gate-self-tests,check:closed-ceremony-text,check:cross-package-test-inputs,check:dispatcher-error-vocabulary,check:driver-memory-census,check:engine-double-contract,check:gitlink-declared,check:nul-bytes,check:pm-changeset-deadline-census,check:published-files,check:query-options-erasure(locked),check:refd-timer-probe,check:skill-identifier-liveness,check:test-source-alias,check:tier-file-adoption,check:type-check-coverage,check:type-check-debt(locked),check:undeclared-dep-imports,check:watch-hint-literal,check:where-matcher: exit 0.check-partof-closing-keywordexit 0.Generated by Claude Code