TUI finalized assistant messages can render blank Markdown lines as two viewport rows
<!-- Title: TUI finalized assistant messages can render blank Markdown lines as two viewport rows -->
What version of Codex CLI is running?
codex-cli 0.144.5 installed with the standalone Windows installer. The runtime reproduction was captured on this version; the source-level regression is also present in the current rust-v0.144.6 release tag.
What subscription do you have?
Pro
Which model were you using?
gpt-5.6-sol
What platform is your computer?
Microsoft Windows NT 10.0.19045.0 x64 (Windows 10 Pro 22H2)
What terminal emulator and version are you using (if applicable)?
Windows Terminal 1.24.11321.0 with PowerShell 7.6.3 (Core, x64). No terminal multiplexer.
Codex doctor report
Relevant results from codex doctor --json are summarized below; unrelated entries are omitted.
{
"schemaVersion": 1,
"codexVersion": "0.144.5",
"checks": {
"installation": {
"status": "ok",
"summary": "installation looks consistent",
"installContext": "standalone (windows)"
},
"runtime.provenance": {
"status": "ok",
"summary": "running standalone on windows-x86_64",
"platform": "windows-x86_64",
"version": "0.144.5"
},
"system.environment": {
"status": "ok",
"os": "Windows 10.0.19045 (Windows 10 Pro) [64-bit]",
"osLanguage": "ko-KR"
},
"terminal.env": {
"status": "ok",
"terminal": "Windows Terminal",
"WT_SESSION": "present",
"consoleInputCodePage": "949",
"consoleOutputCodePage": "65001"
}
}
}
What issue are you seeing?
After an assistant response is finalized, an intended blank Markdown line can occupy two terminal viewport rows instead of one. I notice this most clearly after pressing Ctrl+T to open the transcript overlay: spacing between finalized paragraphs, lists, or tables becomes visibly larger than the source Markdown requires.
For the logical message first\n\nsecond, the finalized AgentMarkdownCell produces a whitespace-only line containing two spaces instead of a structurally empty line:
actual: ["• first", " ", " second"]
expected: ["• first", "", " second"]
Ratatui can measure that whitespace-only line as two viewport rows. PR #26636 fixed the same row-height mechanism for the transient StreamingAgentTailCell, but the finalized AgentMarkdownCell still returns the result of prefix_hyperlink_lines(...) without applying that normalization.
What steps can reproduce the bug?
- Start Codex CLI in Windows Terminal.
- Ask for a response containing paragraphs, lists, or tables separated by single blank Markdown lines.
- Wait for the response to finish so the assistant message is finalized.
- Press
Ctrl+Tto open the transcript overlay. - Observe that some intended single blank lines occupy two viewport rows.
The source-level reproduction is deterministic:
let cell = AgentMarkdownCell::new("first\n\nsecond".to_string(), &test_cwd());
let lines = cell.display_lines(/*width*/ 80);
insta::assert_snapshot!(render_lines(&lines).join("\n"), @"
• first
second
");
assert_eq!(cell.desired_transcript_height(/*width*/ 80), 3);
On unmodified main at b8b61bc69, the snapshot fails because the actual blank line is " ". With the candidate change, the snapshot and height assertion both pass.
What is the expected behavior?
One blank Markdown line should remain structurally empty and occupy exactly one viewport row in both streaming and finalized/transcript rendering. Opening or closing the transcript overlay should not change the vertical spacing of a finalized assistant message.
Additional information
The latest stable release during validation was 0.144.6. Its rust-v0.144.6 source tag still returns prefix_hyperlink_lines(...) directly from AgentMarkdownCell::display_hyperlink_lines, so the finalized path does not contain the normalization already used by the streaming path.
Related but distinct prior fix: https://github.com/openai/codex/pull/26636
Candidate branch: https://github.com/starriet9/codex/tree/fix/transcript-finalized-blank-rows
Candidate commit: https://github.com/starriet9/codex/commit/b445b9d6082661a3eb97a42be317455f92cb3eae
The candidate extracts the existing whitespace-only HyperlinkLine normalization into a helper and applies it after prefixing in both StreamingAgentTailCell and finalized AgentMarkdownCell. Nonblank prefixes and styles are unchanged, and impossible hyperlink metadata is cleared only for whitespace-only lines.
Validation on Windows:
Unmodified main plus the regression test: failed; actual blank line was " "
Candidate targeted regression test: passed
history_cell::tests: 107 passed, 0 failed, 2 ignored
required_stream_reflow_during_capped_initial_replay*: 2 passed, 0 failed
cargo fmt -p codex-tui -- --check: passed
cargo clippy --offline -p codex-tui --all-targets -- -D warnings: passed
cargo build --offline -p codex-cli: passed
I have not opened a pull request because docs/contributing.md says external pull requests are by invitation only. The focused branch is ready if maintainers want a PR.