[0.153.4][Windows] Resume reuses two rollout ordinals after trailing token_count records, freezing desktop history
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
Summary
After resuming an interrupted local task, Codex Desktop repeatedly shows an older conversation state when refreshed or reopened. Later user messages and the completed assistant response are still present in the durable rollout JSONL, but the paginated history projection no longer advances.
This report adds a two-record ordinal regression on bundled CLI 0.153.4 and a specific suspected deserialization path to the existing duplicate-ordinal reports.
Environment
- Codex Desktop Windows package:
26.901.6511.0 - Bundled
codex-cli --version:0.153.4 - Windows 11 Pro x64, build
10.0.26200 - Affected rollout:
history_mode = paginated, recorded CLI version0.153.4
Verified on-disk failure boundary
The following are actual top-level ordinals, with conversation contents and identifiers omitted. Times are September 5, 2026, UTC+08:00.
| Physical JSONL line | Time | Record | Stored ordinal |
|---|---|---|---|
| 365 | 21:00:26.532 | tool result | 8288 |
| 366 | 21:00:26.543 | event_msg/token_count |
8289 |
| 367 | 21:00:33.774 | event_msg/token_count |
8290 |
| 368 | 21:05:01.561 | event_msg/thread_settings_applied |
8289 (reused) |
| 369 | 21:05:01.573 | event_msg/task_started |
8290 (reused) |
The inspected segment contains exactly one backward ordinal transition. All lines are valid JSON. Both trailing token_count records contain a rate-limit field used_percent: 82.0.
The segment's projection checkpoint remains:
next_rollout_byte_offset = 2007813
next_rollout_ordinal = 8291
Byte offset 2007813 points exactly to physical line 368. Every subsequent append encounters:
codex_thread_store::local::live_writer:
failed to project durable rollout for <thread>:
thread-store internal error:
thread history projection for <segment> expected ordinal 8291, got 8289
Logs show thread/resume at 21:02:40, then resume_interrupted_task and the first projection error at 21:05:01. The history-reading interface still returns the earlier turn as inProgress; later task_complete and user-message records are present in the JSONL. Continuing the task appends more durable records without updating the rendered history.
Suspected implementation cause in rust-v0.153.4
This is a source-level diagnosis consistent with the on-disk evidence, not an independently compiled Rust reproduction:
- ordinal_state_for_rollout scans backward as
RolloutLine, silently skips rejected records, and sets the next ordinal from the first successfully deserialized record. - ReverseJsonlScanner::finish_record uses ordinary
serde_json::from_slice. - The same version already has decode_rollout_line, with an explicit workaround for floating-point deserialization through flattened envelopes under
serde_json/arbitrary_precision. The ordinal scan bypasses that decoder.
If the two trailing rate-limit records are rejected by the ordinary deserializer, the backward scan falls back to ordinal 8288 and resumes at 8289. This predicts the exact two-record regression observed above. There is no evidence establishing concurrent writers as the cause in this case.
Observed reproduction and impact
- Continue a long-running local task with paginated history.
- Resume an unfinished/interrupted turn whose durable tail contains the two rate-limit records above.
- Continue working, then refresh/reopen the task.
- The UI repeatedly returns to the old turn, although the newer conversation and completed work remain in the raw transcript.
A fresh deterministic reproduction has not been run. The captured resume boundary and repeated history failure are directly observed. This can cause users to repeat already completed work because the displayed conversation appears to have reverted.
Expected behavior / suggested investigation
- Recover the next ordinal using the same compatible decoder as normal rollout loading, without silently moving backward past valid durable records.
- Add regression coverage for resuming a paginated rollout ending in multiple
token_countrecords with floating-point rate-limit values. - Surface an actionable history error and provide a supported per-thread recovery path for already affected transcripts.
Related: #42027 and #41657 (reused ordinals after resume), #42794 (floating-point decoding failures). Investigation was read-only; no transcript or database repair was attempted. Raw conversations, credentials, thread identifiers, and personal paths are 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-rs/rollout/src/ordinal.rs and compare its backward scan with the compatible decoder in codex-rs/rollout/src/lib.rs; inspect ReverseJsonlScanner::finish_record in reverse_jsonl_scanner.rs as the deserialization boundary. Add regression coverage for a paginated rollout ending in multiple token_count records with floating-point rate-limit values. Done means resume preserves the next ordinal and history continues projecting instead of freezing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100