Windows: resumed user messages render without their background because the transcript is styled before the deferred default-color probe
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
What version of Codex CLI is running?
codex-cli 0.153.4
What subscription do you have?
Pro
Which model were you using?
gpt-6-astra (xhigh)
What platform is your computer?
Microsoft Windows NT 10.0.26200.0 x64 (Windows 11 Pro)
What terminal emulator and version are you using (if applicable)?
Reproduced in two hosts: PowerShell 7 in its own console window, and Runner (a GPUI app that embeds alacritty_terminal over ConPTY / OpenConsole 1.24.260303001). No multiplexer.
Codex doctor report
not run
What issue are you seeing?
On Windows, after codex resume, the user messages replayed from the resumed transcript render as plain text with no background block, while the composer at the bottom draws its gray block as usual. Messages sent after the resume get the block. Any change of the terminal width makes the resumed messages get their block back. This is Windows-only by construction: on Unix, init() runs the default-color probe synchronously before anything is drawn.
Root cause (verified against rust-v0.153.4 sources and a ConPTY capture; details below): on Windows the default-color probe is deferred until after protected startup screens, but the resumed transcript is styled before that probe runs, so user_message_style() sees default_bg() == None and emits no background. Those lines are written to scrollback once and never restyled; the composer is restyled every frame, so it picks the color up as soon as the probe completes.
Where it happens
tui/src/tui.rs:503— on Windows,init()callsset_default_colors_from_startup_probe(None)on purpose ("OSC replies can arrive after their deadline. Do not issue terminal queries before directory trust and other protected startup screens have finished..."). The real probe isprobe_default_colors_after_protected_startup().tui/src/style.rs:65-70—user_message_style_for(None)returnsStyle::default(), i.e. no background at all (not a fallback color).tui/src/app/startup.rs:634—drain_active_thread_eventsreplays the resumed thread; each cell goes throughinsert_history_cell(tui/src/app/history_ui.rs:24), which renders the display lines right away viadisplay_hyperlink_lines_for_mode(history_ui.rs:35,resize_reflow.rs:87). That is whereuser_message_style()is evaluated, with the palette stillNone.tui/src/app/startup.rs:657— only after the drain doesprobe_default_colors_after_protected_startup()run.tui/src/app/resize_reflow.rs:191(insert_history_cell_lines_with_initial_replay_buffer) keeps the already-styled lines inretained_lines;finish_initial_history_replay_buffer(resize_reflow.rs:156) writes them to the terminal later. So the bytes reach the terminal after the OSC reply, but they were styled before it.
Timeline from Codex's own log (a resume inside Runner; logs_2.sqlite):
+0.000 INFO codex_tui::app::startup tui startup initial frame scheduled duration_ms=3933 bootstrap_ms=3633 thread_and_widget_ms=185 initial_session_ms=113 (tui/src/app/startup.rs:668)
+0.105 DEBUG codex_tui::terminal_probe::imp using Windows terminal OSC default colors foreground=(220, 220, 224) background=(21, 22, 27) (tui/src/terminal_probe/windows.rs:43)
+0.105 INFO codex_tui::tui terminal default color probe completed duration_ms=0 default_colors=true (tui/src/tui.rs:536)
The terminal answered the query instantly (duration_ms=0); the probe simply ran after the transcript had been styled.
Byte-level capture (real codex.exe under ConPTY in a small harness that models the output with alacritty_terminal and answers OSC 10/11 in the same read; env TERM=xterm-256color, COLORTERM=truecolor, 149x49; resuming a thread whose only user message is zsdfsadf):
| t | event |
|---|---|
| 279 ms | Codex writes › zsdfsadf — the chunk contains no 48;2; SGR |
| 283 ms | Codex queries OSC 10;? / OSC 11;?; harness replies rgb:1515/1616/1b1b in the same millisecond |
| 4013 ms | harness resizes the PTY 149 → 120 columns |
| 4093 ms | resize reflow re-emits the transcript; › zsdfsadf now arrives with SGR 48;2;49;49;54 (the 12% white blend of the reported background) as a full three-row block |
Final grid before the resize: the resumed message row has the default background; the composer rows have rgb(49,49,54). After the resize both do.
What steps can reproduce the bug?
- On Windows (PowerShell 7 console or Windows Terminal), run
codex, send any message such ashello, wait for the reply, exit. - Run
codex resume --last. - Look at the replayed
› helloline: no background block. The composer below it has the block. - Send a new message: it gets the block.
- Resize the window width by one column: the replayed
› hellonow has the block too.
Thread used for the capture above: 01a08124-acf3-7691-a4f2-d47a955b64eb.
What is the expected behavior?
Resumed user messages render with the same background as the composer and as newly sent messages, matching macOS/Linux.
Additional information
Possible fixes, from what the code already has:
- After
probe_default_colors_after_protected_startup()sets real colors, re-render the transcript fromtranscript_cellsif any history was styled before the probe.schedule_immediate_resize_reflowalready does exactly this on width changes (that is what step 5 above exercises), so gating a one-time reflow on "probe completed after history was emitted" would fix it with existing machinery. - Alternatively, when the Windows probe is still pending during the initial replay, set
render_from_transcript_tailon the initial replay buffer sofinish_initial_history_replay_buffergoes through the reflow path (styling from cells after the probe) instead of writing lines that were styled withNone. - Or run the probe before
drain_active_thread_eventswheneverready_for_terminal_color_probeis already true at that point (no protected request queued), keeping the deferral only for the cases the comment intui.rs:503is about.
Not related to the terminal's reply latency: the capture above answers within the same millisecond and the log reports duration_ms=0, and the defect reproduces the same way in the stock PowerShell 7 console.
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
Trace the Windows startup flow in tui/src/tui.rs and tui/src/app/startup.rs, then follow resumed history through history_ui.rs and resize_reflow.rs. Reproduce with codex resume on Windows and inspect when user_message_style() runs relative to probe_default_colors_after_protected_startup(). Done means resumed messages receive the same background as the composer without requiring a terminal resize.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100