Summary
Compact each thread's event log when a turn ends: keep only the last cumulative turn diff and merge streamed deltas. On the maintainer's data this alone removes roughly two thirds of all stored bytes, with no new dependency.
Measurements (maintainer's data dir, read-only scan)
- 2,716 logs, 8.83 GB total.
turn_changes_updated: 4.16 GB (47%). Codex turn/diff/updated carries the whole diff of the turn so far and each one is stored as a full TurnChangesUpdated (crates/agent/src/codex.rs), so a turn's size grows roughly with the square of its edits. Keeping only the last record per turn leaves 0.041 GB. The largest log (193 MB) is 95% such records.
delta: 5.36M records, 1.38 GB (15.6%), ~150 bytes of envelope per few bytes of text.
- On a 100-file sample (404 MB): turn-end compaction alone → 138 MB (34%); compaction + gzip-6 → 40 MB; compaction + zstd-3 → 33 MB.
Proposal
- At turn end, rewrite that turn's records: drop superseded
TurnChangesUpdated for the same turn, and merge consecutive deltas of the same item into one record.
- Deltas cannot simply be dropped when an item completes:
merge_text keeps the streamed text when the completed text is empty (crates/core/src/session.rs). Merge, don't discard.
- The
perf/remote-wire-bytes work already strips superseded diffs and merges deltas on the wire (crates/runtime/src/app/history.rs). Move that logic to the persistence path so the wire gets it for free and there is one owner.
- Rewrite existing logs lazily (on open) or in a background pass, gated by the existing fold-equivalence check (
debug_equiv in crates/runtime/src/app/history.rs) so the folded timeline is provably unchanged.
- Export stays byte-compatible JSONL.
Notes
Summary
Compact each thread's event log when a turn ends: keep only the last cumulative turn diff and merge streamed deltas. On the maintainer's data this alone removes roughly two thirds of all stored bytes, with no new dependency.
Measurements (maintainer's data dir, read-only scan)
turn_changes_updated: 4.16 GB (47%). Codexturn/diff/updatedcarries the whole diff of the turn so far and each one is stored as a fullTurnChangesUpdated(crates/agent/src/codex.rs), so a turn's size grows roughly with the square of its edits. Keeping only the last record per turn leaves 0.041 GB. The largest log (193 MB) is 95% such records.delta: 5.36M records, 1.38 GB (15.6%), ~150 bytes of envelope per few bytes of text.Proposal
TurnChangesUpdatedfor the same turn, and merge consecutive deltas of the same item into one record.merge_textkeeps the streamed text when the completed text is empty (crates/core/src/session.rs). Merge, don't discard.perf/remote-wire-byteswork already strips superseded diffs and merges deltas on the wire (crates/runtime/src/app/history.rs). Move that logic to the persistence path so the wire gets it for free and there is one owner.debug_equivincrates/runtime/src/app/history.rs) so the folded timeline is provably unchanged.Notes
flate2(already in the tree via zip/png) is enough — zstd gains ~20% more on top of compaction for a new C dependency.