Repository navigation
Commit 054652b
fix(app-shell,react,plugin-detail): fold an override-only admin's approval levers into one menu and draw Recall once (#26053)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9Yb7oXSyx8Wfrc3ohS5yGJm)_
Fixes #25877
Clause-②: yes (widening)
Metadata-protocol: none
The `Clause-②` arm is the claim's ruling: one optional member is added
to the exported `InlineEditContext` value (and to `InlineEditProvider`'s
props) of `@object-ui/react`, which widens its public type surface and
narrows nothing. The member is optional so that every existing host
keeps its behaviour: absent means `false`, and then the DetailView
approval band draws its own Recall exactly as before. A host sets it
only when it draws the request's declared Recall itself.
## What changes
Placement only. Who may override, what an override records, the override
notice each lever shows and the request each lever sends are unchanged.
- **Record page, admin who is not the approver** (`viewer.can_act:
false`, `viewer.can_override: true`): the approval bar above the title
is now one line: a Pending badge with "Waiting on" and the pending
approvers by name, the levers with no override arm as buttons (Reply,
for the submitter), and one amber "Admin override" menu holding every
lever the override flag admits (Override Approve, Override Reject,
Override Reassign, and Recall, whose gate ORs the flag in too). The
folded set is read off each action's own `visible` gate
(`actionAdmittedByOverride`), not off a name list. The submitter's own
Recall stays the plain Recall inside the menu; a non-submitter admin
sees Override Recall, as before.
- **Record page, approver**: unchanged (`foldOverrides` folds nothing
for a viewer who can act).
- **Recall drawn once**: `DeclaredActionsBar` reports which declared
actions it actually draws (new optional `onDrawnActionsChange`, after
the capability gate and each action's `visible` verdict; folded levers
report through a visibility probe while their menu is closed). While it
draws `approval_recall`, the record page sets
`approvalRecallDrawnByHost`, and the DetailView approval band
(`@object-ui/plugin-detail`) keeps its lock badge and tally only. Read
off what the bar draws, not off whether it is mounted: before the
request definition loads, or on a backend that sends no `viewer` block
(every declared gate fails closed), the bar draws no Recall and the band
stays the submitter's lever.
- The override menu appears only once a folded lever is shown, so a set
whose every gate says no leaves no empty menu.
- `scripts/sdui-manifest.record.json` re-recorded on the merged tree
(`node scripts/gen-sdui-manifest-node.mjs`); `sdui.manifest.json` itself
is byte-identical.
Files: `ui/packages/app-shell/src/views/DeclaredActionsBar.tsx`,
`ui/packages/app-shell/src/views/RecordDetailView.tsx`,
`ui/packages/react/src/context/InlineEditContext.tsx`,
`ui/packages/plugin-detail/src/DetailView.tsx` (seat 2's package, held
by this card with seat 2's knowledge), tests beside each, root changeset
(patch for `@object-ui/app-shell`, `@object-ui/react`,
`@object-ui/plugin-detail`).
## Verification
All on head `df86fb71` (main `39a78b15` merged in):
```
$ cd ui && pnpm exec vitest run --maxWorkers=2 PATHS # 101 files under the verify lock
Test Files 101 passed (101)
Tests 728 passed (728)
$ pnpm --filter @object-ui/react --filter @object-ui/plugin-detail --filter @object-ui/app-shell type-check
os-verify-lock: VERDICT command-exit 0
```
The 101 files are every `RecordDetailView.*`, `RecordApprovalsPanel.*`
and `DeclaredActionsBar.*` test in app-shell, every `DetailView.*` test
in plugin-detail, and the `InlineEditContext` / `resolveApprovalLock`
tests in react. They include
`RecordDetailView.recordOpenRequests-12701`, whose control case ("every
approvals consumer still shows its state") is what moved the signal from
"bar mounted" to "bar draws Recall". New tests:
-
`app-shell/src/views/__tests__/DeclaredActionsBar.foldOverrides-25877.test.tsx`
(8): the fold, the status line, Reply stays a button, menu order and
labels, a folded lever still dispatches with its override notice, the
approver's bar unchanged, no change without `foldOverrides`, the
drawn-actions report (menu closed, approver set, unmount), no empty
menu.
-
`plugin-detail/src/__tests__/DetailView.approvalRecallHostDrawn-25877.test.tsx`
(3): signal on, absent, and toggled.
- `RecordDetailView.approvalRecallGate.test.tsx` (+3, page to band
seam): the signal is true while the bar draws Recall, false on a backend
with no `viewer` block, false with no approvals API.
- `RecordDetailView.approvalDeclaredActions.test.tsx`: the
override-admin case now opens the menu to find its three levers.
The first type-check run on the merged tree failed once in
`metadata-admin/useMonacoFallback.ts` (`configureBundledMonaco` missing
from a `plugin-editor` dist built before #25993 merged); after `pnpm
--filter '@object-ui/app-shell^...' build` all three packages pass. That
file is not in this diff.
Reverse verification, each mutation through
`scripts/ablation-replace.mjs` with its on-disk proof and a
byte-identical restore to `HEAD` (tests resolve `@object-ui/*` to
source, so no rebuild leg):
| mutation | went red |
|---|---|
| band: drop the `approvalRecallDrawnByHost` term from the Recall
condition | 2 of 3 in `DetailView.approvalRecallHostDrawn-25877` |
| bar: never fold (`false && foldOverrides …`) | 3 of 8 in
`DeclaredActionsBar.foldOverrides-25877` |
| bar: always draw the menu trigger | "draws no menu when every folded
lever is hidden by its own gate" |
| page: `approvalRecallDrawnByHost={false}` | "tells the band the page
draws recall while the decision bar draws it" |
| page: signal on mount instead of on drawn (`&&` to `\|\|`) | "leaves
the band its recall when the bar is up but draws none" |
Gates: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 46 commands for this diff (10 paths
vs merge base `39a78b15`); every one exited 0 on the merged tree,
including `check-sdui-manifest.mjs` after the re-record. `--ran`
reconciliation: "46 derived famil(ies) accounted for — 46 run, 0
NOT-MEASURED". After the last commit (changeset wording only) the
changeset-reading gates were re-run and exited 0.
## Acceptance notes
- **Placement reading.** The card says 「标题旁一个「…」菜单」. The menu sits where
the bar was, on the line above the title, and is not injected into
`page:header`; the seat's claim records this reading.
- **Status wording.** The line reuses existing bundle keys
(`approvalsInbox.statusPending`, `approvalsInbox.waitingOn`,
`approvalsInbox.approverNameSeparator`, `approvalsInbox.viaOverrideChip`
for the menu's name), so in zh-CN it reads 「待审批」 + 「等待以下审批人」 + names
rather than the card's literal 「审批中 · 等待 X」. A dedicated sentence key
would touch `ui/packages/i18n` in ten locales, outside this card's file
surface. carrier: none.
- **Menu items keep the menu open while the lever's own dialog runs**
(`preventDefault` on select), the same shape as the page header's
overflow items in `PageHeaderActions`.
- **Narrowed local run.** `ui/packages/app-shell` has about 1700 test
files; locally only the files named after the touched components ran
(listed under Verification). The full package is CI `Test Core`'s.
---
_Generated by [Claude
Code](https://claude.ai/code/session_014N3u93mMkVGhq6DBKXqx4J)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent e1aef17 commit 054652b
11 files changed
Lines changed: 907 additions & 43 deletions
File tree
- .changeset
- scripts/sdui-manifest.sources
- ui/packages
- app-shell/src/views
- __tests__
- plugin-detail/src
- __tests__
- react/src/context
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
471 | 471 | | |
472 | 472 | | |
473 | 473 | | |
474 | | - | |
475 | | - | |
476 | | - | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
477 | 483 | | |
478 | 484 | | |
479 | 485 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
| 94 | + | |
94 | 95 | | |
95 | 96 | | |
96 | 97 | | |
| |||
158 | 159 | | |
159 | 160 | | |
160 | 161 | | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
161 | 183 | | |
162 | | - | |
| 184 | + | |
163 | 185 | | |
164 | 186 | | |
165 | 187 | | |
166 | 188 | | |
167 | 189 | | |
168 | 190 | | |
169 | | - | |
| 191 | + | |
| 192 | + | |
170 | 193 | | |
171 | 194 | | |
172 | 195 | | |
| |||
286 | 309 | | |
287 | 310 | | |
288 | 311 | | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
383 | 383 | | |
384 | 384 | | |
385 | 385 | | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
386 | 392 | | |
387 | 393 | | |
388 | 394 | | |
| |||
1683 | 1689 | | |
1684 | 1690 | | |
1685 | 1691 | | |
| 1692 | + | |
| 1693 | + | |
| 1694 | + | |
| 1695 | + | |
| 1696 | + | |
| 1697 | + | |
| 1698 | + | |
| 1699 | + | |
| 1700 | + | |
| 1701 | + | |
| 1702 | + | |
| 1703 | + | |
| 1704 | + | |
1686 | 1705 | | |
1687 | 1706 | | |
1688 | 1707 | | |
| |||
3676 | 3695 | | |
3677 | 3696 | | |
3678 | 3697 | | |
| 3698 | + | |
3679 | 3699 | | |
3680 | 3700 | | |
3681 | 3701 | | |
| |||
3808 | 3828 | | |
3809 | 3829 | | |
3810 | 3830 | | |
3811 | | - | |
3812 | | - | |
| 3831 | + | |
| 3832 | + | |
| 3833 | + | |
| 3834 | + | |
| 3835 | + | |
| 3836 | + | |
| 3837 | + | |
| 3838 | + | |
| 3839 | + | |
| 3840 | + | |
| 3841 | + | |
3813 | 3842 | | |
3814 | 3843 | | |
3815 | 3844 | | |
3816 | 3845 | | |
3817 | 3846 | | |
3818 | 3847 | | |
3819 | 3848 | | |
| 3849 | + | |
| 3850 | + | |
3820 | 3851 | | |
3821 | 3852 | | |
3822 | 3853 | | |
| |||
0 commit comments