Repository navigation
Commit 2713377
fix(app-shell): a screen-flow result screen draws Done, and closing it completes the run (#27046)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8VWCXoujnsz5LFXGfY9fWyR)_
Fixes #26902
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:屏幕流的「结果屏」(所有字段都是只读 `record_link`、且引擎说后面没有下一屏)底部只剩一个「完成」;点「完成」、右上角
X、按 Esc、点遮罩,都会以空输入恢复运行并照 Submit 的路径收尾(成功提示、刷新数据、`onComplete`)。
- 为什么改:以前这一屏还画「取消 + 提交」,取消和关闭只关掉对话框,运行一直挂起、宿主页不刷新,刚转化的线索仍显示
Qualified,「取消」还像能撤销已写入的记录。
- 风险与回滚:只动控制台 `FlowRunner` 的底部按钮与关闭处理,以及一个新判定
`isDisplayOnlyScreen`;有输入的屏、无字段的纯消息屏、引擎说还有下一屏的只读步骤、`object-form`
步骤、失败/拒绝的终态都保持原样。回滚 = revert 本 PR。
- 先看:`ui/packages/app-shell/src/views/FlowRunner.tsx` 的 `resultScreen` /
`dismiss` 两行与底部分支;`ScreenView.tsx` 的 `isDisplayOnlyScreen`;新测试
`FlowRunner.resultScreen-26902.test.tsx`。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/views/__tests__/FlowRunner.resultScreen-26902.test.tsx`,预期
11 个用例全绿。
- 需要你做的:无,席位落地。
## What changes
`FlowRunner` (the console's screen-flow dialog) now treats a **result
screen** as something the user acknowledges, not cancels:
- New predicate `isDisplayOnlyScreen(screen)` in `ScreenView.tsx`,
beside `isRecordLinkScreenField`: a flat screen that declares at least
one field, every one of them `type: 'record_link'` (matched exactly, as
the engine's resume rule matches it). `record_link` is the only member
of the spec's open screen-field `type` vocabulary that takes no input,
so this is the whole of "a screen with no input fields". Read over the
DECLARED fields, like the dialog width, so the answer never changes
while the screen is open.
- On such a screen, when it is the end of the line
(`progress.nextScreen` is `'none'`, or the server serves no `progress`),
and the run is not already terminal: the footer is one primary **Done**
(the existing `common.done` row, no new i18n row), and Done, the
dialog's X, Escape and an outside click all go through `submit()`:
resume with no inputs, then take the answer exactly as Submit does
(completion toast, `notifyDataChanged({ objectName: '*' })`,
`onComplete`; the next screen if the run pauses again; the banner if the
resume fails). A close while that resume is in flight is ignored, as on
Submit. Once a resume has failed, closing is a plain close again
(`onClose`) and Done is the retry, so an outage never traps the user in
the dialog.
- A new header section in `FlowRunner.tsx` records the behaviour and its
bounds.
Unchanged on purpose:
- A screen with any input field keeps Cancel + Submit, and its Cancel
and X still only close.
- A screen with **no fields at all** is not display-only: a message-only
step can be the go-ahead for what follows it ("Delete these records?"),
so its Cancel must stay a way out.
- A display-only step the engine says another screen follows
(`nextScreen` `'certain'` / `'possible'`) keeps Cancel + Next: there
Cancel abandons a wizard, and an X that resumed would open the next step
instead of closing.
- `object-form` steps and the terminal paths (refused end, terminal
failure: a single Close) keep their behaviour.
- `FlowRunnerProps` is unchanged, so neither launch host
(`RecordDetailView`, `useConsoleActionRuntime`) changes.
## Why
On the HotCRM `lead_conversion` flow, the last screen is three
`record_link` fields (the created account, contact and opportunity). The
dialog drew Cancel + Submit; Cancel or the close dismissed the dialog
without resuming, so the run stayed suspended, `onComplete` never fired,
and the record page kept showing the converted lead as Qualified until a
reload. "Cancel" also read as if it could undo the conversion.
The server side completes on the empty resume, so the client is not
faking completion: `screen-record-link-resume.test.ts`
(service-automation) resumes a record-link result screen with
`variables: {}` and asserts success plus the next node's write, and
`flow-screen-record-link.dogfood.test.ts` posts `inputs: {}` to the real
resume door and asserts the run completes.
## Tests
New file
`ui/packages/app-shell/src/views/__tests__/FlowRunner.resultScreen-26902.test.tsx`
(11 tests), on the `lead_conversion` shape (a last screen of three
`record_link` fields):
- the footer shows Done and no Cancel or Submit;
- Done POSTs the resume with `inputs: {}` and calls `onComplete` once
(never `onClose`), one success toast, one `'*'` data change over the
real bus;
- the X and Escape each POST the resume and call `onComplete` once;
- a close while the resume is in flight posts nothing more;
- a resume that pauses again draws the next screen (back to Cancel +
Submit);
- a retryable failed resume keeps the banner and Done;
- after a retryable failed resume, the X and Escape call `onClose` with
no second resume POST, and Done stays enabled;
- controls: a screen with any input keeps Cancel + Submit and its
Cancel/X only close; a message-only screen keeps its Cancel; a
display-only step with another screen after it keeps Cancel + Next;
- `isDisplayOnlyScreen` truth table (links only, one link, link + text,
case-mismatched `Record_Link`, empty, absent `fields`, `object-form`).
## Acceptance notes
- The `react-refresh/only-export-components` warning count in
`ScreenView.tsx` rises by one with the new export, the same warning
every other helper export in that file already carries; eslint exits 0.
- Ablation (one-shot, not kept): the `dismiss` binding's result-screen
arm removed, so it reads `const dismiss = onClose;` (the old close
behaviour), through `scripts/ablation-replace.mjs`: the X/Escape test
turns red (`onComplete` never called, 1 failed / 9 passed), then the
file is restored to the HEAD blob with `git diff HEAD` empty.
- Second ablation (on `00147cd2`): the dismiss guard `&& !resumeError`
removed: the "after a failed resume the close only closes" test turns
red (1 failed / 10 passed), then restored to the HEAD blob
`6c27e23d5741`.
## Verification
All readings on head `00147cd2`, base `origin/main` `cb8988ee`.
<details><summary>Local test and type-check runs</summary>
- `pnpm --dir ui exec vitest run
packages/app-shell/src/views/__tests__/FlowRunner.resultScreen-26902.test.tsx`:
Test Files 1 passed, Tests 11 passed.
- 38 affected files (every `FlowRunner*`, `ScreenView*` and
`ScreenPreview*` test, the flow simulator parity, `flowResponse`,
`flow-envelope.ratchet`, `spec-symbol-parity`, the flow and
refused-launch tests of both hosts, `FlowRunsPage`): Test Files 38
passed, Tests 372 passed.
- `pnpm --filter @object-ui/app-shell run type-check`: exit 0.
- `eslint --no-inline-config` on the three changed files: exit 0, 0
errors.
- Ablation: `os-verify-lock: VERDICT command-exit 1`, `Tests 1 failed |
9 passed (10)`, restore `blob == HEAD (318486c)`.
</details>
<details><summary>Derived gates (dispatch-gates --commands, reconciled
with --ran)</summary>
24 derived, 24 run, 0 NOT-MEASURED, 0 UNRUN; every one exit 0:
`check-closing-keyword-parity` (+ `--self-test`),
`check-comment-mask-corpus` (18647 files, 0 disagree),
`check-sdui-manifest` (+ `--self-test`), `vitest run scripts/` under
`ui/` (176 files passed), spec `check:duration-unit-keys`, and
`check:cross-package-test-inputs`, `dispatcher-error-vocabulary`,
`driver-memory-census`, `engine-double-contract`, `gitlink-declared`,
`nul-bytes`, `published-files`, `query-options-erasure`,
`refd-timer-probe`, `skill-identifier-liveness`, `test-source-alias`,
`tier-file-adoption`, `type-check-coverage`, `type-check-debt`,
`undeclared-dep-imports`, `watch-hint-literal`, `where-matcher`. Also
run outside the derivation: `ui` `check:new-line-citations` (0 new) and
`check:i18n-keys`, both exit 0.
</details>
Changeset: none. `@object-ui/app-shell` is `private: true`, so the diff
publishes nothing; labelled `skip-changeset`.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(not touched)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes. (changed files linted
locally; repo-wide lint is CI's)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset. (publishes
nothing)
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (not touched)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbWYBBF9CLagvsKZNkN)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent f86106b commit 2713377
3 files changed
Lines changed: 302 additions & 1 deletion
File tree
- ui/packages/app-shell/src/views
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
276 | 276 | | |
277 | 277 | | |
278 | 278 | | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
279 | 308 | | |
280 | 309 | | |
281 | 310 | | |
| |||
304 | 333 | | |
305 | 334 | | |
306 | 335 | | |
| 336 | + | |
307 | 337 | | |
308 | 338 | | |
309 | 339 | | |
| |||
736 | 766 | | |
737 | 767 | | |
738 | 768 | | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
739 | 775 | | |
740 | 776 | | |
741 | | - | |
| 777 | + | |
742 | 778 | | |
743 | 779 | | |
744 | 780 | | |
| |||
829 | 865 | | |
830 | 866 | | |
831 | 867 | | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
832 | 874 | | |
833 | 875 | | |
834 | 876 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
155 | 155 | | |
156 | 156 | | |
157 | 157 | | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
158 | 177 | | |
159 | 178 | | |
160 | 179 | | |
| |||
| 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 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 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 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
0 commit comments