Repository navigation
fix(app-shell): the approval decision panel draws a localized notice off an approval request page (objectui#12072) - #12073
Conversation
…off an approval request page (objectui#12072) Off a sys_approval_request record page (another object's record page, or a page with no record context) the record:approval_decision renderer now draws a short notice that it works only on approval request pages, instead of null. The outside test is the existing objectName comparison, kept as one test. On a request page whose row has not loaded it still draws nothing; with its row it draws the panel unchanged. One new language-pack key, approvalsInbox.decisionPanelOffRequestPage, in all ten packs. The panel stays module-internal: no registration, index import, known-types line or zod arm. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
…ctui#12072 Append-only rider on the pending objectui#12045 changeset: outside a sys_approval_request record page the decision panel now draws a localized notice instead of nothing, so that entry's "renders nothing" clause no longer holds when both changes release together. Front matter and every existing line unchanged (claim amendment 6090288136). Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
Contract reviewServed-tier: ① Derived judgmentsRead, and nothing else: card objectui#12072 (body, claim Public surface, Public surface, Accept-set: UNCHANGED. Right. No registration, no Renderer behaviour against the card's acceptance: right on every clause.
Pins, Check-runs on the head: every gate family concluded success. Lint; Type Check; Build & E2E; Test shards 1/8 through 8/8 and Test; Test (dist pins); Build Docs; Bundle Analysis (Console Performance Budget PASS, eager closure 3168.0 KB of 3204.6 KB); README Export Check; Changeset Declaration; Changeset Claim Re-read; Changeset Bump Policy; Changeset Fixed Group Check; Changeset Overwrite Report; Governed Surface Queue Guard; Line Citation Gate; Control Byte Scan; Inert vi.mock Specifier Check; Pre-Install Import Graph Check; Spec Main Shape Gate; Action Ref Convention; Shell Escape Residue Scan; the doc and skill checks; label; Live E2E (informational). Three concluded ② Semver levelClause-②: yes
The second changeset file, ③ Boundary flags
Implemented-by: VERDICT: PASS |
Fixes #12072
Clause-②: yes
What this does
Placed anywhere other than an approval request's record page, the decision panel used to draw nothing. Its renderer (
ApprovalDecisionRendererin@object-ui/app-shell, the renderer for the proposedrecord:approval_decision, still module-internal) now draws a short, localized notice there instead of null. A block an author places on the wrong page is now visible to the author instead of silently empty.ctx?.objectName === SYS_APPROVAL_REQUEST_OBJECTcomparison. It stays one comparison, written as!==with an early return of the notice, androwis read fromctx.dataafter it. There is no second copy.approvalsInbox.decisionPanelOffRequestPage, in all ten packs. No other key.Changesets:
@object-ui/i18nminor (one new pack key, so the exportedenpack and theTranslationKeystype read off it gain one member) and@object-ui/app-shellpatch, in.changeset/12072-decision-panel-notice.md.Zone 2 hypotheses, measured
DecisionProgressIndicatorandRecordApprovalsPanel, calluseObjectTranslationfrom@object-ui/reactand readapprovalsInbox.*rows by literal key, with an inlinedefaultValue. The notice does the same:t('approvalsInbox.decisionPanelOffRequestPage', { defaultValue }). The default equals theenvalue, whichcheck:i18n-keysverifies. The key isapprovalsInbox.decisionPanelOffRequestPage.record:path's "no stages configured" placeholder (plugin-detailrecord-path.tsx, thestages.length === 0branch), a muted, italic, dashed-border box:text-xs text-muted-foreground italic px-3 py-2 border border-dashed rounded. The notice reuses that class string verbatim;record:path's copy is a literal, this one comes from the packs. Nothing underpackages/components/src/ui/**is touched. The node'sclassNameand designer attributes stay on the wrapper, as they do on the panel.useRecordContext()returns null, soctx?.objectNameis undefined, which is not the request object, and the notice draws. That matches the card ("any page other than an approval request's") and the claim ("the context names another object, or none"). Pinned below.On the real request page,
RecordDetailView'sRecordContextProvidernames the object from the route on its first render, so no notice flashes while the request row loads. The console'sapprovalRequestsDataSource.decisionPage.test.tsx, which mounts the real request page with the renderer registered in-test, is 7/7 green on this head.Pins (
ApprovalDecisionPanel.test.tsx)The claim's four, plus the no-context case and a designer-attribute check:
enpack value, with no panel and no actions bar;zhprovider renders thezhpack value, and a control shows anenprovider renders theenvalue and that the two packs differ;className,data-obj-idanddata-obj-type.Gates, on head
7d10b7c05pnpm exec vitest run packages/app-shell/src/views/approval-decision/: 1 file, 11 passed.pnpm exec vitest run apps/console/src/services/approvalRequestsDataSource.decisionPage.test.tsx: 1 file, 7 passed.pnpm exec vitest run packages/i18n/: 82 files, 1,316 passed, 13 skipped.pnpm --filter @object-ui/app-shell type-checkandpnpm --filter @object-ui/i18n type-check: exit 0, both script names echoed. They ran afterturbo run build --filter='@object-ui/app-shell^...'(28 tasks), so the types are read from freshdistbuilds.tsc -p tsconfig.test.json --listFilesOnlylists the panel test, so the test is type-checked too.pnpm exec eslinton the 12 touched code files: 0 errors, 0 warnings.check:i18n-keys,check:i18n-drift("0 en value(s) changed (1 key(s) added …)"),check:i18n-dead-keys(the new key is not listed),check:i18n-designer-parity,check:control-bytes,check:test-path-roots,check:changeset-claims,check:pending-changeset-literals,check:new-line-citations,check:vi-mock-inherit,check:action-forward-parityandcheck:installed-pin-claims. Also exit 0: the changeset presence, no-major, fixed and overwrite scripts.eslint.config.js'sfiles: ['**/*.{ts,tsx}'], which covers all 12 code files;parserOptionsand noprojectService, and no rule undereslint-rules/reads the filesystem. Linting is therefore per-file, and this diff cannot move a verdict on any untouched file.Ablation (predictions written before either leg ran)
Both legs ran through
ablation-replacein wrap mode. Each anchor hit exactly once, and each mutation was confirmed on disk by the anchor count and a blob change. After each leg, the file's blob equals its HEAD blob andgit diff HEADis empty. The test resolves@object-ui/i18nto source through the vitest alias, so nodistis in the path.if (ctx?.objectName !== SYS_APPROVAL_REQUEST_OBJECT) return null;, with the notice block made dead code). Predicted 5 failed and 6 passed; observed 5 failed and 6 passed, the same five: both notice cases, the designer-attribute check, thezhprovider and theencontrol.zhpack. Predicted 2 failed and 9 passed; observed 2 failed and 9 passed: "all ten packs define the key" and "thezhprovider renders thezhvalue" (it fell back toen).First-load bytes
No entry imports the panel module:
git grepforApprovalDecisionPaneloutside tests returns zero hits, and as a control the console test does import it. Theencatalogue is in the eager closure, though, so the newenvalue adds bytes there. At the source level,en.tsgrows by 102 B raw and 36 B gzip -9.check:eager-closureis NOT MEASURED here, because it needs a full console build; the CI Bundle Analysis job runs it.Acceptance notes
.changeset/12045-approval-decision-panel.md) says that outside asys_approval_requestrecord page the panel "renders nothing". If both changesets release together, this change makes that clause false. That file is outside this claim's file surface and is not edited here. Instead, this PR's changeset says it replaces that clause. The seat may add an append-only "Superseded in part" paragraph there, as amendment 4 did for objectui#12032.envalue; see First-load bytes above.NOT MEASURED, left to CI: the full app-shell suite (per the dispatch),
check:eager-closureandcheck:eager-locale-catalogues(both need a console build; no catalogue is added, so the eager composition is unchanged by construction), and the repo-widepnpm lint.Session:
https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8(the dispatching seat's session; this dev ran as its subagent).Generated by Claude Code