fix(chat): tie conversation cursors to the Codex rollout chain - #18
Conversation
Follow-up to devswha#5's review. A cursor's id named only the live file, while its offset counts from the start of the whole Codex chain. When the chain changed between requests (an earlier rollout found later, or a parent archived after its chain was evicted), an old cursor pointed at other turns: `before` answered a page from elsewhere and the gap fill showed turns twice and out of order, never a 409. The id now also carries a fingerprint of the earlier rollouts (path, bytes used, file identity), so a cursor from another chain answers 409 and the chat reloads; older pages are cached under the same id, and the stream is identified before the cache is consulted. A chain that stops at a cut no file holds is now remembered for 30s rather than walked again on every append. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
abc1ab8 to
823c8c7
Compare
|
Merged. Thanks. The chain fingerprint covers what can change, and the new test fails without it. One follow-up for a separate PR: a cached chain doesn't recover when its parent is archived. |
Follow-up to #18's review. A complete chain stays cached until 64 others push it out. If its parent rollout was archived meanwhile, every read that reached the parent failed with transcript_missing and the chat fell back to terminal output. Right after a backtrack the live file is small, so even the newest page reached the parent. When a remembered chain names a file that no longer exists, the chain is forgotten and looked up again. It comes back shorter (archived_sessions/ is not searched) under another stream id, so a cursor into the old chain answers 409 once and the chat reloads. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Follow-up to #5, as requested in its merge review.
Cursors are tied to the earlier-rollout chain
A cursor's id named only the live file (
dev-ino), while its offset counts from the start of the whole Codex chain. If the chain changed between requests (an earlier rollout found later, or a cached chain evicted after its parent was archived), an old cursor pointed at other turns.beforereturned a page from elsewhere, and the gap fill showed turns twice and out of order. It never returned a 409.The id now carries a fingerprint of the earlier rollouts: a hash of each one's path, the bytes of it the chain uses, and its file identity (
dev:ino). A cursor from a different chain failsparseCursor, answers409 history_changed, and the chat reloads.Unresolved chains are remembered briefly
A chain that stops at a cut that no file holds used to walk
sessions/again on every append. It is now cached for 30s (complete chains stay cached as before), so a rollout that shows up later is still picked up.Tests
conversation.test.ts: a cursor issued before the earlier rollout appears answersHistoryChangedafterwards, for bothbeforeandfrom, and a fresh read then pages back to the earlier rollout's first prompt. Without the fingerprint the test fails: the old cursor returns a page from the wrong place.codex.test.ts: the "shows less history" case clears the chain cache (forgetHistoryChains) before it expects the late rollout to resolve.bun test: 269 pass, 0 fail. Typecheck clean.🤖 Generated with Claude Code