Context compaction can turn completed plans into active work, causing repeated investigation loops
Summary
In long-running Codex tasks, context compaction can appear to preserve the content of an earlier plan while losing its execution state. As a result, work that was already performed can be reintroduced after compaction as if it were still the next action.
This causes repeated investigation/planning loops and wastes context/tokens.
What I observed
I asked Codex to inspect whether a UI migration had caused any functional regressions, with the constraint that it should first perform a read-only audit and not modify code.
Before compaction, Codex explicitly stated that it would perform a read-only audit and then proceeded with the inspection.
After context compaction, Codex again emitted essentially the same plan:
I’ll first do a read-only inspection, focusing on routes, key interactions, notifications, login flow, and static assets; I won’t modify code yet.
This was not a new user instruction. It was a restatement of an earlier plan that had already been acted on.
The session still remembered useful facts discovered before compaction (for example, that type checking passed and some routes/build artifacts had been checked), so the problem did not look like complete context loss. Instead, it looked like the compaction summary preserved an old plan without clearly marking whether it was completed, in progress, or still pending.
Why this matters
For agentic coding tasks, these are semantically different pieces of state:
- user goal
- constraints
- confirmed findings
- completed actions
- currently-running action
- remaining actions
If compaction collapses them into a generic narrative summary, an earlier statement such as “next I will inspect X” may be interpreted after compaction as a fresh instruction to inspect X again.
This can produce a loop like:
- inspect repository / routes / UI behavior
- collect findings
- context compaction occurs
- old plan is restored as current work
- inspect the same areas again
- repeat after later compactions
Steps to reproduce
This is easiest to observe in a long-running Desktop task:
- Ask Codex to perform a multi-step read-only investigation before making changes.
- Let it execute several checks and report intermediate findings.
- Continue until automatic context compaction occurs.
- Observe the first planning/status messages after compaction.
- In affected runs, Codex restates an earlier already-executed plan and begins re-investigating the same areas.
Expected behavior
Compacted context should preserve task state explicitly enough that completed work is not promoted back into pending work.
Conceptually, a compaction handoff should distinguish at least:
GOAL
USER CONSTRAINTS
CONFIRMED FINDINGS
COMPLETED
CURRENTLY INVESTIGATING
NEXT
The exact internal representation is not important, but the post-compaction agent should be able to tell that an earlier plan has already been executed.
Actual behavior
The compacted context appears to preserve the wording/intention of an earlier plan, but not always its temporal state. The next model continuation can therefore treat historical plans as current tasks and repeat work.
Related issues
#38466 mentions repeated status/planning messages after repeated compaction in very large Desktop threads, but this report is narrower: the issue is specifically loss of completed vs pending task-state semantics during compaction, which can directly cause duplicated tool work even when much of the factual context is still retained.
I can provide a screenshot showing the near-duplicate pre- and post-compaction planning messages if useful.
9 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
<img width="1320" height="1099" alt="Image" src="https://github.com/user-attachments/assets/1061e956-d3a5-41ae-8dd0-d3cf480b18ad" />
The screenshot shows that after context compaction, Codex repeated almost the same “read-only inspection” plan that it had already stated and acted on before compaction, suggesting that the compaction preserved the plan’s content but lost its completed/pending state.
#38489 appears related, but the failure mode here is effectively the inverse: instead of unresolved work being dropped and treated as complete, already-completed work is promoted back into pending work after compaction, causing duplicate investigation/tool execution.
Your hypothesis — "the compaction summary preserved an old plan without clearly marking whether it was completed" — is directly confirmed by the shipped compaction prompt. This isn't a model quirk; the prompt never asks for the distinction you're missing.
The summarizer's instructions. The built-in compaction prompt is
codex-rs/prompts/templates/compact/prompt.md, in full:There is no instruction to tag previously stated intentions with their execution state, no COMPLETED section, and no warning that pre-compaction "I will now do X" statements must not be restated as pending. A transcript where the assistant announced a read-only audit and then performed it gives the summarizer two candidate homes for that plan — "progress" or "next steps" — and nothing in the prompt penalizes filing it under the wrong one. Your exact loop follows.
The resume side can't compensate. The post-compaction prefix (
summary_prefix.md) does say "use this to build on the work that has already been done and avoid duplicating work" — but if the summary itself presents an executed plan as the next step, the resuming model has no signal to override it. The defect is upstream, in what the summary is asked to contain.Immediately testable workaround. The prompt is user-overridable via the
compact_promptconfig option (codex-rs/core/src/config/mod.rs). Something like your proposed structure works as a drop-in:If runs with this template stop re-planning after compaction (and I'd expect them to, given the mechanism), that's strong evidence for fixing the default template the same way — which would be a small, low-risk diff confined to
prompts/templates/compact/prompt.mdplus its snapshot tests.One additional data point for maintainers: the compaction request is just the transcript plus this prompt (
run_compact_task_innerincore/src/compact.rs) — there is no distinguished "current task state" input, so the completed/pending distinction must be reconstructed from the narrative, which is exactly where it gets lost. Where a structured plan exists (plan-tool step statuses), feeding the latest state into the compaction request as ground truth would make the classification trivial rather than inferential.I hit a more severe version of this in Codex CLI 0.147.0 on Linux during a long coding/evaluation session.
Evidence:
In this case the lost/blurred state included completed LAN CUDA evaluation work, pending uncommitted model-manifest changes, and the distinction between “the Hugging Face connector is newly configured” and “this active agent did not receive its tool schema.” The agent then made a bad recommendation to restart instead of recovering the existing parent transcript.
Expected behavior: after compaction, preserve a checkpoint that explicitly carries completed work, uncommitted changes, current user intent, and active integrations/tool availability. If a tool schema genuinely cannot be refreshed, the agent should say that narrowly—not collapse the session into “start over.”
I have not attached the session logs here because they contain project-specific details, but the local transcript shows the compaction and recovery timeline.
Correction to my prior comment: this was not automatic compaction.
Actual sequence:
The older transcript was recoverable from the local JSONL history, but the resume path did not restore the latest post-compaction conversation state. Please investigate persistence/checkpointing across manual compaction → subsequent turns → exit → resume, rather than only automatic-compaction summaries.
Related structured compact-handoff proposal (open AC ledger + completion gate) posted on #38489 — same root cause as this “completed plan reopened after compaction” failure mode.
https://github.com/openai/codex/issues/38489
Additional sanitized reproduction, with a confounding factor disclosed explicitly.
A long-running task used waves W1-W6. W1-W5 were complete and W6 was future work, but one active goal still covered both completed and future waves. After compaction, the agent selected a stale checkpoint, reopened W4, requested an approval that had already been granted, and revisited the same completed stage approximately 3-4 times. No new user instruction asked it to reopen W4.
The agent later identified the failure itself: after compaction it had loaded a stale checkpoint, and the broad active goal allowed continuation to treat completed W4 as pending again. The underlying transcript remained available; the failure was in execution-state selection, not raw-history deletion.
Confounder: this task also used an external post-compaction context injection. Therefore this occurrence cannot isolate whether the stale state originated in native compact output, additional context, or their interaction. It does show the same user-visible failure mode: preserved textual context did not preserve the completed/pending boundary.
Project names, prompts, local paths, hostnames, account data, and task IDs are omitted.
Update from the issue reporter: I traced this against the current public
mainat039eb58a0ba6647fb8f29fdd35341f3f1b153728to determine whatthe public code can and cannot establish about this failure.
One important implementation correction: the standard OpenAI path is not the local prompt-based compactor. Configured OpenAI/Azure Responses providers advertise
RemoteCompactionSupport::V2, andremote_compaction_v2is Stable and enabled by default.compact_promptis consulted by the localRemoteCompactionSupport::Unsupportedpath, but remote v2 sends the current history plusResponseItem::CompactionTriggerand receives an opaqueResponseItem::Compaction.Therefore, the current local
prompt.mddoes expose a real state-classification weakness, but it does not directly confirm the root cause of this Desktop reproduction, and overridingcompact_promptis not a reliable workaround for the default OpenAI path.What the public client code does confirm is a broader execution-state problem:
FINAL_ANSWERcompletion messages are excluded, as introduced by #36128. This means task intent can remain in raw history while completion evidence depends on the opaque compaction item.update_planis not an authoritative Runtime state store. It persistsEventMsg::PlanUpdate, but Core does not reduce those events into a current plan duringrollout_reconstruction, and the reconstructed session state contains no execution ledger. After a compaction boundary, a completed transition may therefore survive only as prose.CompactedItemalready carries a separate rollback-awaremcp_resource_originscheckpoint, added by #39192, but it has no equivalent execution-state checkpoint.This creates the structural failure mode reported here:
task intent survives
= completed work can be reopened as pending
So the public-code root cause is broader than an underspecified summary prompt: compaction is currently allowed to regenerate execution-control state from narrative history, with no Runtime invariant preventing either:
Completed → Pending (#38931)
Pending/Open criterion → Complete (#38489)
What I cannot determine from the OSS repository is where my exact Desktop run first diverged, because the remote-v2 compactor is server-side. A maintainer trace of the affected thread would need to compare:
ResponseItem::Compaction;replacement_history;I raised the same checkpoint-completeness concern in review on the now-closed draft #31175: semantic state reconstructed from the full rollout should equal semantic state reconstructed from the surviving compaction checkpoint plus tail. The draft was later closed by its author without a response to that review, so I do not interpret the closure as resolving or rejecting this issue.
The durable invariant should be:
ExecutionState_before_compaction
==
ExecutionState_after_compaction
unless a validated state transition actually occurred.
A likely implementation seam, consistent with the existing World State and MCP provenance mechanisms, is:
Runtime-owned execution-state checkpoint
A completed item should not silently return to Pending; it should require an explicit Reopened transition with a reason and new contradictory evidence. Conversely, an open item or acceptance criterion should not become Completed without evidence.
A focused regression test could deliberately return a compaction item saying “next inspect T1” after Runtime state has already marked T1 completed, and assert that T1 remains completed and cannot become the immediate next task. The mirror test should verify that an open acceptance criterion cannot disappear or become complete merely because the compaction item says the work is finished.