TUI can permanently lose transcript scrollback when resize lands during replay/stream (cells become incomplete; later resizes can't restore)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.5k
- PR merge metrics
- PR metrics pending
Description
Summary
The codex TUI can permanently lose transcript scrollback when a terminal resize lands while a resumed session is still replaying (or while an agent response is streaming). Resize reflow clears terminal scrollback and re-emits from transcript_cells; when the cells are incomplete at that moment (replay still in progress, stream cells not yet consolidated), the re-emitted scrollback is shorter than the transcript — and unlike #30745, the missing rows are never restored by later resizes, because the rebuild source itself (transcript_cells) is missing them.
Environment
- codex 0.147.0 TUI (main screen; runs inside a headless terminal host that owns the PTY)
- Terminal host: herdr 0.8.0 (ghostty-vt based), which resizes the shared PTY whenever the attached client's viewport changes
- Client: mobile terminal app, 56 columns; toggling the on-screen keyboard changes the viewport between 17 and 45 rows, so resizes arrive repeatedly during a session
Reproduction
codex resume <session-id>for a long session (observed: 4605-line session JSONL).- Confirm the working directory prompt with Enter; the replay starts.
- Immediately trigger a resize while the replay/stream is still settling (in my setup: split/close the pane, equivalent to the client viewport changing mid-session).
- After the prompt returns, scrollback contains only part of the transcript:
- observed
scroll + viewporttotal of 76 rows → 51 rows after one resize round-trip, stable at 51 afterwards. - The 25 missing rows never come back; repeated resizes re-emit the same 51 rows.
- observed
pane read --source recenton the surviving scrollback shows the welcome header, an interrupted-conversation notice and the prompt — no conversation history at all.
Additional observation: resuming the same session ID from two TUI instances at once makes the replay entirely empty — the resumed view shows only the welcome header and prompt, with zero history, until all instances are closed and a single instance resumes the session again.
Expected
- A resize that lands mid-replay or mid-stream should not permanently shrink scrollback: either the clear+re-emit should wait until
transcript_cellsis complete, or it should be skipped entirely when cells are known to be incomplete. - (Secondary) Resuming a session ID that is already open in another instance should not silently produce an empty replay.
Actual
In tui/src/app/resize_reflow.rs, reflow_transcript_now() returns early when transcript_cells.is_empty() (after clearing pending history lines); when cells are merely partial, the re-emit is exactly the partial set, and every later resize re-emits the same partial set. The initial replay path (begin_initial_history_replay_buffer / finish_initial_history_replay_buffer) buffers display lines separately from cells, so a resize that lands between replay inserts and the flush can commit only a prefix.
Notes
Related issues:
- #30745 — rows disappear after inline viewport height changes; restored by a terminal resize. In our case the cells themselves are lost, so a terminal resize cannot restore them (same family, worse stage).
- #24235 — scrollback corruption in Termius mobile SSH (mobile-client-specific rendering).
- #21635 — resume main view shows partial transcript due to
terminal_resize_reflow_max_rowscap. - #37635 — repeated repaints during paginated history refill on resume.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in tui/src/app/resize_reflow.rs, reading reflow_transcript_now() alongside begin_initial_history_replay_buffer and finish_initial_history_replay_buffer. Reproduce a resize during resumed replay or streaming, then trace how transcript_cells and buffered display lines are committed. Done means mid-replay or mid-stream resizes no longer permanently lose transcript rows.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100