fix(tui): force repaint final chat events#87348
Conversation
|
Codex review: needs maintainer review before merge. Reviewed May 27, 2026, 1:52 PM ET / 17:52 UTC. Summary PR surface: Source 0, Tests +22. Total +22 across 2 files. Reproducibility: Do we have a high-confidence way to reproduce the issue? Yes at source level: current main has three final-event early returns that change visible TUI state and only request an unforced repaint, and the pi-tui contract shows Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge Security Review detailsBest possible solution: Land the focused forced-repaint fix after required CI completes, keeping the proof scoped to TUI repaint behavior rather than Gateway, provider, session persistence, or live streaming coverage. Do we have a high-confidence way to reproduce the issue? Do we have a high-confidence way to reproduce the issue? Yes at source level: current main has three final-event early returns that change visible TUI state and only request an unforced repaint, and the pi-tui contract shows Is this the best way to solve the issue? Is this the best way to solve the issue? Yes, the PR is the narrowest maintainable fix because it changes only the affected early returns and adds focused regression assertions without altering unrelated render paths. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 62550710bfec. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source 0, Tests +22. Total +22 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
ClawSweeper PR egg ✨ Hatched: 🥚 common Cosmic Signal Puff Hatch commandComment Hatchability rules:
Rarity: 🥚 common. What is this egg doing here?
|
|
Added stronger real terminal proof to the PR body. What changed in proof:
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper automerge |
|
🦞🔧
Draft PRs stay fix-only until GitHub marks them ready for review. Pause with Automerge progress:
|
|
ClawSweeper 🐠 reef update Thanks for the contribution here. ClawSweeper tried the original lane first, but branch permissions blocked the push, so a replacement PR is carrying the fix forward. Why replacement: ClawSweeper could not update the source PR branch directly; GitHub did not grant sufficient push rights to the bot for that branch.
fish notes: model gpt-5.5, reasoning high; reviewed against 8ac3192. |
Summary
handleChatEventfinal-event early returns that otherwise only mutate state, drop assistant output, or add command/system text.tui-event-handlersregression assertions.Fixes #86871.
Root Cause
@earendil-works/pi-tuiexposesrequestRender(force?: boolean), and passingtrueresets the previous-line diff state before scheduling a repaint. The TUI already uses that forced path in other full-refresh cases, but the three final-event early returns insrc/tui/tui-event-handlers.tsonly calledrequestRender()and could leave the terminal unchanged until another action forced a repaint.Verification
pnpm install --frozen-lockfilenode scripts/run-vitest.mjs src/tui/tui-event-handlers.test.tsnode scripts/run-vitest.mjs run --config test/vitest/vitest.tui-pty.config.tsOPENCLAW_TUI_PTY_INCLUDE_LOCAL=1 node scripts/run-vitest.mjs run --config test/vitest/vitest.tui-pty.config.tsOPENCLAW_TUI_PTY_INCLUDE_LOCAL=1 OPENCLAW_TUI_PTY_MIRROR_PATH=<tmpfile> node scripts/run-vitest.mjs run --config test/vitest/vitest.tui-pty.config.ts src/tui/tui-pty-local.e2e.test.tsrunTui()loop and a PTY-backed fixture backendgit diff --checkReal behavior proof
Behavior addressed: TUI final chat events that finish through the local BTW empty-final path, the no-message final path, or the command-message final path now request a forced repaint with
tui.requestRender(true). This directly addresses the reported symptom where final assistant/system output could remain off-screen until another action, such as toggling/verbose, forced a full redraw.Real environment tested: local macOS source worktree with repo dependencies installed from the lockfile. I exercised the changed code in three layers: focused handler regression coverage, the scoped fake-backend PTY harness, and a real
tui --localprocess under a pseudo-terminal with only the model HTTP endpoint mocked. I also ran a temporary PTY proof that starts the realrunTui()loop and emits acommand: truefinal chat event, which is one of the patched early-return branches.Exact steps or command run after this patch:
pnpm install --frozen-lockfile;node scripts/run-vitest.mjs src/tui/tui-event-handlers.test.ts;node scripts/run-vitest.mjs run --config test/vitest/vitest.tui-pty.config.ts;OPENCLAW_TUI_PTY_INCLUDE_LOCAL=1 node scripts/run-vitest.mjs run --config test/vitest/vitest.tui-pty.config.ts;OPENCLAW_TUI_PTY_INCLUDE_LOCAL=1 OPENCLAW_TUI_PTY_MIRROR_PATH=<tmpfile> node scripts/run-vitest.mjs run --config test/vitest/vitest.tui-pty.config.ts src/tui/tui-pty-local.e2e.test.ts; temporary command-final PTY proof script;git diff --check.Evidence after fix: the focused handler suite passed with 57 tests, including assertions that all three #86871 early-return branches call
requestRender(true). The fake-backend TUI PTY harness passed with 13 tests. The local-backend TUI PTY lane passed with 14 tests, and the mirroredtui --localterminal output showed the final model response visible in the terminal after submitting one prompt, with no/verbosecommand sent. The temporary command-final PTY proof showedVISIBLE_COMMAND_FINAL_PROOFrendered on the terminal after acommand: truefinal chat event, also with no/verbosecommand sent. The installed@earendil-works/pi-tuicontract was checked:dist/tui.d.tsdeclaresrequestRender(force?: boolean), anddist/tui.jsresetspreviousLines, previous dimensions, cursor rows, and viewport state whenforceis true.Copied redacted terminal proof from the real
tui --localPTY run, ANSI stripped:Copied redacted terminal proof from the command-final PTY run, ANSI stripped. This proof hits the patched
isCommandMessage(evt.message)early-return branch:Observed result after fix: the final output was visible in the terminal immediately after the final event in both PTY captures, and the command-final proof confirmed no
/verbosecommand appeared in the transcript.What was not tested: a real external model provider was not used. The local PTY smoke mocks only the model HTTP endpoint, and the command-final proof uses a fixture backend to emit the specific patched final-event shape. Gateway network transport, real provider behavior, session persistence, and live streaming were not exercised by this proof.