[Re-raise] 5 stale-closed issues re-verified against the bundle: 2 provider-parity items hold (corrected), 2 token items withdrawn/reframed, + 4 new findings

Resolved 💬 2 comments Opened Jun 7, 2026 by Fearvox Closed Jul 13, 2026
Note on format. Claude Code's bug template asks for one bug per issue. This is deliberately a consolidated re-raise: five issues the stale-bot closed without triage, grouped so the cluster is reviewable together, plus the findings my pre-posting re-check surfaced. Every item below carries its own mechanism + repro, and I'm glad to split any one into its own templated [BUG]/[FEATURE] report — just say which. Filed free-form (rather than the single-report template) only because no single template fits a multi-issue re-raise; happy to reformat to your preference.

Summary

On 2026-04-10 I filed five detailed issues (#46416, #46420, #46421, #46422, #46423). All five were auto-closed as not planned by the stale bot — none received maintainer triage, and several had substantive community engagement (technical corroboration, workarounds, design proposals) after they were filed.

Before re-raising I re-verified each claim against the shipped bundle (@anthropic-ai/claude-code v2.1.112, cli.js) and cross-checked the public CHANGELOG through 2.1.167, so you don't have to re-investigate what's already shipped — and so I'm not re-raising anything that's already fixed. That re-check changed my conclusions on two of the five: I'm withdrawing one to a confirm-only question (B1) and reframing another as a by-design confirmation (B2), because the bundle shows the original mechanism doesn't hold. The remaining two provider-parity items hold up, with corrected mechanisms (my original root-cause guesses were wrong on the cause even where the symptom was right). I've also included a short set of new, separately-reproduced findings surfaced during the re-check.

I'm happy to split this back into separate issues if that's easier to route — consolidating only to make triage cheaper.

All function names below (e.g. ff, vJ, sI, Jn, keY) are minified identifiers from the v2.1.112 cli.js. They're included as grep anchors, not as source symbols.

---

Cluster A — third-party / custom-provider parity

A1 · Context-window detection caps third-party providers at 200K (was #46416) — still open

Corrected mechanism (my original root-cause was wrong on the cause). The context-window resolver (ff(q,K) in the bundle) does not gate on the base URL. It matches the model name against a hardcoded first-party allowlist and otherwise returns a hardcoded 200K default:

function ff(q,K){
  if(S6(process.env.DISABLE_COMPACT)&&process.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS){...return _}
  if(DP(q))return 1e6;                  // model name matches /\[1m\]/i
  if(K?.includes(Zo)&&vo(q))return 1e6; // vo(): claude-sonnet-4 / opus-4-6 / opus-4-7
  if(XV8(q))return 1e6;                 // coral_reef sonnet
  return DR1                            // DR1 = 200000
}

So a gateway/3P model (MiniMax, OpenRouter, vLLM/Ollama routers, Bedrock/Vertex aliases) whose id is not on that name allowlist and does not contain the [1m] marker always resolves to 200K, regardless of base URL or the gateway's real window. (My original issue blamed a base-URL eligibility gate / getContextWindowForModel; those symbols aren't in the shipped bundle. The symptom is the same, but the cause is a model-name allowlist — flagging so triage looks in the right place.)

Correction to my own "no override env var" claim: there are two partial overrides, both unsatisfying — so the honest ask is for a clean one, not "add any override":

  • CLAUDE_CODE_MAX_CONTEXT_TOKENS exists in ff() but only takes effect when DISABLE_COMPACT is also set (CHANGELOG 2.1.98 confirms this coupling is intentional). So you can raise the window only by turning autocompact off — see the new finding N1 below, where this becomes a hard either/or.
  • The [1m] model-name marker (a substring match anywhere in the id via /\[1m\]/i, not strictly a suffix) forces 1M, so a 3P user can sometimes piggyback by putting [1m] in the pinned model id — a hack, not a capability fix.

Current state — still open. CHANGELOG cross-check: the Opus-4.7 "200K → 1M" fix is first-party only (it extends the same name allowlist); the gateway /v1/models picker entries only affect the picker listing and output-token resolution, never ff()'s context window; ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU}_MODEL_SUPPORTS/_NAME/_DESCRIPTION override effort/labels but explicitly not the context window. No entry resolves 3P context-window resolution.

Why it matters: the context bar, AutoCompact, and "Prompt is too long" guards all read this number, so 3P/local-router users get early/false compaction and a misleading bar. The practical workaround today is "ignore the bar and /compact by feel."

Ask: either consult the gateway's reported capabilities, or add a standalone per-model context-window override that does not require DISABLE_COMPACT (e.g. ANTHROPIC_CONTEXT_WINDOW_TOKENS).

Repro: configure a custom ANTHROPIC_BASE_URL gateway serving a real >200K model whose id isn't opus-4-7/4-6/sonnet-4 and doesn't contain [1m]; /context and AutoCompact behave as if max = 200K. Only escapes: DISABLE_COMPACT=1 + CLAUDE_CODE_MAX_CONTEXT_TOKENS=<real>, or put the [1m] marker in the model id.

A2 · No portable session-state continuity across a provider/model switch (was #46423 feature; #46420 bug reframed) — feature still open

Reframe (correcting the original bug claim). Re-checking the bundle, I need to retract the strong form of #46420 — "switching between two configured models drops conversation context". It doesn't:

  • An in-session /model switch preserves the conversation. The handler spreads the entire existing state object and changes only the model fields: T6(($w)=>({...$w, mainLoopModel:T8, mainLoopModelForSession:null, ...})). It does not touch the separate conversation-message state — the only history-wipe path is the distinct /clear command. CHANGELOG corroborates ("Resumed sessions now keep the model they were using…").
  • The real discontinuity is env-var driven and restart-only: ANTHROPIC_MODEL / ANTHROPIC_BASE_URL are read once at startup and are in the restart-required set, so changing provider means relaunching claude — a new process, a new session, no carry-over. That's startup-config semantics, not a runtime reset.

What remains genuinely open (the #46423 feature): there is no portable cross-switch continuity mechanism. Full-CHANGELOG scan for continuity/rehydrate/handoff/portable-state across a model or provider switch returns zero matches. (The adjacent "/model in one session silently changing the autocompact threshold in other concurrent sessions" fix is cross-session threshold leakage — not this.)

Why it matters: multi-provider / cost-routing workflows are increasingly common; switching provider today means relaunch + hand-writing critical state into memory files (fragile). A community commenter (wazionapps) proposed a clean boundary worth considering: on switch, rehydrate a portable operational-state snapshot (current goal, next action, hot files, pending approvals, loaded memory refs) — distinct from the raw transcript.

Ask: an opt-in portable operational-state packet that survives a provider relaunch / handoff.

---

Cluster B — token accounting (both down-graded after re-check; please confirm rather than treat as open bugs)

B1 · Local token estimation vs cached context (was #46422) — withdrawing the bug claim; confirm-only

My original claim was that the local estimator ignores cache_read_input_tokens / cache_creation_input_tokens versus the API usage. Against v2.1.112 this is not the case — every context-usage path already sums the cache fields and anchors on real API usage:

function ey6(q){return q.input_tokens+(q.cache_creation_input_tokens??0)+(q.cache_read_input_tokens??0)+q.output_tokens}
function vJ(q){ ... return ey6(z)+qT(q.slice(K+1)) } // last real API usage + char/4 estimate of only the un-acked tail

The same cache-inclusive sum drives the bar percentage and latestInputTokens, and it's present identically in the v2.1.89 baseline — so the "cache ignored" mechanism wasn't there even at filing time. I'm withdrawing B1 as a bug.

Two narrow things I'd only ask you to confirm, not treat as open:

  1. The statusline cumulative-vs-current fix ("Fixed statusline context_window token counts reflecting cumulative session totals instead of current context usage") is, I believe, the actual fix for the symptom I most likely saw (bar over-reading on a long cached session). Confirming that closes the user-visible side.
  2. The only residual divergence I can find is the char/4 estimate of the current un-acked trailing turn (qT(q.slice(K+1))) — inherently approximate, bounded to one turn, self-correcting on the next API response. Not cache-specific. (This connects to new finding N2 below.)

B2 · Parallel subagent cache_read accumulation (was #46421) — by design; confirmation + cost-doc note would close it

Re-checking: each subagent is an independent context window ("Implicit fork — inherits full conversation context", model:"inherit"), so it re-sends the parent prefix; with caching warm that returns as cache_read_input_tokens, aggregated with += (no cap, no cross-agent dedup). So parent-visible cache_read does scale with fan-out — but cache_read is the cheap, desired path, not a leak: the bundle's price table shows promptCacheReadTokens:0.3 vs inputTokens:3 (Sonnet) — ~10× cheaper than fresh input and ~12.5× cheaper than cache_creation. My original "no user-visible benefit" framing had it backwards.

The genuinely expensive adjacent path — cache_creation on sub-agent progress summaries — was already addressed: 2.1.128 "Fixed sub-agent progress summaries missing the prompt cache (~3× cache_creation reduction)" and "Fixed sub-agent summaries firing repeatedly while a sub-agent's transcript is static, capping worst-case token cost on idle sub-agents."

So B2 is effectively by design with the costly path already mitigated. The only open question: is a shared-prefix dedup or a per-dispatch cache_read cap on the roadmap, or should this be documented as expected fan-out cost? A one-line confirmation + a cost-docs note closes it.

---

Additional reproducible findings (surfaced during the re-check, separately reproduced)

These are new (not in the original five), each verified against v2.1.112 and confirmed un-fixed through CHANGELOG 2.1.167.

N1 · CLAUDE_CODE_AUTO_COMPACT_WINDOW is capped to the mis-detected 200K window for 3P models (P2)

Mechanism. The autocompact-window resolver returns window: Math.min(ff(model), override):

function Jn(q,K){let _=ff(q,eM());
  if(process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW){...return{window:Math.min(_,A),configured:A,source:"env"}}
  ... }

For a 3P model ff() returns DR1=200000, so min(200000, 1000000)=200000. The override is parsed, clamped to [100K,1M], and echoed back as configured:1000000/source:"env" — yet the effective window stays 200K. (/autocompact does disclose the result as "capped by model", so it isn't entirely silent — but the override is accepted and surfaced as configured:1000000 before being clamped away, which reads as "it took my setting." Separately, for experiment-sourced compaction the UI prints a hint to override with CLAUDE_CODE_AUTO_COMPACT_WINDOW=1000000 that is itself then clamped — misleading for the narrow set of users who see it.) The autoCompactThreshold setting and CLAUDE_AUTOCOMPACT_PCT_OVERRIDE are capped the same way.

This is worse than A1's "no override": the documented escape hatch fails closed precisely for the 3P users who need it (accepted, echoed as configured, then clamped). Combined with A1's CLAUDE_CODE_MAX_CONTEXT_TOKENS requiring DISABLE_COMPACT, there is no supported configuration that gives a 3P large-context model both a correct window and autocompact — the two override paths are mutually exclusive.

CHANGELOG: not fixed. (The 1M-context-model false-block fix only applies to models already detected as 1M; AUTO_COMPACT_WINDOW/AUTOCOMPACT_PCT_OVERRIDE never appear as a 3P remedy.)

Repro: ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic, ANTHROPIC_MODEL=MiniMax-M2.7 (real 1M window, no [1m] in id), CLAUDE_CODE_AUTO_COMPACT_WINDOW=1000000, autocompact on → /context still scales to 200K and autocompact fires near ~187K; the debug autocompact log shows effectiveWindow=200000.

N2 · Context bar and the AutoCompact gate count different totals — bar shows headroom while AutoCompact fires (P2)

Mechanism. The footer token-warning and several token surfaces read the last assistant turn's API usage (the sI-family; /context has its own related ce6/_l8 path, not identical to sI, but likewise anchored on last-response usage). The AutoCompact gate instead uses vJ(q) = that last-response usage plus a local char/4 estimate of every message after the last assistant turn:

function sI(q){ ... if(z)return ey6(z); ... return 0 }              // bar
function vJ(q){ ... return ey6(z)+qT(q.slice(K+1)) }               // autocompact gate

Same denominator, different numerator. After the last reply, a large paste or a big tool_result sits in q.slice(K+1) until the next assistant turn lands fresh usage. During that window the bar can read "32% until auto-compact" while AutoCompact, counting the just-injected large tool result, fires immediately — compaction "out of nowhere." It self-heals on the next assistant turn (both reconverge), which is why it reads as intermittent.

CHANGELOG: not fixed. Prior reconciliations (/context ↔ statusline header, cumulative→current) align the display surfaces with each other — all sI-family. None aligns the display with the AutoCompact decision input vJ.

Repro: in a session with an existing last-assistant usage, note the "% until auto-compact" bar, then paste a very large block / trigger a tool returning a large result → AutoCompact fires while the bar had just shown comfortable headroom. Verbose shows autocompact: tokens=… carrying a higher number than the bar implies.

N3 · Local estimator counts every image and every document/PDF block as a flat 2000 tokens (P3)

Mechanism. In the local estimator, if(q.type==="image"||q.type==="document")return 2000. Claude Code sends PDFs as {type:"document",source:{type:"base64",media_type:"application/pdf"}}; a real multi-page PDF is tens of thousands of tokens but is pinned at 2000. Since the AutoCompact pre-check (vJ) uses this estimate for the un-acked trailing slice, a freshly-attached large PDF under-accounts by tens of thousands of tokens until the next API turn returns true usage. (The bundle's own image config — pxPerToken:28, maxTargetTokens:1568, and a note about 2576px on newer models — shows the flat 2000 also mis-estimates high-res images.)

CHANGELOG: not fixed. Image-pipeline entries are about actual API cost / compression, not the local estimator constant; no entry touches document/PDF token estimation at all.

Repro: attach a large multi-page PDF mid-session and watch /context / AutoCompact — most visible as AutoCompact not firing when a huge PDF should have pushed it over. Estimate-only; corrects on the next API response and is bounded by the "Prompt is too long" guard.

N4 · Stop/SubagentStop prompt-hook transcript truncation uses [1m]-only 1M detection (P3)

Mechanism. Prompt-based Stop/SubagentStop hooks re-query the model with the session transcript, first truncated by keY(transcript, model) with budget _=DP(model)?1e6:DR1 then floor(_*0.7). DP matches the literal [1m] suffix only, while the real context-window resolver ff grants native 1M to additional models (opus-4-7, opus-4-6, sonnet-4, coral-reef) without the suffix. So a user on plain opus-4-7 — 1M everywhere else (picker, bar, autocompact) — has the Stop-hook transcript truncated to 140,000 tokens, dropping older evidence the stopping condition may depend on.

CHANGELOG: not fixed. Stop-hook entries change other fields; the Opus-4.7 200K→1M fix targets /context + autocompact, not the hook truncation budget in keY.

Repro: on opus-4-7 (no [1m]), build a >~140K-token session, then trigger a prompt Stop hook whose stopping condition depends on something early in the session → the hook evaluates against a transcript truncated to the most recent ~140K and can answer wrong. Debug shows Hooks: truncated Stop transcript … (budget 140000, model …opus-4-7).

---

Why re-raise

None of these were closed by a maintainer decision — they were closed by inactivity (stale). I've done the homework this time: re-verified each against the shipped bundle, withdrew B1 and reframed B2 where the code showed my original mechanism was wrong, corrected A1/A2's mechanisms so triage looks in the right place, and added four separately-reproduced findings. The surviving provider-parity items (A1, A2-feature, N1) appear genuinely unfixed and have a clear, small fix surface. Even a triage label or a "won't-fix, here's why" would be more useful than another stale timeout.

Original issues: #46416 · #46420 · #46421 · #46422 · #46423

Version note. Re-verified against the locally-installed @anthropic-ai/claude-code v2.1.112 bundle; CHANGELOG cross-checked through 2.1.167. The version I'm running is 2.1.165 — the CHANGELOG shows no explicit fix for any mechanism above, but N2/N3/N4 are the most exposed to silent implementation drift in the 2.1.112 → 2.1.165 gap, so a quick spot-check against the current build is worth it before treating those three as prod-current. A1/A2/N1 are structural and long-standing (present identically in the 2.1.89 baseline).

View original on GitHub ↗

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