Codex Desktop compaction does not bound transcript hydration/rendering; valid long thread becomes unscrollable
Summary
Codex Desktop becomes nearly unusable when opening a long, repeatedly compacted thread: the conversation pane cannot be scrolled reliably, renderer/app-server memory rises into the GiB range, and CPU remains active.
This reproduction is materially different from malformed legacy-history or image/tool-output bloat cases:
- the rollout parses with zero JSON or protocol-shape errors in a complete streaming scan;
- it contains no inline images and no invalid
image_urlvalues; - persisted tool-output text is only about
0.07 MiB; - the latest compacted checkpoint is valid and only about
0.97 MiBof rollout data follows it; - nevertheless, Desktop still appears to hydrate/replay the full pre-compaction transcript/event stream.
The model context is compacted, but the UI workload is not. Long threads therefore eventually become effectively unsafe to open even when compaction succeeds.
Environment
- Codex Desktop:
26.707.9981.0 - Codex CLI:
0.144.2 - Windows 11 x64, version
10.0.22631, build22631 - 31.4 GiB physical memory; about 8.3 GiB remained free during the sample
Reproduction
- Use a single Desktop thread for a long-running, agent-heavy workflow.
- Let automatic context compaction occur repeatedly.
- Fully restart Codex Desktop.
- Open the long thread while it has an active or recently active turn.
- Try to scroll the conversation pane or interact with the thread.
- Observe severe UI lag or an effectively unscrollable pane, while app-server and renderer memory/CPU remain elevated.
This is reproducible after removing closed descendant threads and restarting Desktop. Continued work can regenerate descendants, but the main rollout itself is sufficient to expose the unbounded transcript/event replay path.
Sanitized measurements
Complete streaming scan of the affected main rollout at one snapshot:
rollout size: ~79.3 MiB
JSONL records: 32,834
JSON parse errors: 0
response_item records: 21,547
event_msg records: 11,148
event_msg/user_message: 3,079
response_item/message user role: 4,629
response_item/message assistant: 284
token_count events: 5,648
reasoning records: 5,453
compacted checkpoints: 8
data:image references: 0
invalid image_url values: 0
persisted tool-output text: ~0.07 MiB
encrypted reasoning content: ~7.99 MiB
text fields: ~34.6 MiB
data after latest checkpoint: ~0.97 MiB / 255 records
The latest checkpoint was valid. The rollout file remained byte-stable during a short sample while the UI was still slow.
Observed process footprint while the affected thread was open:
codex app-server private memory: ~2.6 GiB
largest renderer working set: ~1.15 GiB
other renderer working set: ~0.88 GiB
renderer CPU in 3-second sample: ~8-19%
app-server CPU in sample: ~5%
system free physical memory: ~8.3 GiB
There were no relevant rollout parsing errors in the current logs. The only recent warnings were unrelated plugin-manifest warnings.
Compaction/cleanup experiment
Before this snapshot, local maintenance successfully:
- reduced the main rollout from about
73.5 MiBto69.1 MiBby retaining one compacted checkpoint while preserving prompt-bearing records; - removed
449closed child threads and about5.28 GiBof child rollout files; - verified SQLite integrity, zero residual closed-thread references, zero JSON parse errors, and preservation of the main thread.
After continued execution, the main rollout grew back to about 79.3 MiB, accumulated eight compacted checkpoints, and the conversation pane again became effectively unscrollable. This shows that storage cleanup and model-context compaction do not bound Desktop's active transcript/rendering cost.
Expected behavior
- Opening a compacted thread should hydrate metadata plus the latest checkpoint and a bounded recent tail, not all pre-compaction event records.
- The conversation list should use real virtualization/pagination so DOM/renderer work remains bounded by the viewport rather than total thread length.
- Durable audit history should remain available on demand without being part of the initial renderer/app-server payload.
- Hidden technical records such as token counts, reasoning envelopes, tool-call metadata, and old lifecycle events should not be materialized into the active UI unless requested.
- Active-turn updates should append bounded deltas and must not repeatedly rebuild or rebroadcast the full transcript.
- Compaction should expose a clear contract separating model context, durable history, and active UI history.
Actual behavior
Compaction reduces the model's current context, but opening the thread still causes Desktop to process enough of the old event stream that scrolling and interaction become work-blocking. Repeated local slimming can temporarily reduce disk usage but does not prevent the same UI failure from recurring.
Why this report is separate from related issues
- #32198 covers legacy records that emit parse errors during full replay. This reproduction has zero parse errors.
- #25251 and #22991 cover very large files dominated by tool output or hundreds of MiB of rollout data. This reproduction occurs around 79 MiB with negligible persisted tool-output text.
- #25779 tracks the broader unbounded-session-state problem. This report isolates the missing contract between successful context compaction and bounded transcript hydration/rendering.
- #32722 covers full snapshot fan-out to newly connected windows. The same bounded/paginated protocol principles likely apply, but this report is specifically about opening and scrolling one compacted long thread.
Requested regression coverage
Please add a Desktop/app-server performance test that:
- creates a valid long thread with tens of thousands of pre-compaction events and a small latest checkpoint/tail;
- opens that thread in a fresh Desktop client;
- asserts initial hydration bytes, in-memory turn items, rendered rows, and DOM nodes remain bounded;
- asserts scrolling remains responsive;
- asserts active-turn deltas do not trigger full transcript reconstruction or snapshot rebroadcast;
- verifies old history can still be fetched lazily in pages.
I did not attach the rollout or local databases because they contain private prompts and local paths. I can provide a synthetic valid JSONL generator if maintainers specify the current supported fixture format.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗