Skip to content

fix(tui): redo and child session ordering across id wrap - #42590

Open
mdrkrg wants to merge 2 commits into
anomalyco:devfrom
mdrkrg:id-wrap-ordering
Open

fix(tui): redo and child session ordering across id wrap#42590
mdrkrg wants to merge 2 commits into
anomalyco:devfrom
mdrkrg:id-wrap-ordering

Conversation

@mdrkrg

@mdrkrg mdrkrg commented Aug 14, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #42583

Related: #38787, #42589, #39806, #42461

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Message and session IDs contain a time field that wraps around every ~795 days. After a wrap, comparing IDs as strings no longer matches their real order in time.

Two places in the TUI still compared IDs as strings:

  • Redo after revert looked for the next user message by ID, which can pick an old message after a wrap. It now finds the next user message by its position in the list (the message list is kept in time order).
  • Child session lists were sorted by ID, so a newer session could show up after older ones. They are now sorted by last activity, newest first.

Also added a test making sure trimSessions keeps its output ordered by ID, since the app looks sessions up in that list by ID.

This follows up on #40991/#40990, which removed the same kind of ID comparison on the server.

How did you verify your code works?

  • bun test test/util/ in packages/tui
  • App global-sync tests pass
  • bun typecheck clean in new code

Screenshots / recordings

None

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

I found a related PR, though it's in a different component:

Related PR:

Note: This is listed in the PR description as a related issue (Related: #38787, #42589, #39806, #42461), so it's not a duplicate but rather a complementary fix to the same underlying problem.

No duplicate PRs found

@mdrkrg

mdrkrg commented Aug 14, 2026

Copy link
Copy Markdown
Author

Root cause

packages/opencode/src/id/id.ts:

let now = BigInt(currentTimestamp) * BigInt(0x1000) + BigInt(counter)
now = direction === "descending" ? ~now : now

const timeBytes = Buffer.alloc(6)          // <-- only 48 bits stored
for (let i = 0; i < 6; i++) {
  timeBytes[i] = Number((now >> BigInt(40 - 8 * i)) & BigInt(0xff))
}

return prefix + "_" + timeBytes.toString("hex") + randomBase62(LENGTH - 12)
  • now = Date.now() * 0x1000 + counter needs ~53 bits, but only the low 48
    bits
    are kept. The stored value is effectively
    (Date.now() mod 2^36) * 0x1000 + counter.

  • 2^36 ms = 68,719,476,736 ms ≈ 795.4 days ≈ 2.18 years. Whenever
    Date.now() crosses an integer multiple of 2^36, the ID "clock" resets:
    IDs go from ...ffff... back to ...0000....

  • The first wrap since 1970 that users will hit is k = 26, i.e.
    26 * 2^36 ms ≈ 1,786,706,395,136 ms (mid-2026).

The ID format is unchanged in this PR (still wraps every ~795 days), this is tracked in #39806

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug Report: Message ID generation wraps every ~2.18 years, causing sessions to stop responding silently

1 participant