fix(tui): order live transcript by message created time so wrapped ids stay visible - #13247
Merged
Conversation
johnnyeric
marked this pull request as ready for review
August 20, 2026 10:36
Contributor
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (5 files)
Reviewed by grok-4.6 · Input: 242.3K · Output: 30.8K · Cached: 322.4K Review guidance: REVIEW.md from base branch |
marius-kilocode
approved these changes
Aug 20, 2026
This was referenced Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The TUI live transcript hides new turns in long-running sessions whose message IDs crossed the 48-bit timestamp wrap on 2026-08-14. Persistence is fine — quitting and reopening shows the missing work — only the live store drops it.
For context I resumed a session from 2 weeks before and it got stuck without streaming anything and the only visible movement was the spinner. With the fix, the same session resumed correctly.
Screenshots
Before
After
Why
packages/opencode/src/id/id.tsencodesDate.now() * 0x1000 + counterin 48 bits, wrapping roughly every 795 days. Wrap 25 ended 2026-08-14 13:19:55; new IDs restarted atmsg_000…/msg_019…. The TUI sync store (packages/tui/src/context/sync.tsx) inserted messages by binary search on id and evictedlist[0]as "oldest" when the 100-message window overflowed. After the wrap, a newmsg_019f…sorts before an oldmsg_ff0c…, so every new turn inserted at index 0 and was immediately evicted. Hydration over HTTP orders bytime_created, which is why reopening looked correct (observed inses_023cb4401ffe2R9oTrbp7nj9Hz, 606 messages spanning the wrap).This also closes a second latent bug: after hydration the list was time-ordered but binary-searched as if id-ordered, so a lookup could miss an element that was present —
message.updatedcould insert a duplicate entry andmessage.removedcould fail to remove one. The linear id lookup eliminates both.How
Ordering logic lives in a new kilo-owned helper,
packages/tui/src/kilocode/message-order.ts, ordering bytime.createdwith id as tiebreaker (matching the server's canonical(time_created, id)order):older(a, b)— comparator by created time, then idslot(list, item)— insert/update index by created timeat(list, id)— linear id lookup (the list is no longer id-sorted)recent(list, cap = 100)— newest-by-created windowsync.tsxchanges are limited to five marked call-site swaps:message.updated/message.updated.1insert viaslot,message.removed/message.removed.1look up viaat, and the hydrate window usesrecentinstead ofslice(-100).Tests live in
packages/tui/test/kilocode/: unit coverage for the helper plus sync-store tests asserting a latermsg_019f…emitted after an oldermsg_ff0c…stays at the tail (on both the plain and versioned sync channels) and that eviction at the 100-message cap drops the oldest message, not the newest.Known remaining limitation, out of scope here: a few upstream-owned render-layer sites in
packages/tui/src/routes/session/index.tsxstill compare message ids lexicographically as a proxy for chronology (queued styling, revert boundaries for undo/redo/copy). Those are pre-existing and unaffected by this change.