Proposal: Treat context compaction as a trust-boundary transition for long-running mutable tasks
Summary
For long-running Codex tasks that can modify files or other external state, automatic context compaction should optionally be treated as a trust-boundary transition rather than an ordinary invisible continuation point.
I am proposing an opt-in fail-closed mode in which an observed context-compaction event:
- checkpoints the current execution state;
- revokes further mutation authority;
- persists that denial across restart/resume; and
- requires explicit state re-verification before mutations are allowed again.
This is complementary to proposals that try to improve post-compaction continuity. If continuity can be restored reliably, Codex should continue. But when the runtime cannot guarantee that the active execution state survived compaction intact, continuing to mutate the workspace is riskier than stopping.
Why this matters
A long-running agent can cross a context-compaction boundary after it has already:
- inspected files;
- changed code;
- run tests;
- rejected approaches;
- reached a verification or release stage;
- accumulated acceptance criteria and stop conditions.
After compaction, a partially reconstructed state can lead to failure modes such as:
- repeating completed work;
- reopening resolved decisions;
- losing the exact completion condition;
- treating an earlier state as current;
- making new mutations from stale assumptions;
- reporting unexecuted verification as complete.
The key distinction is that context continuity and mutation authority do not need to be the same thing.
If Codex is uncertain about its restored state, it can still explain what happened, inspect durable evidence, and recover. What should be optional is the right to continue modifying external state before that recovery is verified.
User-space reference implementation
I implemented a small user-space guard to test this model against the real Codex app-server notification stream on Windows.
Environment used for the live validation:
- Codex CLI/app-server:
codex-cli 0.147.0-alpha.6.5 - platform: Windows
- real Codex executable SHA-256:
fb5c760e14cf8fe86e12e49e8a3e7f237af06082d6b9fe1e411e463b7229c916
In the independently re-read live run:
- raw app-server JSONL: 39 lines, 0 parse errors;
item/started + contextCompaction: exactly 1;- guard firing: exactly 1;
- checkpoint create/write: 1 / 1;
- persistent deny lock created;
- checkpoint terminal state:
COMPACTION_GUARD_TRIPPED; detected=true;task_complete=false;post_compaction_work_allowed=false.
The guard then rejected mutation in both relevant states:
- same-process post-compaction mutation attempt: rejected, exit
73; - mutation attempt after restart: rejected, exit
73; - restart path did not start a new app-server child before the denial.
The controlled target remained byte-for-byte unchanged across the live run:
- pre file count:
42; - post file count:
42; - content diff:
0; - metadata diff:
0; - pre tree SHA-256:
1c2c0486700f8f75bb2f63a00fd975a1a0515c4a63e4cefaa7cb59cc9ad77cea; - post tree SHA-256:
1c2c0486700f8f75bb2f63a00fd975a1a0515c4a63e4cefaa7cb59cc9ad77cea.
The live validation was closed read-only after the original validation session itself hit context compaction during final evidence review. The successful run was not promoted until the external evidence, raw event stream, checkpoint, deny lock, process termination, and hashes were independently re-read.
Proposed product behavior
An opt-in policy could look conceptually like:
RUNNING_MUTABLE
-> context compaction observed
-> CHECKPOINTED_UNVERIFIED
-> mutation_authority = false
-> explicit recovery / durable-state re-verification
-> RUNNING_MUTABLE
Possible configuration semantics:
context_compaction_policy = "continue" | "pause" | "fail_closed"
For fail_closed, I would expect at least:
- a first-class pre/post compaction runtime event;
- durable checkpoint identity;
- explicit
task_completestate; - explicit mutation-authority state;
- persistent denial across restart/resume;
- a supported recovery/re-verification transition;
- clear UI status explaining why writes are blocked.
This does not require disabling compaction. It only separates a context-management event from authorization to continue mutating external state.
Relationship to existing reports
This proposal appears related to, but is not the same as:
- #36712 — automatic compaction can destroy active task context;
- #35935 — post-compaction task-state regression and repeated work;
- #29356 — preserve recent operational continuity;
- #34963 — preserve a configurable raw tail during compaction;
- #25660 — post-compaction resume-state regression.
Those issues primarily ask Codex to preserve or restore enough state to continue. This proposal adds a safe failure mode when that guarantee is not available.
A useful invariant would be:
unverified_post_compaction_state != mutation_authority
Expected benefit
This would not eliminate hallucinations generally. It would constrain one specific and observable failure class in long-horizon mutable workflows: unsupported continuation after a context-compaction boundary.
For tasks involving builds, release preparation, evidence generation, repository modification, or other externally persistent effects, an opt-in fail-closed boundary would provide a deterministic alternative to silently trusting reconstructed execution state.
4 Comments
Clarification on the reference implementation and disclosure boundary:
The validated Compaction Guard is being kept separate from my private GenAI Evidence Workbench technical-review repository. That private repository is not intended to be published or offered as reproduction material for this issue.
I plan to publish the guard, after a sanitized standalone build and independent hash closure, in a dedicated public repository under
hiroki-tamba-research. The public package will contain only the guard-specific implementation, tests, threat model, checkpoint/deny-lock format, sanitized reproduction evidence, and release hashes needed to reproduce the fail-closed behavior described above.Until that standalone package is published, the quantitative live-validation results in this issue should be read as the independently re-verified evidence boundary. I will add the public reference-implementation link here once that build is closed and available.
This separation is intentional: the mitigation can be evaluated independently without exposing unrelated GenAI Evidence Workbench implementation or review material.
Hi @hiroki-tamba-research, that rollout size is valuable stress evidence. Codex Rescue Alpha5 scans large local session histories with bounded memory and produces aggregate size, media, and compaction diagnostics without loading the whole file into RAM or dumping full payloads.
If you still have an affected closed rollout, you can test it locally:
No raw session upload is needed; sanitized aggregate output is sufficient.
Two pieces of grounding for this proposal from the current code and open-issue evidence:
Stoppedalready aborts the turn right at the compaction boundary (run_post_compact_hooks→PostCompactHookOutcome::Stopped→TurnAborted, https://github.com/openai/codex/blob/1f41cc5d92/codex-rs/core/src/compact.rs#L216-L218, mirrored incompact_remote.rs). So step 2 of your fail-closed sequence — halt at the transition — is expressible today with a user-configured hook; what's missing is the rest: checkpointing, a persistent mutation-denial flag that survives resume, and a re-verification gate (hooks currently can't durably alter the thread's permission state).Given (1), a pragmatic first increment: extend the post-compact hook contract with a
deny_mutationsoutcome that flips the session's permission profile to read-only and records it in thread settings (which already persist and support mid-thread overrides —ThreadSettingsOverrides.permission_profile). That gets checkpoint-and-revoke without new subsystems; the re-verification UX can layer on top.One implementation detail could keep this fail-closed mode narrow without making recovery binary: model recovery as a typed, expiring re-verification receipt, not a broad re-enable.
The receipt could bind:
The runtime would accept only a receipt that covers the requested next mutation. A fresh read might restore read-only analysis, while a network or browser write still requires its own scoped re-verification. That keeps “state reconstructed” separate from “right to act,” and makes audit/replay unambiguous: every post-compaction mutation cites a particular receipt and scope rather than merely a resumed turn.
Disclosure: I’m building Klik, a pre-launch product exploring Sessions → persistent Context → permission-aware follow-through. This is an architectural perspective, not a claim of Codex integration or availability: Klik pre-launch page.