Remote/bridge-attached sessions silently disable threshold auto-compaction and ignore CLAUDE_AUTOCOMPACT_PCT_OVERRIDE unless an explicit window is configured

Open 💬 3 comments Opened Jun 24, 2026 by jens-f

Summary

In a remote/bridge-attached session (i.e. one driven through the cloud/remote-control bridge), synchronous threshold auto-compaction never runs, and CLAUDE_AUTOCOMPACT_PCT_OVERRIDE is silently ignored entirely, unless an explicit window source is configured (CLAUDE_CODE_AUTO_COMPACT_WINDOW or a settings.autoCompactWindow). The context window grows unbounded (observed to ~238k tokens on a model whose auto-compact window was configured far lower) with zero auto-compaction; only manual /compact reduces it.

The trigger predicate WKp returns early — before it ever computes the token level or applies the percentage override — whenever the session is remote and the auto-compact-window source is the default "auto"/"experiment".

Environment

  • Claude Code 2.1.186 (native install), model claude-opus-4-8
  • Session driven via the remote/bridge worker (CCR v2 worker session)
  • settings.json env in effect: "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "80"
  • No CLAUDE_CODE_AUTO_COMPACT_WINDOW and no autoCompactWindow setting (the natural config: "auto-compact at 80%").

Observed behavior

  • Auto-compaction is enabled (the background precompute fired — precomputed compact: started/ready/consumed — which requires auto-compact to be on).
  • Despite CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=80, the conversation grew to ~238k tokens with no auto-compaction applied (it should have tripped far earlier).
  • The session was run with verbose+debug logging on, yet the debug log contains zero autocompact: tokens=… level=… lines — the orchestrator's own log statement never executed.
  • Manual /compact worked every time; it was the only thing that actually reduced context.

Root cause

The orchestrator XSo (exposed as p.autocompact) is called unconditionally every turn from the main REPL loop (the bridge injects inbound messages into that same loop), so it is invoked. The suppression is in its predicate WKp, which bails at its 4th guard — before the level computation, the percentage override, and the autocompact: log:

async function WKp(e, t, n, r, o = 0) {
  if (r === "compact") return false;
  if (hke(r)) return false;
  if (!ZR()) return false;
  if (Yz() && !v4() && !gke(t, n)) return false;   // <-- bails here, before everything below
  let s = SA(e, IR(t)) - o, i = _ke(s, t, n);       // _ke -> Gkn applies CLAUDE_AUTOCOMPACT_PCT_OVERRIDE
  return (C(`autocompact: tokens=${s} level=${i.level} effectiveWindow=${ree(t, n)}`),
          i.level === "compact" || i.level === "blocked");
}

In a remote/bridge session, all three conjuncts of the 4th guard are true:

  • Yz() — true in remote mode.
  • !v4() — true (kill-switch off).
  • !gke(t, n) — true, because gke() returns true only when the auto-compact-window source is env / settings / clientdata / model-default:

``js
function gke(e, t) {
let { source: n } = p$(e, t);
return n === "env" || n === "settings" || n === "clientdata" || n === "model-default";
}
`
With no explicit window configured,
p$() falls through to source: "auto" (or "experiment"), so gke() is **false** → !gke is true → the guard fires and WKp returns false. No level is computed, nothing is applied, and the autocompact:` log never runs.

Why CLAUDE_AUTOCOMPACT_PCT_OVERRIDE is completely ignored

The percentage override is read separately (into testPctOverride) and only applied downstream, inside _keGkn, which runs after the guard above:

// EKr(): testPctOverride = parseFloat(CLAUDE_AUTOCOMPACT_PCT_OVERRIDE)
function Gkn(e, t) {                       // e = effective (ree) window
  let n = e - 13000, r = t.testPctOverride;
  if (r !== undefined && !isNaN(r) && r > 0 && r <= 100)
    return Math.min(Math.floor(e * (r / 100)), n);
  return n;
}

Two compounding facts make the override a complete no-op in this scenario:

  1. CLAUDE_AUTOCOMPACT_PCT_OVERRIDE does not change the window source — it flows through EKr.testPctOverride, never through p$(). So it cannot, by itself, make gke() true.
  2. Because gke() therefore stays false, WKp bails at the 4th guard before _ke/Gkn are ever called — so the override value is never read on the trigger path.

Net: a user who sets only CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=80 (the obvious way to say "auto-compact at 80%") in a bridge session gets no effect at all — it neither lowers the threshold nor unlocks the orchestrator. It is silently ignored.

Workaround

Set an explicit window, which forces source: "env"gke() true → the 4th guard is disarmed → WKp proceeds, computes the level, applies the pct, and applies compaction:

"CLAUDE_CODE_AUTO_COMPACT_WINDOW": "150000"

With this set, CLAUDE_AUTOCOMPACT_PCT_OVERRIDE does take effect and composes with the window (threshold ≈ floor((window − reserve) × pct/100), e.g. ~104k for window 150000 + pct 80). Confirm it's working by checking the debug log for autocompact: tokens=… level=… lines, which should now appear.

(Note: once engaged, a custom window routes apply through the reactive path L8n/hSo, which can itself silently skip on a compaction abort or when the precompute isn't ready — so the window is necessary and near-sufficient, not bulletproof.)

Suggested fix

The 4th guard conflates "remote mode" with "no explicit window source" to suppress synchronous auto-apply, leaving threshold compaction unhandled for the default-config remote case. Options:

  1. Don't suppress synchronous auto-apply solely because source ∈ {auto, experiment} in remote mode — fall through to the level check regardless of source (and let the reactive routing branch handle the apply if that's intended).
  2. If the intent is to always route remote threshold compaction through the reactive path, ensure that path is actually reached for source: "auto"/"experiment" (today XSo's reactive branch requires p !== "auto", so the default source also misses that path — nothing applies).
  3. Make CLAUDE_AUTOCOMPACT_PCT_OVERRIDE set the window source (or otherwise be honored) so that "auto-compact at N%" works without also requiring an explicit window — at minimum, it should not be silently ignored.

Reproduction / detection

  • Remote/bridge-attached session, claude-opus-4-8, auto-compact enabled, no CLAUDE_CODE_AUTO_COMPACT_WINDOW / autoCompactWindow set; optionally CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=80.
  • Let the conversation grow past the expected threshold.
  • Symptom: context climbs well past the configured percentage with no auto-compaction; ~/.claude/debug/<session>.txt contains precomputed compact: lines but no autocompact: tokens=… level=… lines.
  • Setting CLAUDE_CODE_AUTO_COMPACT_WINDOW=<value> makes the autocompact: lines appear and compaction apply — confirming the source-gated suppression.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗