Repository navigation
feat(app-shell): the served approval request page names who the pending step waits on (objectui#12482) - #12497
Merged
Conversation
…ng step waits on (objectui#12482) The decision panel (`record:approval_decision`), which objectstack's served `sys_approval_request_detail` page already mounts, draws a "Waiting on" row: each pending approver named through app-shell's one approver-naming path (`approverChips` over the row's `pending_approver_names` and `pending_approver_groups`, and `useApproverDirectory` for a reference the server left unnamed), with a position's registry label and the countersign group each slot fills. One person in several quorum slots is one chip with a count (objectui#12077's case); an id nothing can name keeps the identity formatter's middle-truncated face, as the inbox's sheet draws it. No page edit, no spec row, no prop and no export: the panel reads the row it is bound to. `DecisionProgressIndicator` does not carry the names because it is drawn only with a tally, and a first_response node has none. Pins: the panel's own suite, and the served page on the real RecordDetailView over the console's routed approvals source. Claude-Session: https://claude.ai/code/session_01JZnppgHRp4sREcouhkQYDB Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contributor
Author
Seat ACCEPT: objectui PR #12497 at
|
This was referenced Oct 15, 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.
Fixes #12482
Clause-②: no. The panel draws more of the bound row it already reads; no type, prop, export, page node or spec declaration changes.
What changes
The decision panel (
record:approval_decision), which objectstack's servedsys_approval_request_detailpage already mounts in itsactionsslot, now draws a "Waiting on" row between the tally and the decisions. Each pending approver is one chip:pending_approver_names, from the same approvals get the page reads);position:NAMEslot the server left unnamed, the position's registry label, with the people who hold it;pending_approver_groups, on aper_groupnode), as· Finance;Ada Lovelace ×3, objectui#12077's case); two people sharing a name in two groups stay two chips;zzUnre…z123), the face the inbox's sheet draws for it. Every raw slot stays in the chip'stitle, on hover only.The drawing reuses app-shell's one approver-naming path, the one the business record's approvals panel already draws its chips with:
approverChipsoverapproverDisplay, anduseApproverDirectoryfor references the server left unnamed. Files:packages/app-shell/src/views/approval-decision/ApprovalDecisionPanel.tsx(+ its test), one console test, one changeset.ApprovalsInboxPage(the sheet) is not touched (ADR-0060 rule 3), and nothing is exported.Fit before build (the ruling's item 2), measured
record:approval_decisionis a declared spec type with an empty strict props row, and the panel reads the bound request row. At objectstack57994c311fthe approvals get servespending_approvers,pending_approver_names(the service's display enrichment) and, on aper_groupnode,pending_approver_groups(set besidedecision_progress). The spec's approval-service contract declares both name maps as display enrichment. So the face needs no spec row, no page edit and no new block.DecisionProgressIndicatorcannot carry it without a contract change. It draws a tally, and its own docblock says a caller renders it only when the tally is present. Afirst_responsenode carries nodecision_progress, so the indicator is not drawn there at all, while that node's approver is still waiting. The sheet and the business record's approvals panel both draw the tally and the names as two parts under two gates (tally present; pending request with at least one slot). The panel does the same.approval-progress/**is unchanged.The PM's mechanism assumptions, measured
A1 holds. The panel already holds the row: it read
pending_approversfor the eligible count before this change.A2 holds, with the cost named. A slate the get named whole costs 0 extra requests (pinned). A
position:slot the server left unnamed costs:ensureType('position'), oneGET /api/v1/meta/positionper session, shared with every other reader of the store;sys_user_positionfind, plus onesys_userfind when the seat has holders;team:anddepartment:slots: one find per directory object.All reads are
$in-batched, once per slate per page session, through a module cache the decision buttons on the same request share. So at most 3 reads per page, whatever the number of approvers. Per approver: none. The sheet's own path reads onlyensureType('position'). The app-shell path also names the seat's holders, as the business record's panel already does.A3: the sheet's face is matched; its "and N more" form does not exist. The sheet draws its "Waiting on" row on a pending request with a slot:
Role: X, an email as is, a long id middle-truncated);· G, duplicates as×N, first-seen order, raw ids intitle.The sheet has no "and N more" form. Its only "more" strings are the list's load-more. So nothing of that shape is matched.
A4 is false on the served page: no raw approver id is printed there today. The served page's Details tab is a bare
record:details. Since objectui#12433 that draws the object'shighlightFieldsminus the strip's fields, which leavesrecord_idalone.pending_approversis not a highlight field. Measured on the realRecordDetailViewoverApprovalRecordReads, before this change, with a three-slot slate: the approver ids appear 0 times on the served page. The "More details" print objectui#12077 measured is the synthesized page, used when no page is served: the same probe there prints the ids 3 times, and the decision panel is not mounted on that page.The spec declares
pending_approversasField.textarea, a comma-separated slot store that mixes user ids,position:androle:refs and emails, not a user reference. So "draw the field by name" does not apply, and on the served page there is nothing to hide.Pins
ApprovalDecisionPanel.test.tsx, a new describe block covering:first_responsenode with no tally still names its approver.apps/console/src/services/approvalRequestsDataSource.servedPageApprovers-12482.test.tsx: the served page, copied whole, on the realRecordDetailViewunder the console'sApprovalRecordReads, with only the transport, the console adapter and the metadata store doubled. It pins:It lives in
apps/consolebecause the routed source it mounts is console code.Ablation (one-shot, not kept)
The fix was committed first (
233965379b). The script mutatedApprovalDecisionPanel.tsx, dropping thePendingApproverselement for a marker comment:Restore:
git checkout HEAD --the path, then a blob-hash match againstHEAD, withgit diff HEADempty, under atrapon EXIT, INT and TERM. No build sits between: the root vitest config aliases every@object-uispecifier to source, so the pins read the edited file.Gates (local subset; the full farm is CI's)
All local readings are at
233965379b(the branch head), with each exit code captured before any pipe.pnpm turbo run build --filter='@object-ui/console^...' --concurrency=2, run so the type-checks read built declarations. Exit 0; 34 tasks, 28 of them cache hits.pnpm --filter @object-ui/app-shell type-check(tsc --noEmit && tsc -p tsconfig.test.json): exit 0. The test project's include glob,src/**/*.test.tsx, covers the panel's suite. That is read off the glob, not counted with--listFiles.apps/consoletsc --noEmit --listFiles: exit 0. The new pin is in the file list (1 hit).approval-decision/andapproval-progress/;approvalRequestsDataSource.*suite andAppContent.approvalRecordReads;apps/console/src/pages/system/, which holds theApprovalsInboxPage.*pins, run as the CONTROL;RecordApprovalsPanel*, the twoRecordDetailViewapproval suites,global-page-blocks.render,DeclaredActionsBar.overrideAffordance,useApproverDirectory.contractEnvelope-5945andapproverIdentity*.product-neutral-renderer.ratchet,no-component-any-cast,no-refresh-key-remount,adr0054-ratchet,defaults-maps-mirror-en-pack,writeRefusalCensus-11583;ariaLabelLiteralCensus-12145,machineLocaleCensus-9909,dead-pack-keys-retired-8754,iconOnlyButtonCensus-12143;check-i18n-call-site-keys, the threecheck-vi-mock-*,one-authority-per-exported-name-6273,check-control-bytes;registry-inputs-spec-parity,record-approval-decision-arm-12342.check:control-bytesandcheck:new-line-citations(0 new citations);check:i18n-keysandcheck:i18n-drift;check:vi-mock-*andcheck:test-path-roots;check:changeset-claimsandcheck:pending-changeset-literals;check:handler-key-reads,check:metadata-write-doorsandcheck:unreferenced-sources;check-changeset-no-major,-overwrite,-presenceand-fixed.pnpm lint, repo-wide;pnpm testshards;check:sdui-registration-pins, which reads a full build;check:i18n-designer-parity(no label was added and no pack changed).Acceptance notes
submitter_id, declaredField.lookup('sys_user'), as the bare id. The approvals get serves the id plussubmitter_name, never an expansion. The strip made no label read:findOnewas never called, nosys_userread was made, and the servedsubmitter_namewas not shown.RecordDetailViewunderApprovalRecordReads, not on a live server.sys_approval_request_detailpage. This face does not reach it: the panel is not mounted there. The fix would be producer-side (the object's declaration) or a route-scoped hide, both off this surface.×3chip. The sheet and the business record's panel count the same way, and the count's meaning (slots or people) is not declared anywhere. Noted, not changed.approverChipsoutput: this panel and the business record's approvals panel (outside this card's file surface). The closing card that retires the sheet is the natural place to fold them into one.Generated by Claude Code