Context compaction can turn completed plans into active work, causing repeated investigation loops

Open 💬 9 comments Opened Aug 17, 2026 by GeYugong
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

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:

  1. inspect repository / routes / UI behavior
  2. collect findings
  3. context compaction occurs
  4. old plan is restored as current work
  5. inspect the same areas again
  6. repeat after later compactions

Steps to reproduce

This is easiest to observe in a long-running Desktop task:

  1. Ask Codex to perform a multi-step read-only investigation before making changes.
  2. Let it execute several checks and report intermediate findings.
  3. Continue until automatic context compaction occurs.
  4. Observe the first planning/status messages after compaction.
  5. 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.

View original on GitHub ↗

9 Comments

github-actions[bot] contributor · 11 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #38466
  • #38489

Powered by Codex Action

GeYugong · 11 days ago

<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.

GeYugong · 11 days ago

#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.

jdcodes1 · 11 days ago

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:

Include: - Current progress and key decisions made - Important context, constraints, or user preferences - What remains to be done (clear next steps) - Any critical data, examples, or references needed to continue

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_prompt config option (codex-rs/core/src/config/mod.rs). Something like your proposed structure works as a drop-in:

compact_prompt = """
You are performing a CONTEXT CHECKPOINT COMPACTION. Create a handoff summary for another LLM that will resume the task.

Use exactly these sections:
GOAL / USER CONSTRAINTS / CONFIRMED FINDINGS / COMPLETED ACTIONS / IN PROGRESS / NEXT STEPS

Rules:
- Every plan or intention stated earlier in the transcript must be classified: if it was already executed, it belongs under COMPLETED ACTIONS with its outcome, never under NEXT STEPS.
- NEXT STEPS may only contain work with no evidence of execution in the transcript.
- Be concise and structured.
"""

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.md plus its snapshot tests.

One additional data point for maintainers: the compaction request is just the transcript plus this prompt (run_compact_task_inner in core/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.

anseljh · 11 days ago

I hit a more severe version of this in Codex CLI 0.147.0 on Linux during a long coding/evaluation session.

Evidence:

  • the parent session persisted locally with 8,049 JSONL events;
  • automatic compaction occurred at event 7,419;
  • after reconstruction, the agent no longer had enough active task/tool state to continue reliably and told the user to start a fresh session;
  • the user correctly pointed out that a large portion of the ongoing session had effectively disappeared from the agent's working context;
  • the underlying transcript and uncommitted worktree were still intact, so this was not data deletion—it was failed context/task-state recovery.

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.

anseljh · 11 days ago

Correction to my prior comment: this was not automatic compaction.

Actual sequence:

  1. The user manually compacted the session.
  2. They continued with several more prompts after that compaction.
  3. They exited the Codex session.
  4. On resuming it, those newer post-compaction turns were absent from the active conversation context.

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.

ahmashadani · 3 days ago

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

nos1609 · 2 days ago

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.

GeYugong · 2 days ago

Update from the issue reporter: I traced this against the current public
main at 039eb58a0ba6647fb8f29fdd35341f3f1b153728 to determine what
the 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, and remote_compaction_v2 is Stable and enabled by default. compact_prompt is consulted by the local RemoteCompactionSupport::Unsupported path, but remote v2 sends the current history plus ResponseItem::CompactionTrigger and receives an opaque ResponseItem::Compaction.

Therefore, the current local prompt.md does expose a real state-classification weakness, but it does not directly confirm the root cause of this Desktop reproduction, and overriding compact_prompt is not a reliable workaround for the default OpenAI path.

What the public client code does confirm is a broader execution-state problem:

  1. Local compaction rebuilds history from retained user messages plus a model-generated summary. Historical assistant/tool evidence survives only if the summary preserves it correctly.
  1. Remote v2 installs an opaque compaction item plus a filtered retained tail. Raw function calls, tool outputs, shell calls, and reasoning are not retained as operational evidence. For multi-agent messages, bounded non-completion task messages are intentionally retained while FINAL_ANSWER completion 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.
  1. update_plan is not an authoritative Runtime state store. It persists EventMsg::PlanUpdate, but Core does not reduce those events into a current plan during rollout_reconstruction, and the reconstructed session state contains no execution ledger. After a compaction boundary, a completed transition may therefore survive only as prose.
  1. CompactedItem already carries a separate rollback-aware mcp_resource_origins checkpoint, added by #39192, but it has no equivalent execution-state checkpoint.

This creates the structural failure mode reported here:

task intent survives

  • completion evidence is summarized or filtered
  • no authoritative Runtime state contradicts a stale “next step”

= 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:

  • the pre-compaction history and any available plan/tool evidence;
  • the returned ResponseItem::Compaction;
  • the installed replacement_history;
  • the first post-compaction model input.

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

  • rollback-aware reconstruction
  • authoritative model-visible World State section

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.