Skip to content

fix(session): keep loop exit and part ordering robust to id wrap - #48472

Open
yixiao5428 wants to merge 1 commit into
anomalyco:devfrom
lihaokun:session-gate-chronology
Open

fix(session): keep loop exit and part ordering robust to id wrap#48472
yixiao5428 wants to merge 1 commit into
anomalyco:devfrom
lihaokun:session-gate-chronology

Conversation

@yixiao5428

Copy link
Copy Markdown

Issue for this PR

Fixes #42816 — specifically the completion-gate fallback item. The part-ordering gap below is new (not in that issue). Items 1 and 2 of #42816 (revert cleanup ordering, localeCompare collation) are intentionally left to #42819.

Type of change

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

What does this PR do?

Two ordering gaps survive the August rollover fixes:

  1. The completion gate has no chronological fallback. It requires lastAssistant.parentID === lastUser.id. For histories written before the parentID gate landed — or with synthetic users carrying legacy short ids — the chronologically latest assistant stays parented to an older user, the equality never holds, and every run issues one spurious provider request. [BUG] Two ordering-correctness gaps remain after the ID-rollover fix #42816 measured 31 of 251k sessions in that shape. Add a fallback: the turn is also complete when the assistant precedes the latest user by time.created (id tiebreak) — compareChronology, the same ordering the pagination cursor already uses. The parentID arm is unchanged, so well-formed histories behave identically.

  2. Message parts were still ordered by raw part id (hydrate, parts()). Parts of one message whose ids straddle the wrap sort backwards. Order them by persisted time_created with the id tiebreak, matching the message-level cursor. (Client stores still key parts by id — follow-up.)

Two comment corrections in id.ts: the encoding keeps the low 36 timestamp bits, so ids are not globally monotonic across the 2^36 ms period.

Known tradeoff, stated openly: with the fallback, a run whose latest assistant predates a later concurrent user (assistant parented to the earlier user) exits before answering the newer user; parentID alone answers the newer user but never exits stale-parent histories. We chose exit — the same choice the issue reporter made.

Related work: earlier attempts at this family — #39806 (full ordering rework), #42640 (time.created in latest()/runLoop), #42684 (numeric compare in isAfter) — were closed unmerged, two of them on the description-compliance window rather than on merit; the message-level half they targeted has since landed via #40990. This PR covers the two gaps none of them reached, with two differences: both fixes are pinned to reproductions on current dev (one request on dev, zero here; wrong part order flipping to correct), and it deliberately stays out of the domains already owned by open work — revert cleanup/collation (#42819, #46974), TUI redo/child ordering (#42590, #42907, #43035), and the app store sort that #42816 raised as wanting a shared comparator — so nothing here competes with those.

How did you verify your code works?

  • bun run typecheck in packages/opencode
  • bun test test/session/ — 423 tests, 0 fail, including two new fixtures: one reproducing the spurious request across a pinned rollover boundary (one request on dev, zero here), one seeding parts across the wrap boundary (order flips from wrong to correct)

Screenshots / recordings

N/A — no UI change.

Checklist

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

Ascending ids encode only the low 36 timestamp bits, so they wrapped on
2026-08-14 and no longer order chronologically across the boundary. The
terminal gate's parentID check never holds when the newest assistant was
written by a pre-fix binary with a stale parent, so the loop issues one
spurious request per run (issue anomalyco#42816, measured 31 of 251k sessions);
fall back to comparing the assistant against the latest user by
time.created with id as tiebreaker. Message parts were still ordered by
raw part id, which reorders parts of a message whose ids straddle the
wrap; order them by persisted creation time like the message pagination
cursor already does. Document the encoding period on the id helpers.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
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] Two ordering-correctness gaps remain after the ID-rollover fix

1 participant