Codex TUI scrollback rows can disappear after inline viewport height changes
Summary
Codex TUI can lose finalized assistant message rows from terminal scrollback after the inline bottom viewport changes height during or after streaming. The persisted conversation and Codex-rendered rows are complete, but the terminal document/scrollback can omit a small number of rows until a real terminal resize forces transcript-backed reflow.
The issue was reproduced on Windows Terminal at default geometry. My local setup uses a public patched Windows Terminal fork ("PWT") for automation, mainly to drive wt send-input and repeatable captures. The user-visible failure is still ordinary Windows Terminal/Codex TUI behavior: maximizing the window repairs the text because Codex replays scrollback from transcript source during terminal resize.
Environment
- OS: Windows 10 21H2
- Terminal: Windows Terminal; local patched WT/PWT fork used for automation and repeatable captures
- Codex source:
cfead68e5d3984b247cf0758e3e53b19165de848 - Commit:
[codex] disable Nagle on Rendezvous WebSockets (#30269) - Dev build:
codex-cli 0.0.0 - Repro geometry observed in traces:
120x30
Reproduction
Use an assistant message containing this section:
- Gateway host updated
- Patched installed host-current using the supported patch-host flow.
- New host process: PID 11548.
- New host build: 1.0.0+db20263cf34f7a05de4fe00616499ed6dd63fa7a.
- Existing Codex CLI sessions were not restarted.
In a default-height Windows Terminal Codex session, ask Codex to print the fixture exactly.
Expected
Once Codex has emitted finalized assistant rows into scrollback, app-internal bottom pane height changes should not cause previously emitted rows to disappear. If the inline viewport grows or shrinks over emitted history, Codex should replay scrollback from transcript source, as it already does for terminal resize.
Actual
Final default-height UIA document_range showed:
Gateway host updatedNew host build...Existing Codex CLI sessions...
It omitted:
Patched installed host-current...New host process: PID 11548.
Maximize/restore made the missing rows reappear.
Evidence
Evidence pack files:
screenshots/00-streaming-gateway-bullets-visible.pngscreenshots/01-finalized-gateway-bullets-clipped.pngscreenshots/01-failing-default-height-final.pnguia-excerpts/01-failing-default-height-final.txttrace-excerpts/01-failing-marker-rows.tsvscreenshots/02-resize-repaired-after-maximize.pnguia-excerpts/02-resize-repaired-after-maximize.txttrace-excerpts/02-failing-resize-reflow-repair.tsvscreenshots/03-fixed-default-height-final.pnguia-excerpts/03-fixed-default-height-final.txttrace-excerpts/03-fixed-height-change-reflow.tsvscreenshots/04-control-minimal-ansi-replay.pnguia-excerpts/04-control-minimal-ansi-replay.txt
Codex trace from the failing run proves the missing rows reached terminal insertion:
insert_history_row markers=gateway_header text= - Gateway host updated
insert_history_row markers=patched_host text= - Patched installed host-current using the supported patch-host flow.
insert_history_row markers=host_pid text= - New host process: PID 11548.
insert_history_row markers=host_build text= - New host build: 1.0.0+db20263cf34f7a05de4fe00616499ed6dd63fa7a.
insert_history_row markers=existing_sessions text= - Existing Codex CLI sessions were not restarted.
The same run showed resize repair after maximize because transcript-backed reflow replayed the rows.
The key visual evidence is the original screenshot pair captured from the live session:
- while the response was still rendering, all four child bullets under
Gateway host updatedwere visible; - after final render, only
New host buildandExisting Codex CLI sessionsremained visible in that section.
Relationship to #27644
GitHub flagged #27644 as a potential duplicate. This report appears related to the same broad class of disappearing transcript/scrollback rows that repair after resize. The difference is that this reproduction is from Windows Terminal/PWT rather than an xterm.js host, and the local diagnostics point at app-internal inline viewport height changes needing transcript-backed reflow.
If maintainers consider this the same root cause as #27644, this issue can be merged there; otherwise this should help track the Windows Terminal/PWT and viewport-height-change variant separately.
Root Cause Hypothesis
Codex resize reflow currently handles terminal width/height changes, but app-internal inline viewport height changes can also clear or shift rows where terminal scrollback history was emitted.
When the bottom viewport height changes over emitted history, Codex needs to schedule a source-backed transcript reflow on the next frame. Otherwise rows can be lost until a real terminal resize triggers the existing reflow path.
Candidate Fix
Track the last desired inline viewport height in App. After drawing a frame where that desired height changes and scrollback history has already been emitted, schedule immediate transcript reflow. If the change happens during streaming, mark it as stream-time reflow so consolidation can still repair with source-backed finalized cells.
Also keep the viewport bottom-aligned when it shrinks after having been bottom-aligned.
Patch sketch:
last_inline_viewport_height: Option<u16>,
let desired_height = self.chat_widget.desired_height(tui.terminal.size()?.width);
let previous_height = self.last_inline_viewport_height.replace(desired_height);
let viewport_height_changed =
previous_height.is_some_and(|previous| previous != desired_height);
tui.draw_with_resize_reflow(desired_height, |frame| {
// existing render
})?;
if viewport_height_changed && self.has_emitted_history_lines && self.overlay.is_none() {
if self.should_mark_reflow_as_stream_time() {
self.transcript_reflow.mark_resize_requested_during_stream();
}
self.transcript_reflow.schedule_immediate();
tui.frame_requester().schedule_frame();
}
let viewport_height_shrank = area.height < previous_area.height;
// ...
} else if viewport_was_bottom_aligned && (terminal_height_grew || viewport_height_shrank) {
area.y = size.height - area.height;
}
Validation
Passed:
cargo test -p codex-tui streaming::controller::tests::affected_fixture_streamed_rows_match_finalized_markdown_rows -- --nocapture
Live validation:
- failing baseline reproduced at default height;
- required-final-reflow-only candidate did not fix;
- raw-insert-only candidate did not fix;
- bottom-align-only candidate did not fix;
- app-level height-change reflow candidate fixed the stronger
document_rangerow loss at default height.
Attachments
I have attached a sanitized public evidence bundle plus the key screenshots. The inline screenshots are ordered as:
- all gateway bullets visible while the response was still rendering;
- finalized response with two gateway child bullets missing;
- maximize/restore repair showing the missing rows restored.
The bundle contains the README, hash index, sensitivity review, UIA excerpts, trace excerpts, and the candidate fix sketch.
codex-message-truncation-windows-terminal-evidence-20260630.zip
1. Streaming: all gateway bullets visible
<img width="1110" height="618" alt="Streaming view with all Gateway host updated bullets visible" src="https://github.com/user-attachments/assets/feee8933-2544-442b-ac00-805eeed94447" />
2. Finalized: two gateway bullets missing
<img width="1119" height="626" alt="Finalized view with two Gateway host updated bullets missing" src="https://github.com/user-attachments/assets/fa8ef139-0e41-4f3b-89c5-368588b1b387" />
3. Resize repair: missing rows restored after maximize
<img width="1616" height="876" alt="Resize-repaired view with missing Gateway host updated bullets restored" src="https://github.com/user-attachments/assets/43b32041-1477-434f-bf10-19bd8b1b9e12" />
Notes
The local diagnostic trace helper should not be upstreamed as-is. A PR should keep only the minimal fix and focused tests for app-internal inline viewport height changes over emitted history.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗