fix(tui): avoid duplicated streamed markdown lines#26618
Conversation
0c9585b to
31bd9c3
Compare
## Summary During assistant-message streaming, blank markdown lines in the transient active tail were prefixed with two spaces. Ratatui measured those whitespace-only lines as two viewport rows, so list- and table-heavy answers showed doubled vertical gaps while streaming and then visibly compacted when finalized into scrollback. - keep whitespace-only `StreamingAgentTailCell` lines structurally empty while preserving nonblank message prefixes - clear impossible hyperlink metadata when normalizing a blank tail line - add an inline snapshot and height regression proving one blank markdown line occupies one viewport row Related to #26618, but fixes a separate live-tail row-height issue rather than stale committed markdown content. ## How to Test Recommended before/after reproduction: 1. Start the latest Codex build without this change. 2. Submit this exact prompt: > Send 20 different lists: bullets vs numbered, simple vs complex with paragraphs in between items, etc. Intertwine them with some tables and some paragraphs. 3. While the answer streams, observe duplicated vertical gaps around list items and paragraphs. When the answer finishes, observe the spacing compact. 4. Start this branch with `just c` and submit the same prompt. 5. Confirm each intended blank markdown line occupies one terminal row throughout streaming and that the spacing does not compact or jump when the answer finishes. 6. As a focused regression, verify the sections after the first table, especially loose lists with paragraphs between items; those blank rows should remain stable throughout streaming. Targeted tests: - `just test -p codex-tui streaming_agent_tail_blank_line_uses_one_viewport_row` - `just test -p codex-tui history_cell::tests` ## Test Notes - Verified the exact prompt above in a real tmux TUI using latest Codex and this branch as the before/after comparison. - The full `just test -p codex-tui` run completed 2,782 of 2,784 tests successfully. Two unrelated guardian feature-flag tests fail reproducibly in isolation because the expected `OverrideTurnContext` message is absent. - `just argument-comment-lint` is blocked locally by the existing Bazel `compiler-rt` missing-header glob error; the touched Rust diff was inspected manually for opaque positional literals.
|
@codex review |
etraut-openai
left a comment
There was a problem hiding this comment.
I did a local review, and codex found several issues that look like real bugs.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31bd9c3fe9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 072b439810
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 896c79ee12
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5c720ae96d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5c720ae96d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bc4d51d03b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5f38c6cda1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 664fdd2d6e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1fc298a5c7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cf8935be51
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 032c5dfe54
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6e7fc95d59
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
## Summary During assistant-message streaming, blank markdown lines in the transient active tail were prefixed with two spaces. Ratatui measured those whitespace-only lines as two viewport rows, so list- and table-heavy answers showed doubled vertical gaps while streaming and then visibly compacted when finalized into scrollback. - keep whitespace-only `StreamingAgentTailCell` lines structurally empty while preserving nonblank message prefixes - clear impossible hyperlink metadata when normalizing a blank tail line - add an inline snapshot and height regression proving one blank markdown line occupies one viewport row Related to openai#26618, but fixes a separate live-tail row-height issue rather than stale committed markdown content. ## How to Test Recommended before/after reproduction: 1. Start the latest Codex build without this change. 2. Submit this exact prompt: > Send 20 different lists: bullets vs numbered, simple vs complex with paragraphs in between items, etc. Intertwine them with some tables and some paragraphs. 3. While the answer streams, observe duplicated vertical gaps around list items and paragraphs. When the answer finishes, observe the spacing compact. 4. Start this branch with `just c` and submit the same prompt. 5. Confirm each intended blank markdown line occupies one terminal row throughout streaming and that the spacing does not compact or jump when the answer finishes. 6. As a focused regression, verify the sections after the first table, especially loose lists with paragraphs between items; those blank rows should remain stable throughout streaming. Targeted tests: - `just test -p codex-tui streaming_agent_tail_blank_line_uses_one_viewport_row` - `just test -p codex-tui history_cell::tests` ## Test Notes - Verified the exact prompt above in a real tmux TUI using latest Codex and this branch as the before/after comparison. - The full `just test -p codex-tui` run completed 2,782 of 2,784 tests successfully. Two unrelated guardian feature-flag tests fail reproducibly in isolation because the expected `OverrideTurnContext` message is absent. - `just argument-comment-lint` is blocked locally by the existing Bazel `compiler-rt` missing-header glob error; the touched Rust diff was inspected manually for opaque positional literals.
## Summary During assistant-message streaming, blank markdown lines in the transient active tail were prefixed with two spaces. Ratatui measured those whitespace-only lines as two viewport rows, so list- and table-heavy answers showed doubled vertical gaps while streaming and then visibly compacted when finalized into scrollback. - keep whitespace-only `StreamingAgentTailCell` lines structurally empty while preserving nonblank message prefixes - clear impossible hyperlink metadata when normalizing a blank tail line - add an inline snapshot and height regression proving one blank markdown line occupies one viewport row Related to openai#26618, but fixes a separate live-tail row-height issue rather than stale committed markdown content. ## How to Test Recommended before/after reproduction: 1. Start the latest Codex build without this change. 2. Submit this exact prompt: > Send 20 different lists: bullets vs numbered, simple vs complex with paragraphs in between items, etc. Intertwine them with some tables and some paragraphs. 3. While the answer streams, observe duplicated vertical gaps around list items and paragraphs. When the answer finishes, observe the spacing compact. 4. Start this branch with `just c` and submit the same prompt. 5. Confirm each intended blank markdown line occupies one terminal row throughout streaming and that the spacing does not compact or jump when the answer finishes. 6. As a focused regression, verify the sections after the first table, especially loose lists with paragraphs between items; those blank rows should remain stable throughout streaming. Targeted tests: - `just test -p codex-tui streaming_agent_tail_blank_line_uses_one_viewport_row` - `just test -p codex-tui history_cell::tests` ## Test Notes - Verified the exact prompt above in a real tmux TUI using latest Codex and this branch as the before/after comparison. - The full `just test -p codex-tui` run completed 2,782 of 2,784 tests successfully. Two unrelated guardian feature-flag tests fail reproducibly in isolation because the expected `OverrideTurnContext` message is absent. - `just argument-comment-lint` is blocked locally by the existing Bazel `compiler-rt` missing-header glob error; the touched Rust diff was inspected manually for opaque positional literals.
## Summary During assistant-message streaming, blank markdown lines in the transient active tail were prefixed with two spaces. Ratatui measured those whitespace-only lines as two viewport rows, so list- and table-heavy answers showed doubled vertical gaps while streaming and then visibly compacted when finalized into scrollback. - keep whitespace-only `StreamingAgentTailCell` lines structurally empty while preserving nonblank message prefixes - clear impossible hyperlink metadata when normalizing a blank tail line - add an inline snapshot and height regression proving one blank markdown line occupies one viewport row Related to openai#26618, but fixes a separate live-tail row-height issue rather than stale committed markdown content. ## How to Test Recommended before/after reproduction: 1. Start the latest Codex build without this change. 2. Submit this exact prompt: > Send 20 different lists: bullets vs numbered, simple vs complex with paragraphs in between items, etc. Intertwine them with some tables and some paragraphs. 3. While the answer streams, observe duplicated vertical gaps around list items and paragraphs. When the answer finishes, observe the spacing compact. 4. Start this branch with `just c` and submit the same prompt. 5. Confirm each intended blank markdown line occupies one terminal row throughout streaming and that the spacing does not compact or jump when the answer finishes. 6. As a focused regression, verify the sections after the first table, especially loose lists with paragraphs between items; those blank rows should remain stable throughout streaming. Targeted tests: - `just test -p codex-tui streaming_agent_tail_blank_line_uses_one_viewport_row` - `just test -p codex-tui history_cell::tests` ## Test Notes - Verified the exact prompt above in a real tmux TUI using latest Codex and this branch as the before/after comparison. - The full `just test -p codex-tui` run completed 2,782 of 2,784 tests successfully. Two unrelated guardian feature-flag tests fail reproducibly in isolation because the expected `OverrideTurnContext` message is absent. - `just argument-comment-lint` is blocked locally by the existing Bazel `compiler-rt` missing-header glob error; the touched Rust diff was inspected manually for opaque positional literals.
## Summary During assistant-message streaming, blank markdown lines in the transient active tail were prefixed with two spaces. Ratatui measured those whitespace-only lines as two viewport rows, so list- and table-heavy answers showed doubled vertical gaps while streaming and then visibly compacted when finalized into scrollback. - keep whitespace-only `StreamingAgentTailCell` lines structurally empty while preserving nonblank message prefixes - clear impossible hyperlink metadata when normalizing a blank tail line - add an inline snapshot and height regression proving one blank markdown line occupies one viewport row Related to openai#26618, but fixes a separate live-tail row-height issue rather than stale committed markdown content. ## How to Test Recommended before/after reproduction: 1. Start the latest Codex build without this change. 2. Submit this exact prompt: > Send 20 different lists: bullets vs numbered, simple vs complex with paragraphs in between items, etc. Intertwine them with some tables and some paragraphs. 3. While the answer streams, observe duplicated vertical gaps around list items and paragraphs. When the answer finishes, observe the spacing compact. 4. Start this branch with `just c` and submit the same prompt. 5. Confirm each intended blank markdown line occupies one terminal row throughout streaming and that the spacing does not compact or jump when the answer finishes. 6. As a focused regression, verify the sections after the first table, especially loose lists with paragraphs between items; those blank rows should remain stable throughout streaming. Targeted tests: - `just test -p codex-tui streaming_agent_tail_blank_line_uses_one_viewport_row` - `just test -p codex-tui history_cell::tests` ## Test Notes - Verified the exact prompt above in a real tmux TUI using latest Codex and this branch as the before/after comparison. - The full `just test -p codex-tui` run completed 2,782 of 2,784 tests successfully. Two unrelated guardian feature-flag tests fail reproducibly in isolation because the expected `OverrideTurnContext` message is absent. - `just argument-comment-lint` is blocked locally by the existing Bazel `compiler-rt` missing-header glob error; the touched Rust diff was inspected manually for opaque positional literals.
## Summary During assistant-message streaming, blank markdown lines in the transient active tail were prefixed with two spaces. Ratatui measured those whitespace-only lines as two viewport rows, so list- and table-heavy answers showed doubled vertical gaps while streaming and then visibly compacted when finalized into scrollback. - keep whitespace-only `StreamingAgentTailCell` lines structurally empty while preserving nonblank message prefixes - clear impossible hyperlink metadata when normalizing a blank tail line - add an inline snapshot and height regression proving one blank markdown line occupies one viewport row Related to openai#26618, but fixes a separate live-tail row-height issue rather than stale committed markdown content. ## How to Test Recommended before/after reproduction: 1. Start the latest Codex build without this change. 2. Submit this exact prompt: > Send 20 different lists: bullets vs numbered, simple vs complex with paragraphs in between items, etc. Intertwine them with some tables and some paragraphs. 3. While the answer streams, observe duplicated vertical gaps around list items and paragraphs. When the answer finishes, observe the spacing compact. 4. Start this branch with `just c` and submit the same prompt. 5. Confirm each intended blank markdown line occupies one terminal row throughout streaming and that the spacing does not compact or jump when the answer finishes. 6. As a focused regression, verify the sections after the first table, especially loose lists with paragraphs between items; those blank rows should remain stable throughout streaming. Targeted tests: - `just test -p codex-tui streaming_agent_tail_blank_line_uses_one_viewport_row` - `just test -p codex-tui history_cell::tests` ## Test Notes - Verified the exact prompt above in a real tmux TUI using latest Codex and this branch as the before/after comparison. - The full `just test -p codex-tui` run completed 2,782 of 2,784 tests successfully. Two unrelated guardian feature-flag tests fail reproducibly in isolation because the expected `OverrideTurnContext` message is absent. - `just argument-comment-lint` is blocked locally by the existing Bazel `compiler-rt` missing-header glob error; the touched Rust diff was inspected manually for opaque positional literals.
## Summary During assistant-message streaming, blank markdown lines in the transient active tail were prefixed with two spaces. Ratatui measured those whitespace-only lines as two viewport rows, so list- and table-heavy answers showed doubled vertical gaps while streaming and then visibly compacted when finalized into scrollback. - keep whitespace-only `StreamingAgentTailCell` lines structurally empty while preserving nonblank message prefixes - clear impossible hyperlink metadata when normalizing a blank tail line - add an inline snapshot and height regression proving one blank markdown line occupies one viewport row Related to openai#26618, but fixes a separate live-tail row-height issue rather than stale committed markdown content. ## How to Test Recommended before/after reproduction: 1. Start the latest Codex build without this change. 2. Submit this exact prompt: > Send 20 different lists: bullets vs numbered, simple vs complex with paragraphs in between items, etc. Intertwine them with some tables and some paragraphs. 3. While the answer streams, observe duplicated vertical gaps around list items and paragraphs. When the answer finishes, observe the spacing compact. 4. Start this branch with `just c` and submit the same prompt. 5. Confirm each intended blank markdown line occupies one terminal row throughout streaming and that the spacing does not compact or jump when the answer finishes. 6. As a focused regression, verify the sections after the first table, especially loose lists with paragraphs between items; those blank rows should remain stable throughout streaming. Targeted tests: - `just test -p codex-tui streaming_agent_tail_blank_line_uses_one_viewport_row` - `just test -p codex-tui history_cell::tests` ## Test Notes - Verified the exact prompt above in a real tmux TUI using latest Codex and this branch as the before/after comparison. - The full `just test -p codex-tui` run completed 2,782 of 2,784 tests successfully. Two unrelated guardian feature-flag tests fail reproducibly in isolation because the expected `OverrideTurnContext` message is absent. - `just argument-comment-lint` is blocked locally by the existing Bazel `compiler-rt` missing-header glob error; the touched Rust diff was inspected manually for opaque positional literals.
## Summary During assistant-message streaming, blank markdown lines in the transient active tail were prefixed with two spaces. Ratatui measured those whitespace-only lines as two viewport rows, so list- and table-heavy answers showed doubled vertical gaps while streaming and then visibly compacted when finalized into scrollback. - keep whitespace-only `StreamingAgentTailCell` lines structurally empty while preserving nonblank message prefixes - clear impossible hyperlink metadata when normalizing a blank tail line - add an inline snapshot and height regression proving one blank markdown line occupies one viewport row Related to openai#26618, but fixes a separate live-tail row-height issue rather than stale committed markdown content. ## How to Test Recommended before/after reproduction: 1. Start the latest Codex build without this change. 2. Submit this exact prompt: > Send 20 different lists: bullets vs numbered, simple vs complex with paragraphs in between items, etc. Intertwine them with some tables and some paragraphs. 3. While the answer streams, observe duplicated vertical gaps around list items and paragraphs. When the answer finishes, observe the spacing compact. 4. Start this branch with `just c` and submit the same prompt. 5. Confirm each intended blank markdown line occupies one terminal row throughout streaming and that the spacing does not compact or jump when the answer finishes. 6. As a focused regression, verify the sections after the first table, especially loose lists with paragraphs between items; those blank rows should remain stable throughout streaming. Targeted tests: - `just test -p codex-tui streaming_agent_tail_blank_line_uses_one_viewport_row` - `just test -p codex-tui history_cell::tests` ## Test Notes - Verified the exact prompt above in a real tmux TUI using latest Codex and this branch as the before/after comparison. - The full `just test -p codex-tui` run completed 2,782 of 2,784 tests successfully. Two unrelated guardian feature-flag tests fail reproducibly in isolation because the expected `OverrideTurnContext` message is absent. - `just argument-comment-lint` is blocked locally by the existing Bazel `compiler-rt` missing-header glob error; the touched Rust diff was inspected manually for opaque positional literals.
## Summary During assistant-message streaming, blank markdown lines in the transient active tail were prefixed with two spaces. Ratatui measured those whitespace-only lines as two viewport rows, so list- and table-heavy answers showed doubled vertical gaps while streaming and then visibly compacted when finalized into scrollback. - keep whitespace-only `StreamingAgentTailCell` lines structurally empty while preserving nonblank message prefixes - clear impossible hyperlink metadata when normalizing a blank tail line - add an inline snapshot and height regression proving one blank markdown line occupies one viewport row Related to openai#26618, but fixes a separate live-tail row-height issue rather than stale committed markdown content. ## How to Test Recommended before/after reproduction: 1. Start the latest Codex build without this change. 2. Submit this exact prompt: > Send 20 different lists: bullets vs numbered, simple vs complex with paragraphs in between items, etc. Intertwine them with some tables and some paragraphs. 3. While the answer streams, observe duplicated vertical gaps around list items and paragraphs. When the answer finishes, observe the spacing compact. 4. Start this branch with `just c` and submit the same prompt. 5. Confirm each intended blank markdown line occupies one terminal row throughout streaming and that the spacing does not compact or jump when the answer finishes. 6. As a focused regression, verify the sections after the first table, especially loose lists with paragraphs between items; those blank rows should remain stable throughout streaming. Targeted tests: - `just test -p codex-tui streaming_agent_tail_blank_line_uses_one_viewport_row` - `just test -p codex-tui history_cell::tests` ## Test Notes - Verified the exact prompt above in a real tmux TUI using latest Codex and this branch as the before/after comparison. - The full `just test -p codex-tui` run completed 2,782 of 2,784 tests successfully. Two unrelated guardian feature-flag tests fail reproducibly in isolation because the expected `OverrideTurnContext` message is absent. - `just argument-comment-lint` is blocked locally by the existing Bazel `compiler-rt` missing-header glob error; the touched Rust diff was inspected manually for opaque positional literals.
Summary
Expected:line in the live TUIInvestigation
The reported session was
019e980f-52de-7851-a519-7204b1fcca61. Its stored rollout contained the duplicated screenshot line only once in the final assistant message, which points at the live incremental TUI renderer rather than model output or history serialization.The issue reproduced when the stream committed a list-continuation line immediately after a fenced code block. A following blank line/list marker can still change how CommonMark interprets that newest block, so the stale rendered row remained in scrollback and the final render emitted it again.
Testing
just test -p codex-tui streaming::controllerjust test -p codex-tui flush_answer_streamjust fmtgit diff --checkNotes
just argument-comment-lintwas blocked locally by a Bazel/LLVM compiler-rt glob error. The file-scoped wrapper was also blocked by missingdotslash, and the direct source fallback was blocked by the pinned nightly being too old forsqlx 0.9.0. I manually inspected the touched Rust diff for opaque positional literal arguments.