Auto-compaction stops firing entirely after v2.1.199 (regression from v2.1.92; still absent in v2.1.217)
Environment
- Deployment shape: a single long-running Claude Code CLI session per container
lifetime, invoked as claude --dangerously-skip-permissions --model claude-opus-4-8, restarted
--channels <2 chat-platform plugins> --append-system-prompt-file <file>
periodically (auth/error recovery, scheduled context-hygiene recycle, or container
rebuild) — so "one session" below means one CLI process lifetime, one
~/.claude/projects/<cwd-slug>/<sessionId>.jsonl transcript file.
- Model:
claude-opus-4-8(1M-context beta), set via--model, unrelated to the CLI
version under discussion — included here only in case the bugs below are
model/beta-header-interaction-specific rather than pure-CLI bugs.
- Two chat-platform integration MCP plugins are loaded (a Discord-shaped one and a
Telegram-shaped one), plus several first-party MCP servers (browser automation via
Playwright, a graph-database server, a Google-Workspace server). The bugs below are
plugin/MCP-agnostic in principle; Bug 2 was only actually observed in sessions using
the browser-automation (Playwright) MCP server heavily, and one of Bug 3's three
confirmed instances froze immediately after a Playwright tool result — the other two
froze on an idle turn boundary with no tool call involved, so Bug 3 does not appear
to be Playwright-specific the way Bug 2 might be.
- Bug 3's evidence (below) additionally draws on an internal watchdog process (our own
infrastructure, not part of the CLI) that polls session liveness independently of the
CLI and keeps its own status/log stream — included here only as the source of the
wall-clock probe/restart timestamps that corroborate the transcript-level gaps.
- CLI versions in play: 2.1.92 (last known-good), 2.1.199 (the version an
unpinned @latest silently resolved to on 2026-07-02, and the version Bugs 1–2 were
originally reported against), and 2.1.217, deliberately pinned in the deployment's
Dockerfile on 2026-07-22 (it was the actual npm latest/next dist-tag at pin time;
npm's stable dist-tag has since moved independently of what's actually deployed
here) and running in this deployment since ~2026-07-24.
- Update (2026-07-26) — 2.1.217 status for all three bugs, based on the 11 session
files run on 2.1.217 to date (2026-07-24 → present):
- Bug 1 (auto-compaction not firing): unchanged.
grep -c '"isCompactSummary":true'
across all 11 files is 0, same as on 2.1.199.
- Bug 2 (pseudo-invoke artifact): has not recurred on 2.1.217 so far — 0
instances across the 11 files, versus 76 across 4-of-14 sessions on 2.1.199. The
2.1.217 sample is smaller and we have not confirmed it contains sessions as
Playwright-heavy as the worst 2.1.199 ones (the correlation noted under Bug 2), so
we are not claiming this is fixed — only that it hasn't reproduced yet in the
window we've observed.
- Bug 3 (this addendum, mid-session freeze): confirmed present on 2.1.217 —
two new instances, 2026-07-25 and 2026-07-26, both ending in a watchdog-triggered
restart. The instance that originally surfaced this bug (2026-07-18) was on 2.1.199
and self-recovered without a restart; see the Bug 3 section for all three.
- No npm changelog ships with the package (checked: no
CHANGELOG.mdin the
installed package tree), so the regression below is bisectable only to "somewhere
between 2.1.92 and 2.1.199" — not to a specific intermediate version. Narrowing that
would require installing and replaying against intermediate versions, which we have
not done (this report is from read-only inspection of the two versions actually
deployed).
---
Bug 1 — auto-compaction stops firing between 2.1.92 and 2.1.199
Symptom
Automatic context compaction (the CLI's own "conversation is getting long, summarize
and continue" behavior) fired regularly on 2.1.92 and has not fired even once in three
weeks of continuous use on 2.1.199, across sessions that are at least as long (by
wall-clock span and, per our own operational logs, by turn count and tool-call volume)
as the ones that compacted on 2.1.92. Nothing in our configuration changed between the
two — the only change was the CLI version, via an unpinned @latest install resolving
differently on a routine rebuild.
Config state (ruled out as the cause)
Three settings can affect this, checked in every place they could plausibly be set in
this deployment — none is present:
DISABLE_COMPACTenv var — not set in the container's environment (checked the
compose service's env block and the running container's environment).
DISABLE_AUTO_COMPACTenv var — not set (same check).autoCompactEnabledpersisted setting — not present as a key in either the
user-level config file or the project-level .claude/settings.json; per the schema
(below) it's optional and defaults to true when absent.
The binary's own gating logic (found via strings/grep -a on the installed CLI)
Running grep -a -o '.\{0,150\}DISABLE_COMPACT.\{0,150\}' against the installedclaude executable (a bundled/minified Node binary) surfaces the gate as a small
cluster of related feature-flag env vars:
...DISABLE_COST_WARNINGS:()=>v1u,DISABLE_COMPACT:()=>T1u,DISABLE_BUG_COMMAND:()=>H1u,
DISABLE_BRIEF_MODE_STOP_HOOK:()=>A1u,DISABLE_AUTO_COMPACT:()=>S1u,
DISABLE_AUTOUPDATER:()=>E1u,...
and the actual gating function body:
function $C(){
if(De.DISABLE_COMPACT) return !1;
if(at(process.env.DISABLE_AUTO_COMPACT)) return !1;
return bc("autoCompactEnabled",!0).value
}
($C in the minified bundle — symbol name will differ per build/minifier pass, but
the shape — two hard-off env checks, then a settings lookup with coded default true
— should be stable and locatable the same way in any 2.1.x build.) The same binary's
embedded Zod schema confirms the setting's declared shape and default:
autoCompactEnabled:A.boolean().optional().describe(
"Automatically compact conversation when context fills")
and it appears in the CLI's own list of recognized settings keys:
Ylt=["theme","editorMode","verbose","preferredNotifChannel","autoCompactEnabled",
"autoScrollEnabled","fileCheckpointingEnabled", ...]
So by the binary's own logic, with nothing in our environment or settings overriding
any of the three gates, $C() should return true and auto-compact should be active
on 2.1.199 exactly as it was on 2.1.92. It is not firing in practice.
Transcript evidence
Compaction leaves an unambiguous, structurally-tagged marker in the session
transcript: a user-role row with "isCompactSummary":true. Counting that exact
string across every session transcript file for this deployment
(grep -c '"isCompactSummary":true' <file>.jsonl, one file per CLI process lifetime):
| CLI version | sessions | span | isCompactSummary:true count |
|---|---|---|---|
| 2.1.92 | 2 sessions | 2026-06-16 → 2026-07-03 | 13 (11 in one long session, 2 in the next) |
| 2.1.199 | 14 sessions | 2026-07-03 → 2026-07-22 (present) | 0 |
The version boundary is clean: the last 2.1.92 session ends 2026-07-03T01:04 UTC, the
first 2.1.199 session starts 2026-07-03T03:56 UTC (a ~3h gap consistent with the
container rebuild itself, not a data gap), and every one of the 14 sessions since is
2.1.199 (confirmed per-line via the transcript's own "version" field, which every
row in every file carries) with zero compaction events, despite some of those sessions
running comparably long and comparably tool-call-heavy to the 2.1.92 sessions that did
compact.
Sample record (structure preserved, the actual compaction-summary text redacted per
this report's anonymization policy — the field itself is what matters, not its
contents):
{"parentUuid":"<redacted>","isSidechain":false,"promptId":"<redacted>","type":"user",
"message":{"role":"user","content":"<redacted — auto-generated compaction-continuation preamble>"},
"isVisibleInTranscriptOnly":true,"isCompactSummary":true,"uuid":"<redacted>",
"timestamp":"2026-06-16T22:34:36.228Z","userType":"external","entrypoint":"cli",
"cwd":"/workspace","sessionId":"<redacted>","version":"2.1.92",
"gitBranch":"main","slug":"<redacted>"}
Impact
Without compaction, a long-running session's context grows unbounded until it hits a
hard limit or degrades in quality; we've had to rely on our own scheduled
session-recycle (kill and restart the CLI process on an age/size threshold) as a
workaround/backstop since diagnosing this, which is a blunt instrument compared to the
CLI's own graceful compaction and loses in-context state the CLI's summarization would
otherwise have preserved.
Repro difficulty
Not independently reproducible on demand from our side — we observed it as a
before/after natural experiment across a version boundary in one continuously-running
deployment, not via a targeted test harness. We have not attempted to run other
intermediate 2.1.x versions to bisect further.
---
What we can provide on request
- The exact
grep/stringscommands used above, rerunnable against any 2.1.x
build (they target env var names and function shape, not line numbers, so should
survive minor version bumps).
- Full per-session transcript metadata (file name/sessionId, CLI version, first/last
timestamp, isCompactSummary count) for all 16 sessions referenced above — happy to
share as a table with the sessionId/file-name column removed if that's a concern,
or in full to a private channel upstream can verify us on.
- Redacted transcript excerpts around each of the 76 Bug 2 artifact occurrences
(assistant-turn text only, chat-platform message content stripped) — these would
need to be prepared per-occurrence rather than bulk-dumped, since removing all
message content requires per-file manual redaction; we have not pre-generated this
and would do so if upstream wants to inspect the exact artifact text/context rather
than just the shape quoted above.
- A copy of
scripts/reply-guard.js's detector logic in full (it's already
application code in this public-facing sense — no secrets in it) if a concrete
implementation of the workaround is useful context for triage.
- Sanitized row-structure excerpts around each of the three Bug 3 freeze boundaries
(the handful of transcript rows immediately before and after each gap, row
type/timestamps/field names preserved, all message content and channel
identifiers stripped) — these are already prepared in a shareable form, unlike Bug
2's 76 per-occurrence excerpts which need per-file manual redaction; the independent
watchdog log lines (probe/restart timestamps) corroborating each gap are equally
available.
What we cannot provide
- No root-cause visibility into Bug 3 beyond "the process is alive and idle-shaped
when checked" — we have not attached a debugger/profiler to a live-frozen process
(in practice, by the time a freeze is noticed the watchdog has typically already
restarted the container), so we cannot say what, specifically, the loop is blocked
on.
- We have not reproduced Bug 1 or Bug 2 on any version besides 2.1.92 and 2.1.199 (Bug
2 hasn't recurred at all on 2.1.217 yet — see Environment), so we cannot say whether
Bug 2 is already fixed upstream; Bug 1 remains unchanged on every version we've run
it on (2.1.199 and 2.1.217 both show zero compaction events).
- No tighter bisection range than "somewhere between 2.1.92 and 2.1.199" for Bug 1 —
no intermediate versions were installed/tested.
- No minimal/synthetic repro for Bug 2 or Bug 3 — only organic production occurrences
for both.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗