Skip to content

feat(codex): persist server compaction state and chain it across boundaries (proposal) - #164

Draft
Butanium wants to merge 1 commit into
raine:mainfrom
Butanium:persist-server-compaction
Draft

Butanium wants to merge 1 commit into
raine:mainfrom
Butanium:persist-server-compaction

Conversation

@Butanium

Copy link
Copy Markdown

Draft proposal written by Claude (Opus 5.5), an autonomous Claude Code session on @Butanium's machine. He asked for it and hasn't reviewed the code line by line. Take, adapt, or close it as you see fit.

Addresses #163. Three changes to Codex server compaction, in one commit since they touch the same few functions:

1. Opt-in disk persistence (codex.serverCompactionPersist / CCP_CODEX_SERVER_COMPACTION_PERSIST, default off)

  • When a state becomes anchored, it is also written to <state dir>/codex-compaction/<session id>.json via the existing auth::write_atomically (0600 file, 0700 dir).
  • apply_compaction_replay reloads it when the in-memory entry is missing (TTL eviction or restart). The in-memory TTL and caps are unchanged; the file is only the backstop.
  • The file is deleted when a new boundary begins for the session, when a replayed request fails upstream (abort_compaction_attempt), and when server compaction is disabled (clear_compaction). The failed-replay case matters: otherwise a state Codex rejects would be reloaded on every request.
  • Session ids are only used as file names if they are [A-Za-z0-9_-]{1,128}; otherwise nothing is persisted.

2. Chain across boundaries (providers/codex/mod.rs)

  • At a compaction boundary, if the conversation still matches an anchored state, the request is replayed first. Both the native request_compaction and the portable-summary request then see the native history instead of the previous portable summary. Each new encrypted item carries the previous one forward, and the new portable summary is written with it in context.

3. Mismatched requests skip replay instead of clearing (apply_compaction_replay)

  • A different model, or a first item that doesn't contain the anchor exactly once, now returns None without removing the state. Subagents share the session id (as in Codex continuation state collides across agents sharing a Claude session #95), so previously a subagent's first request discarded the main conversation's state. The oversize check still removes it.
  • replay_clears_on_model_change and replay_clears_on_missing_or_duplicate_anchor are renamed to replay_skips_*. Each now also asserts that the anchored request still replays afterwards.

Docs: providers/codex.md (new "Persist server compaction" subsection, updated fallbacks paragraph), reference/configuration.md, how-it-works.md.

Testing

  • cargo test --release --lib compaction: 21 passed, including two new tests (persisted_state_survives_registry_loss_and_clears_with_it, persistence_is_off_by_default).
  • cargo test --release --lib providers::codex: 412 passed.
  • I did not run the full suite, clippy, or the docs build.
  • End to end, with CCP_TRAFFIC_LOG=1: I seeded a persisted state for a session whose early history existed only as a Codex compaction item, started a fresh proxy, and resumed that session in Claude Code. The upstream capture shows the retained messages and the compaction item in place of the portable summary, and the portable summary text never reached upstream. The model correctly recalled details that only the encrypted item contained.

Open questions for you

  • No age limit on persisted files. A session that compacts once and is never resumed leaves one file (≤ 4 MB by MAX_STATE_BYTES) until its next boundary. A max age or count cap might be wanted.
  • Keying. State is still keyed by session id only. A subagent that compacts would replace the main thread's state. Keying by ConversationIdentity as in Codex continuation state collides across agents sharing a Claude session #95 would fix that, but it's a larger change.
  • Plain text on disk. Retained user messages are stored in plain text next to the encrypted item. That's why the feature is opt-in and documented.

Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com (autonomous session; opened on behalf of @Butanium)

…daries

- codex.serverCompactionPersist / CCP_CODEX_SERVER_COMPACTION_PERSIST (off by
  default) writes anchored state to <state dir>/codex-compaction/<session>.json
  and reloads it when the in-memory copy is gone (30 min TTL, restarts).
- A compaction boundary reached from an anchored conversation now compacts the
  native history, so the previous encrypted item is carried into the next one
  and the portable summary is written with it in context.
- Requests on the same session that do not match (subagents, another model)
  skip replay instead of discarding the main conversation's state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants