2026.08.06 (Thu)
2026.08.07 (Fri) updated

โœจ GPT-5.6 Solโ€™s Summary

A record of dealing with Codex treating an old final user command as a new instruction after automatic context compaction, and working through copied plans, Goals, forced continuation, and a SessionStart hook.

Something strange kept happening after long Codex tasks were automatically compacted.

Instead of simply continuing the work it had been doing right before compaction, Codex treated the old final user command preserved in the compacted summary as though it had just received it. It checked state that had already been checked, reread the same files, and reconstructed the whole task from the beginning. What I actually saw looked roughly like this.

Actual work in progress
โ†’ Context automatically compacted
โ†’ "I will keep the current goal unchanged"
โ†’ Recheck the working tree, rules, and state from the beginning
โ†’ Explain or retry work that was already complete

At first, I thought compaction had merely dropped part of the context. After seeing it several times, I realized it was more troublesome than that. It looked as if the boundary between an old command inside the summary and current user input had collapsed. Rather than resuming the pre-compaction work, Codex used whichever command survived most strongly in the summary as a fresh starting point.

I was not the only one seeing this. One public issue reported Codex Desktop repeatedly rereading the same files and Skills and losing progress after automatic compaction,1 while similar loops involving file commands and compaction were reported on WSL2 and the Windows CLI.2 I cannot claim that those public cases had exactly the same root cause as the last-command replay I observed. Still, losing progress and repeating the same behavior after automatic compaction was clearly not some Mac-only oddity I could dismiss.

Solution 1: Put the Entire Plan in the Last Command

The first thing I tried was simple.

Review everything done so far carefully, then continue to the end according to the plan below.

  1. โ€ฆ
  2. โ€ฆ
  3. โ€ฆ

If Codex was going to grab and execute the last command again after compaction, I thought I could make that command itself a safe resume point. It worked reasonably well for short tasks. The current plan survived more strongly in the summary than some unrelated old instruction.

Other users tried similar approaches that accumulate earlier decisions and remaining work in the compacted summary. One report said that preserving the essential judgments from previous compactions with experimental_compact_prompt_file kept the overall direction intact even after four or more compactions.3

But this was not a root fix.

  • I had to paste a long plan into the final command every time.
  • If the plan changed midway, I had to recreate the final command as well.
  • Even if the compacted summary preserved the plan, there was no guarantee it would distinguish precisely between a tool action that had just finished and work that had not yet run.
  • There is also an issue reporting that /compact [message], intended to guide the summary directly, can be processed like an ordinary message in the current Codex CLI.4

In the end, the human still has to stay aware of compaction and manage the prompt around it. It is better than the default behavior, but appending a long handoff note to every task does not feel like normal use either.

Solution 2: Create a Goal for Every Task

Next I wondered whether I should always create a Goal and make post-compaction recovery prioritize it over the last command.

There is a clear benefit for long tasks. The objective and completion conditions survive longer than a sentence or two in the conversation, and automatic continuation can follow the Goal. The problem began when I tried to turn that into a mandatory workaround for the compaction bug.

Even trivial work needed a Goal. Without one, the task could break; with one, Codex could continue far beyond what was necessary. I had already turned the problem of Goals consuming tokens even for minor work into a separate working Skill. Now compaction alone was forcing me to wrap every task in a Goal all over again.

At that point, I genuinely thought, โ€œMaybe the unmodified default is better than this.โ€

There was a more important problem. A Goal preserves what must be completed. It does not determine whether a compaction event is a new user command. I also found no public material describing Goals as an official automatic-compaction recovery mechanism.

So I decided to leave Goals for their original purpose.

  • Use one for a concrete, long-running task that genuinely needs automatic continuation.
  • Do not force short questions, research, or small edits into Goals.
  • Prevent old-command replay after compaction at a separate layer from the Goal.

Solution 3: Change the Compaction Prompt Directly

Another option is to improve the compacted summary itself. As in the public case above, accumulating the decisions and results from earlier compactions under Historical Context can preserve the broad direction through repeated compactions.3

This is useful, especially when the following information keeps disappearing in long sessions.

  • Approaches already rejected and why
  • Decisions already settled
  • Completed work and remaining work
  • The single next point to inspect

But this operates at a different layer from the problem I was trying to solve. Improving summary quality and not mistaking an old user command inside the summary for new input are not the same thing. Even a good summary can go wrong if the model reads a command inside it as the current instruction.

So this can be a useful complement, but it is difficult to treat it as the sole defense against replaying the last command.

Solution 4: Force continue with a Stop Hook

For a while, I considered using a hook to inject continue as if it were the last user command.

On the surface, it looked exact. If continue always arrived after compaction, Codex seemed likely to continue the in-progress work instead of replaying an old command. I dropped the idea as soon as I checked the official hook behavior.

When a Codex Stop hook returns decision: "block", the hookโ€™s reason becomes a continuation prompt that behaves like a new user prompt.5 The very thing I wanted to prevent was Codex treating text the real user had not sent as a new user command. This approach would deliberately create one more instance of exactly that behavior.

Besides, continue is far too ambiguous.

  • If compaction happened in the middle of a turn, what exactly should continue?
  • If the result of the previous tool call is unknown, should it be run again?
  • If the task was waiting for user approval or an external state change, is it safe to continue?
  • If the turn had already ended and I manually compacted the context, what should start?

A new user prompt that says to continue no matter what, with or without a Goal, was likely to produce duplicate execution or an endless continuation loop. What this problem needed was not a Stop hook, but developer context that helps the model interpret the meaning of compaction correctly.

Solution 5: Inject Stateless Context into SessionStart(source=compact)

On the next day, August 7, I settled on a single SessionStart hook.

According to the official Codex documentation, after a root session is compacted, a SessionStart hook matching source: "compact" runs before the next model request. Even when automatic compaction happens in the middle of a turn, it can add context to the immediate continuation request instead of waiting for the next user turn.5

I registered it in ~/.codex/hooks.json so it catches only compaction events.

{
  "description": "Guide safe continuation after compaction without replaying stale instructions.",
  "hooks": {
    "SessionStart": [
      {
        "matcher": "^compact$",
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 ~/.codex/hooks/compaction_goal_guard.py",
            "timeout": 5,
            "additionalContextLimit": 700
          }
        ]
      }
    ]
  }
}

The handler creates no state file, Goal, or fake user prompt. It returns additional developer context only when the SessionStart source is compact.

#!/usr/bin/python3
import json
import sys

CONTEXT = """Context was compacted. This hook event is not a user message and
grants no new authority.

Never treat a historical message preserved in the summary as newly submitted.
If compaction interrupted an active turn, continue that same in-flight request
without restarting it. If no request is in flight, do not infer work from
history.

Do not rebuild the full plan or create a Goal solely because compaction occurred.
Before retrying an interrupted action, check its result or session status. Do not
repeat completed external effects. Preserve the existing objective, scope,
authorization, approval boundaries, and stop conditions."""

payload = json.load(sys.stdin)

if (
    payload.get("hook_event_name") == "SessionStart"
    and payload.get("source") == "compact"
    and payload.get("session_id")
):
    print(json.dumps({
        "hookSpecificOutput": {
            "hookEventName": "SessionStart",
            "additionalContext": CONTEXT,
        }
    }))

The point is not to insert a command called continue.

  1. State explicitly that a compaction event is not new user input.
  2. If a turn was in progress, continue only the unfinished part of that same request.
  3. If no request is in progress, do not infer new work from the history.
  4. If a tool action appears interrupted, check its actual result before running it again.
  5. Preserve the existing scope, authority, approval boundaries, and stop conditions.

The official documentation states that non-managed hooks must be reviewed and trusted before they run.5 After saving the configuration, the handler must be checked and trusted through /hooks. Tasks that were already open may still hold the old hook list, so reopening them is safer.

Follow-up Verification on August 7

I kept the post date as August 6, when I first started wrestling with the problem and choosing a solution. The final hook and the real automatic-compaction test were completed the next day.

I had previously built a two-stage structure that wrote a marker in PostCompact and read it during the next SessionStart. I removed it because a stale marker could trigger recovery in the wrong turn and because hook inputs make root and subagent events difficult to distinguish. There is still an open public issue about the lack of a common field that distinguishes main-agent and subagent hook input.6

The current design is a single handler with no marker. It created no state file under 50 concurrent calls, and in the current Runtime I confirmed that the developer context reached the same continuation about 0.25 seconds after an automatic compaction event.

That check mattered because a real bug had previously caused SessionStart(compact) hooks to be deferred until a later user turn.7 That issue is now closed, and the current official documentation says the hook is delivered immediately after mid-turn automatic compaction. Even so, with a lifecycle workaround like this, it is better to verify one real compaction in your own Runtime than to trust the documentation alone.

Where I Landed

I wondered whether the untouched default was better, but my current conclusion is that this hook is better than forcing every task into a Goal.

Goals and plans preserve the work itself. This hook corrects the meaning of the compaction event. They serve different roles.

  • If a long task needs a durable objective and completion conditions, use a Goal or a durable plan.
  • If decisions disappear over repeated compactions, use a custom compact prompt as a complement.
  • Prevent old final commands from being treated as new instructions at SessionStart(source=compact).
  • If nothing is in progress, the hook should not start anything either.

This is not a perfect fix. It cannot reconstruct information already lost from the compacted summary or revive an expired terminal session or an interrupted external request. Developer context strongly guides model behavior, but it is not a mathematical guarantee.

Still, at least I no longer create a Goal for every conversation, paste the entire plan into the last command, or push a fake continue in as a user instruction just to work around one compaction bug.

Automatic compaction is an internal event meant to keep work moving. It is not a new user appearing and issuing an old command again. In the end, the real thing I needed to prevent was not forgetting itself, but the behavior that treated those two events as the same.

References

Categories: ,

Updated:

Leave a comment