TUI prepends paginated history before fork lineage on resumed forked threads

Open 💬 1 comment Opened Aug 9, 2026 by 92645417d9e5c763259dbebc306e3e

What version of Codex CLI is running?

  • rust-v0.148.0-alpha.1

What subscription do you have?

  • ChatGPT Pro 20x

Which model were you using?

  • gpt-5.6-sol

What terminal emulator and version are you using (if applicable)?

  • Alacritty with Codex running inside tmux

What issue are you seeing?

  • Resuming a forked thread initially renders Thread forked from ... before the child transcript, but loading an older paginated history page inserts those messages before the lineage marker.
  • The marker then appears inside the child transcript instead of remaining its ancestry boundary, even though the persisted parent metadata is intact.
  • This makes older child messages look as if they precede the fork relationship and obscures where the resumed thread came from.

What steps can reproduce the bug?

  • Create a fork from a thread with enough inherited or child history to require more than one history page.
  • Resume the forked thread in the TUI and confirm that Thread forked from ... appears before the initially loaded messages.
  • Scroll to the beginning or otherwise trigger loading the preceding history page.
  • Observe that the newly loaded messages are inserted before Thread forked from ....

What is the expected behavior?

  • The fork lineage marker should remain before every transcript item belonging to the child thread as older pages are prepended.
  • Initial resume and later pagination should preserve the order fork lineage, older child history, newer child history.

Additional information

  • Fork lineage is currently emitted before the initial history replay buffer begins and is represented as an ordinary transcript cell.
  • The non-overlay pagination path chooses an insertion point only after the last session-info cell, defaulting to index zero when that cell is absent.
  • The lineage cell is therefore not recognized as a structural insertion boundary.
  • This issue report was written with assistance from Codex CLI.

View original on GitHub ↗

1 Comment

jdcodes1 · 9 days ago

Structural cause, from the TUI side: the Thread forked from … marker is an ordinary history cell emitted once at resume time (emit_thread_forked_event, https://github.com/openai/codex/blob/1f41cc5d92/codex-rs/tui/src/chatwidget/session_flow.rs#L218-L233) — it's just whatever happens to be first in transcript_cells when the initial page renders. Older-page loads then prepend fetched entries at the absolute top of the transcript, with no concept of a pinned ancestry boundary, so each next_older_cursor batch lands above the marker and the lineage line migrates into the middle of the child history, exactly as you observed. The persisted metadata is fine; only the insertion anchor is wrong.

Two fix shapes: (1) make the marker a pinned prologue — track its cell index (or give lineage cells a kind flag) and have the older-page prepend insert after any leading lineage cells; or (2) stop emitting it at resume and instead render it as part of whichever page contains the thread's true beginning (i.e., only when next_older_cursor is exhausted), which also fixes the subtler cousin: on a multi-page child, the marker currently implies "everything below is post-fork" even before pagination rearranges it. Option 2 is more honest about what the marker means; option 1 is the smaller diff.