Pre-turn compaction clears reasoning-included state before accounting

Open 💬 1 comment Opened Jul 11, 2026 by shtratos

What version of Codex CLI is running?

0.144.1; also reproduced from source on main@c4318c386de365bd0dd9595a08d55a30bb142d11

What subscription do you have?

API / custom Responses provider

Which model were you using?

Any Responses model/provider that returns X-Reasoning-Included: true

What platform is your computer?

Linux 6.6.87.2-microsoft-standard-WSL2 x86_64

What terminal emulator and version are you using (if applicable)?

Codex CLI in WSL2

Codex doctor report

{
  "note": "The bug has a deterministic source-level integration test and is not dependent on local installation state."
}

What issue are you seeing?

Codex can trigger premature pre-turn auto-compaction after a response reports
X-Reasoning-Included: true.

server_reasoning_included tells context accounting that the server's input
usage already includes replayed encrypted reasoning. When true,
History::get_total_token_usage() does not add a local encrypted-reasoning
estimate on top of server usage.

The regular-task startup path currently clears that flag before run_turn():

sess.send_event(ctx.as_ref(), event).await;
sess.set_server_reasoning_included(/*included*/ false).await;

However, run_turn() performs pre-turn compaction before it sends the new
sampling request:

run_pre_sampling_compact(&sess, &turn_context, &mut client_session).await

As a result, pre-turn accounting loses the previous response's reasoning
contract and can double-count historical encrypted reasoning. Threads can then
compact substantially before their configured token limit.

This is an ordering issue, not a request-header parsing issue. The response
event correctly sets the flag to true, but the next regular task clears it
before the code that still needs it.

What steps can reproduce the bug?

The following integration-test shape reproduces it deterministically:

  1. Configure an auto-compaction limit of 300 tokens.
  2. Return two Responses streams containing encrypted reasoning items.
  3. Set X-Reasoning-Included: true on both responses.
  4. Report low server usage (10, then 80 tokens), below the compaction limit.
  5. Submit a third user turn.
  6. Assert that /v1/responses/compact was not called.

On unmodified main, the assertion fails twice under nextest:

thread 'suite::compact::auto_compact_preserves_reasoning_header_for_next_pre_turn_check'
panicked:
pre-turn accounting should retain the previous response's reasoning-included signal

Summary: 1 test run: 0 passed, 1 failed

The complete regression test and minimal fix are available in:

https://github.com/shtratos/codex/commit/545af526c

The fix moves the reset to immediately after run_pre_sampling_compact(), so
pre-turn accounting uses the previous response's flag while the upcoming
response still starts cleared and must reassert the header.

Focused verification on current main:

auto_compact_preserves_reasoning_header_for_next_pre_turn_check ... ok
auto_compact_runs_when_reasoning_header_clears_between_turns ... ok
auto_compact_counts_encrypted_reasoning_before_last_user ... ok

The existing omitted-header test is important: it proves the change preserves
the per-turn behavior introduced by #9402. If the next response omits the
header, Codex still re-enables local encrypted-reasoning accounting.

What is the expected behavior?

Pre-turn compaction should account for the existing history using the previous
response's X-Reasoning-Included value. The flag should be reset only after
pre-turn compaction and before the new response stream starts.

Additional information

The tested source change is intentionally small:

  • remove the reset from regular-task startup;
  • reset immediately after successful pre-turn compaction;
  • add an integration regression test for consecutive header-bearing turns.

Validation completed on current main:

  • three focused codex-core compaction tests passed;
  • just fix -p codex-core completed without diagnostics;
  • just fmt completed successfully.

Per the external contribution policy, I am opening the issue first rather than
an unsolicited pull request. The tested branch is ready if a maintainer would
like to invite the PR.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗