This repository was archived by the owner on Oct 26, 2026. It is now read-only.
Repository navigation
Commit 20c6d35
fix(app-shell): the approval decision panel draws a localized notice off an approval request page (objectui#12072) (#12073)
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
(`ApprovalDecisionRenderer` in `@object-ui/app-shell`, the renderer for
the proposed `record: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.
- **The "outside" test** is the existing `ctx?.objectName ===
SYS_APPROVAL_REQUEST_OBJECT` comparison. It stays one comparison,
written as `!==` with an early return of the notice, and `row` is read
from `ctx.data` after it. There is no second copy.
- **Off a request page** (another object's record page, or a page with
no record context at all): the notice. **On a request page whose row has
not loaded yet:** null, as before. **On a request page with its row:**
the panel, unchanged.
- **One new language-pack key**,
`approvalsInbox.decisionPanelOffRequestPage`, in all ten packs. No other
key.
- **Not here:** the registration, the index import, the known-types line
and the zod arm, which belong to objectui#12045's page-mount step. No
lint, save-door or schema rule (triage's choice).
- The module docblock's "renders nothing" sentence now describes the
notice.
Changesets: `@object-ui/i18n` minor (one new pack key, so the exported
`en` pack and the `TranslationKeys` type read off it gain one member)
and `@object-ui/app-shell` patch, in
`.changeset/12072-decision-panel-notice.md`.
## Zone 2 hypotheses, measured
1. **How the sibling renderers translate.** The approval module's
siblings, `DecisionProgressIndicator` and `RecordApprovalsPanel`, call
`useObjectTranslation` from `@object-ui/react` and read
`approvalsInbox.*` rows by literal key, with an inline `defaultValue`.
The notice does the same:
`t('approvalsInbox.decisionPanelOffRequestPage', { defaultValue })`. The
default equals the `en` value, which `check:i18n-keys` verifies. The key
is `approvalsInbox.decisionPanelOffRequestPage`.
2. **The notice's look.** No notice component exists for a record
block's "not applicable here" state. The nearest sibling state is
`record:path`'s "no stages configured" placeholder (plugin-detail
`record-path.tsx`, the `stages.length === 0` branch), 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 under `packages/components/src/ui/**` is touched. The
node's `className` and designer attributes stay on the wrapper, as they
do on the panel.
3. **No record context.** Outside a provider `useRecordContext()`
returns null, so `ctx?.objectName` is 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`'s `RecordContextProvider`
names the object from the route on its first render, so no notice
flashes while the request row loads. The console's
`approvalRequestsDataSource.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:
- the block on a non-request record page renders the notice, whose text
is the `en` pack value, with no panel and no actions bar;
- the block with no record context at all renders the same notice;
- the block on a request page renders the panel, and no notice;
- the notice string is in the i18n bundle: all ten packs define the key
with a non-empty value, a `zh` provider renders the `zh` pack value, and
a control shows an `en` provider renders the `en` value and that the two
packs differ;
- on a request page whose row is not yet loaded, the node still renders
nothing;
- the notice keeps the node's `className`, `data-obj-id` and
`data-obj-type`.
## Gates, on head `7d10b7c05`
- `pnpm 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-check` and `pnpm --filter
@object-ui/i18n type-check`: exit 0, both script names echoed. They ran
after `turbo run build --filter='@object-ui/app-shell^...'` (28 tasks),
so the types are read from fresh `dist` builds. `tsc -p
tsconfig.test.json --listFilesOnly` lists the panel test, so the test is
type-checked too.
- `pnpm exec eslint` on the 12 touched code files: 0 errors, 0 warnings.
- Exit 0: `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-parity` and
`check:installed-pin-claims`. Also exit 0: the changeset presence,
no-major, fixed and overwrite scripts.
- **The eslint run covered only the touched files.** Three facts show
that this narrowing excludes nothing:
- the population is `eslint.config.js`'s `files: ['**/*.{ts,tsx}']`,
which covers all 12 code files;
- the JSON output counts 12 files;
- the config has no `parserOptions` and no `projectService`, and no rule
under `eslint-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-replace` in 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 and `git diff HEAD` is empty. The test resolves `@object-ui/i18n`
to source through the vitest alias, so no `dist` is in the path.
- **Leg A: the renderer returns null off a request page again** (`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, the `zh` provider and the `en` control.
- **Leg B: the key's line deleted from the `zh` pack.** Predicted 2
failed and 9 passed; observed 2 failed and 9 passed: "all ten packs
define the key" and "the `zh` provider renders the `zh` value" (it fell
back to `en`).
## First-load bytes
No entry imports the panel module: `git grep` for
`ApprovalDecisionPanel` outside tests returns zero hits, and as a
control the console test does import it. The `en` catalogue is in the
eager closure, though, so the new `en` value adds bytes there. At the
source level, `en.ts` grows by 102 B raw and 36 B gzip -9.
`check:eager-closure` is NOT MEASURED here, because it needs a full
console build; the CI Bundle Analysis job runs it.
## Acceptance notes
- **objectui#12045's pending changeset**
(`.changeset/12045-approval-decision-panel.md`) says that outside a
`sys_approval_request` record 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.
- The dispatch reading "the notice should move no first-load bytes"
holds for the renderer code, but not for the `en` value; see First-load
bytes above.
NOT MEASURED, left to CI: the full app-shell suite (per the dispatch),
`check:eager-closure` and `check:eager-locale-catalogues` (both need a
console build; no catalogue is added, so the eager composition is
unchanged by construction), and the repo-wide `pnpm lint`.
Session: `https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8` (the
dispatching seat's session; this dev ran as its subagent).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent c0c0a0d commit 20c6d35
14 files changed
Lines changed: 126 additions & 9 deletions
File tree
- .changeset
- packages
- app-shell/src/views/approval-decision
- i18n/src/locales
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
| 17 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
21 | 25 | | |
22 | 26 | | |
23 | 27 | | |
| |||
34 | 38 | | |
35 | 39 | | |
36 | 40 | | |
| 41 | + | |
| 42 | + | |
37 | 43 | | |
38 | 44 | | |
39 | 45 | | |
| |||
60 | 66 | | |
61 | 67 | | |
62 | 68 | | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
63 | 79 | | |
64 | 80 | | |
65 | 81 | | |
| |||
77 | 93 | | |
78 | 94 | | |
79 | 95 | | |
| 96 | + | |
| 97 | + | |
80 | 98 | | |
81 | 99 | | |
82 | 100 | | |
| |||
100 | 118 | | |
101 | 119 | | |
102 | 120 | | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
| 121 | + | |
| 122 | + | |
108 | 123 | | |
109 | 124 | | |
110 | 125 | | |
111 | 126 | | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
41 | 45 | | |
42 | 46 | | |
43 | 47 | | |
| |||
68 | 72 | | |
69 | 73 | | |
70 | 74 | | |
71 | | - | |
| 75 | + | |
72 | 76 | | |
73 | 77 | | |
74 | 78 | | |
| |||
123 | 127 | | |
124 | 128 | | |
125 | 129 | | |
126 | | - | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
127 | 138 | | |
128 | 139 | | |
129 | 140 | | |
130 | 141 | | |
131 | 142 | | |
132 | 143 | | |
| 144 | + | |
133 | 145 | | |
134 | | - | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
135 | 161 | | |
136 | 162 | | |
137 | 163 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3907 | 3907 | | |
3908 | 3908 | | |
3909 | 3909 | | |
| 3910 | + | |
3910 | 3911 | | |
3911 | 3912 | | |
3912 | 3913 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3616 | 3616 | | |
3617 | 3617 | | |
3618 | 3618 | | |
| 3619 | + | |
3619 | 3620 | | |
3620 | 3621 | | |
3621 | 3622 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4247 | 4247 | | |
4248 | 4248 | | |
4249 | 4249 | | |
| 4250 | + | |
4250 | 4251 | | |
4251 | 4252 | | |
4252 | 4253 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3687 | 3687 | | |
3688 | 3688 | | |
3689 | 3689 | | |
| 3690 | + | |
3690 | 3691 | | |
3691 | 3692 | | |
3692 | 3693 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3684 | 3684 | | |
3685 | 3685 | | |
3686 | 3686 | | |
| 3687 | + | |
3687 | 3688 | | |
3688 | 3689 | | |
3689 | 3690 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3618 | 3618 | | |
3619 | 3619 | | |
3620 | 3620 | | |
| 3621 | + | |
3621 | 3622 | | |
3622 | 3623 | | |
3623 | 3624 | | |
| |||
0 commit comments