Skip to content

fix(core): stop auto-compaction looping when compaction cannot get below the limit (WI-2832) - #36

Open
thedancingdeveloper wants to merge 2 commits into
fork/mainfrom
wi-2832-compaction-thrash-guard
Open

thedancingdeveloper wants to merge 2 commits into
fork/mainfrom
wi-2832-compaction-thrash-guard

Conversation

@thedancingdeveloper

Copy link
Copy Markdown

Fixes a compaction loop seen on 2026-11-01 (Vogt WI-2832). Operator-approved one-off PR into fork/main: the fork has no graft line.

Root cause

  • Two swack-haiku sessions compacted 235 and 143 times in one turn, using about 60M tokens.
  • Each cycle: a ~132k-token request (over model_auto_compact_token_limit = 128000), a compaction, one tool call, then ~132k again.
  • History was not what survived compaction. A session in the same checkout 23 s earlier had byte-identical history, world state and turn context, and started at 42.9k tokens. The thrashing session started at 130.5k.
  • The extra ~87k tokens were MCP tool definitions. These sessions were launched without Vogt's worker MCP set, so Codex loaded every server in ~/.codex/config.toml. Compaction cannot shrink tool definitions, so the fixed prefix alone was over the limit.
  • In core/src/session/turn.rs, the turn loop compacted whenever token_limit_reached was true and continued, without checking whether compaction helped. The old comment read: "as long as compaction works well … we shouldn't worry about being in an infinite loop".

The launch-side cause is tracked separately in Vogt (WI-2833: start Codex workers with the worker MCP set).

Fix

  • After a context-limit compaction (pre-turn or mid-turn), the turn checks the prompt size of the first request that follows (last_token_usage.input_tokens).
  • If that prompt alone is at or over the auto-compact limit, or the usable context window, the turn:
    • emits a Warning naming both sizes and suggesting fewer MCP servers or a higher model_auto_compact_token_limit;
    • emits Error ContextWindowExceeded (non-retryable) and ends without compacting again.
  • With the body_after_prefix scope, only the body after the prefix counts against the limit.
  • run_pre_sampling_compact now returns whether it compacted, so a pre-turn compaction is checked too.

Tests

  • Unit tests session::turn::tests::prompt_over_limit_detects_a_prefix_that_compaction_cannot_shrink and prompt_over_limit_ignores_the_prefix_for_body_after_prefix_scope.
  • Integration test suite::compact::auto_compact_stops_when_compaction_cannot_get_below_limit: a 400-token step against a limit of 300, one compaction, then a 400-token step. It asserts exactly three requests, a ContextWindowExceeded error and the warning. With the guard disabled, the test fails.
  • Run locally (RUST_MIN_STACK=8388608):
    • cargo check -p codex-core --tests: clean
    • cargo clippy -p codex-core --tests -- -D warnings: clean
    • rustfmt --check on the changed files: clean
    • cargo test -p codex-core --lib prompt_over_limit: 2/2
    • cargo test -p codex-core --test all -- suite::compact::auto_compact: 13/13

FORK.md

Adds a carried-patch row describing the auto-compaction loop guard: the files touched, the tests, and the branch.

🤖 Generated with Claude Code

…low the limit (WI-2832)

When the part of the prompt that compaction cannot shrink (tool
definitions, instructions, retained context) is by itself over
model_auto_compact_token_limit, every request after a compaction is over
the limit again. The turn loop compacted, ran one step, and compacted
again with no bound: two swack-haiku sessions on 2026-11-01 did 235 and
143 compactions in one turn (60M tokens).

After a context-limit compaction (pre-turn or mid-turn), check the first
request's prompt size. If it alone is at or over the auto-compact limit
(the body after the prefix for the body_after_prefix scope) or the usable
context window, emit a warning naming the sizes, end the turn with
ContextWindowExceeded, and do not compact again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rust-heavy label is also served by container runners such as
nodef-gha-public-docker, which do not bake the build packages, so
codex-bwrap's build.rs panics with "libcap not available via pkg-config"
and the required "fmt, clippy, test" check fails (fork/main red since
529603b). Install libcap-dev and pkg-config there before the build,
using sudo only when not root, and skip when libcap is already present
(the baked image, whose jobs have no sudo).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant