Windows 26.901.4073.0 / CLI 0.153.1: duplicate rollout ordinal stalls history; later conversations disappear after restart
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
Summary
In a long local paginated conversation, canonical JSONL kept saving user/assistant messages, tool activity and generated-image events, but history projection stopped at a duplicate ordinal. Ongoing work was initially visible; after restarting the desktop app the later conversation was no longer displayed. The screen jumped from an earlier installation-test preparation to the new recovery message.
This is separate from Canvas report #42985. That earlier report followed an in-app feedback submission (feedback ID: 01a05eb4-6d9d-7cd2-8bae-207d9839f69b). No new in-app feedback upload for this history bug is being claimed.
Environment and chronology
- Windows x64 desktop package 26.901.4073.0; installed app-server codex-cli 0.153.1.
- September 5, 2026, UTC+8; one logical local conversation with four linked rollout segments.
- No original rollout or projection was manually modified before diagnosis.
- Last history visible after reopening: installation preparation turn started 14:39, records through ~14:48.
- Canonical records still contained 17:25 first-screen delivery, 17:29–17:32 installation/activation design, 17:39 follow-up requests, 17:46 later mockups and evening feedback discussion.
- Restarting to reload history instead made the later conversation disappear from the display. Comparison found all four backed-up pre-restart file prefixes still byte-for-byte present; the active segment had grown.
- App kept appending canonical records while repeatedly logging the same projection error, without an actionable history-integrity warning to the user.
Direct evidence
| Local time | JSONL line | Ordinal | Record |
|---|---|---|---|
| 14:48:49 | 6019 | 8960 | event_msg / token_count |
| 15:13:02 | 6020 | 8960 | event_msg / thread_settings_applied |
| 15:13:06 | 6021 | 8961 | event_msg / thread_settings_applied |
Only one non-consecutive ordinal boundary was found in the inspected active file. Every line parsed as JSON. Both SQLite databases passed integrity checks.
Affected segment entry in thread_history_projection_state:
next_rollout_byte_offset = 29202943
next_rollout_ordinal = 8961
The offset pointed exactly to the second record with ordinal 8960. Repeated codex_thread_store::local::live_writer warnings ended with:
failed to project durable rollout ...
thread-store internal error: thread history projection ... expected ordinal 8961, got 8960
Diagnostic detail: linked conversation projection rows are keyed by segment ID. Looking up only the root task ID returns the oldest segment cursor and can mislead diagnosis. Valid JSON/SQLite does not establish readable paginated history.
Was compaction responsible?
The user remembered a recent compaction and suspected it. Relevant later compactions were at 15:34 and 19:28, after the duplicate at 15:13. These do not establish compaction as the cause. The direct failure is duplicate ordinal plus strict projection rejection; the exact writer/resume trigger has not been deterministically reproduced.
Isolated recovery experiment and live workaround
- Back up all four rollouts with SHA-256 manifests and state/history databases using SQLite online Backup API, including committed WAL data. Keep originals untouched and backups local.
- Run the same installed 0.153.1 app-server against isolated copies, with copied rollout paths redirected, no credentials and no model turns. Baseline native history stopped at the old installation turn; all six target markers (five screen deliveries plus feedback issue) were absent.
- In the copy only, advance the affected segment's projection byte offset exactly 649 bytes, 29202943 -> 29203592, keeping next_rollout_ordinal=8961. This bypasses only the duplicate settings event for UI projection; it does not alter/delete the canonical event.
- Load the copied thread with thread/resume without turn/start and read paginated history. All six markers returned. A second cold app-server read also passed with no ordinal errors. All original JSONL hashes remained unchanged; SQLite integrity was OK.
- After a fresh backup, apply the same guarded transactional single-row cursor adjustment live at 20:46:51. The running app automatically projected the suffix and kept advancing with new activity. No subsequent matching ordinal errors were observed in the checked interval.
- All 91 inspected user/assistant completion-event IDs after the boundary existed in repaired projection. This includes recovery messages, not a claim exactly 91 messages disappeared. Pre-repair canonical file prefixes remained unchanged.
- Projection continued advancing after another compaction during the investigation; latest checked database integrity remained OK.
Desktop-visible refresh confirmation remains pending. Native data reading is recovered, but full UI and subsequent completed-turn validation are not being claimed. This is a local workaround, not a product fix or general advice to edit cursors. A future full reindex with the same strict parser could encounter the preserved duplicate again.
Expected behavior / requested fix
- Derive next writer ordinal reliably from durable state across interrupted turns, restarts and linked segments.
- Safely reconcile duplicate metadata ordinals without hiding all later valid history.
- Preserve canonical records and show an actionable warning when saved and displayed history diverge.
- Provide supported task-scoped diagnosis/rebuild with backup and validation, without hand-editing SQLite cursors.
Related reports: #41657, #41566 and #38792. This report adds evidence from desktop 26.901.4073.0 / CLI 0.153.1 and a tested projection-only recovery. Current main-branch materializer includes duplicate/regressed ordinal handling; please clarify which release contains it and whether it recovers already-stalled projections.
Full transcripts, credentials, local user paths, private conversation screenshots and raw logs are intentionally not attached.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with codex_thread_store::local::live_writer and the thread_history_projection_state cursor, using the isolated-copy recovery experiment as a reproducible baseline. Read the current main-branch materializer's duplicate/regressed ordinal handling and exercise thread/resume across linked segments. Done means later valid history remains readable after duplicate metadata, canonical records stay intact, and divergence produces an actionable warning.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, sqlite
- Domain
- backend, databases, desktop
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100