[BUG] Server-side experiment (`x-cc-atis`) silently removed Opus 4.8 thinking summaries, and the CLI silently self-updated even with `autoUpdates: false`. No notice, no opt-in, settings silently overridden.
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
UPDATE 2026-07-08:
The mitigation documented below (purge the cached assignment + DISABLE_GROWTHBOOK=1) stopped working within a day. The CLI's startup bootstrap fetch re-enrolls the install about one second after launch, ignoring that variable. The only working user-side opt-out is now purge + CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1, which also disables Projects sync, DesignSync, /feedback, live preview, and telemetry. Measurements in the correction comment below.
ORIGINAL POST:
I want a straight answer from someone at Anthropic about what shipped yesterday, because from where I sit you reached into my working setup mid-evening without authorization, turned off a documented feature on the one model I use most (Opus 4.8), told nobody, and left us to burn the rest of the night debugging to ultimately determine it was Anthropic's own stealthily installed experimental flag that caused the issues.
If that wasn't bad enough, while diagnosing the above, I discovered a second violation the same day: the CLI binary somehow updated itself while auto-updates are explicitly disabled (confirmed). Combined with the other recent issues and revelations, Anthropic seems to be engaging in some extremely sketchy practices.
Although lengthy, I urge other users to read through this issue report.
Background (issue #63358)
#63358 documents that Opus 4.8 returns thinking blocks with an empty thinking field, so the UI has nothing to render, continuing the Opus 4.7 regression family (#49268, #49555, #56356, #49322, #49902, #54348). The root cause established in that thread was client-side: the API defaults thinking.display to omitted on 4.7/4.8, showThinkingSummaries was honored only in interactive mode, and the VS Code extension (non-interactive) never mapped the setting onto the --thinking-display flag.
My comment in that thread
I root-caused the client-side gap, posted the analysis (https://github.com/anthropics/claude-code/issues/63358#issuecomment-4656278497), and built and shared a public workaround so other affected users could get summaries back: a launcher that injects --thinking-display summarized, the one lever that is not interactivity-gated, plus an extension patch and a proxy design:
https://github.com/phase3dev/claude-code-workarounds
What happened on 2026-07-07 (times HST, UTC-10): full timeline
- 6:35am: I manually updated the VS Code extension to
2.1.202(extension install directory mtime). That was my last version action until the evening. - 11:09am: The CLI native binary SILENTLY self-updated
2.1.202->2.1.203. I did NOT run an update. Auto-updates are disabled ("autoUpdates": falsein~/.claude.json) and were disabled at the time. The CLI's own updater recorded the event in~/.claude/.last-update-result.json; timestamp converted below to HST for readability:
``json``
{"timestamp_hst_start":"2026-07-07 11:09:12.832 HST","path":"native","outcome":"success","status":"success","version_from":"2.1.202","version_to":"2.1.203","error_code":null}
The 2.1.203 binary in ~/.local/share/claude/versions/ carries the same creation second (11:09:12.648 local). A Claude Code session was running at that minute. The background updater fired inside a live session, against the explicit setting. I have NEVER had the CLI binary update itself before, ever.
- Sessions all day continued to return populated summaries on Opus 4.8 (session transcripts: max
thinkinglength 5,347 chars at 7:50pm, 4,179 at 7:12pm). - At 7:12:18pm, Claude Code's built-in remote feature-flag client cached a new experiment assignment into
~/.claude.json(epoch1783487538925). - The next session (7:58pm) and every session after: EVERY Opus 4.8 thinking block came back with
thinkinglength 0 and a valid signature (21 consecutive signed-but-empty blocks in one session; signatures 660-7,436 bytes). - I noticed thinking summaries had stopped showing in VS Code upon opening a new chat. I checked versions: the VS Code extension was still
2.1.202, unchanged since my morning update, and no2.1.203extension version was ever installed on this machine. The CLI binary, however, had somehow become2.1.203. - ~7:56pm: I downgraded the CLI back to
2.1.202to test whether summaries would return (the re-downloaded2.1.202binary carries mtime7:56:55pm). They did not; consistent with the root cause being server-side, not the binary. - 10:01-10:02pm: to ensure everything matched, I manually updated both surfaces to the latest version: VS Code extension
2.1.204(10:01:22) and CLI2.1.204(10:02:13). - To be clear: the regression occurred before any manual update or downgrade. The break is at 7:12:18pm; my first manual version action of the evening was ~7:56pm.
- No client component changed across the break. Both sides of it: CLI
2.1.202, modelclaude-opus-4-8, entrypointclaude-vscode: byte-identical binary, opposite outcome. We wasted hours ruling out CLI 2.1.201/202/203/204 and extension versions. All red herrings for the blanking itself: the silent2.1.203self-update at 11:09am is NOT the cause of the blanking (summaries worked for hours after it, and the blanking reproduces on 2.1.201-2.1.204 alike once the assignment is cached). It is a second, independent problem from the same day, documented in its own section below.
The CLI updated itself while auto-updates were disabled
Auto-updates were and remain off in both the CLI and VS Code. My ~/.claude.json reads, verbatim:
{
"autoUpdates": false,
"autoUpdatesProtectedForNative": true,
"installMethod": "native"
}
Yet the updater ran through the native path starting at 11:09:12am HST on 2026-07-07, downloaded 2.1.203, switched the active binary, and recorded "outcome":"success" in its own log. This occurred with no notice, no prompt, and no user action. I have never experienced the CLI binary auto-updating before. This significantly reduces my trust in what this tool does on my machine. I want to know:
- How is this possible with
"autoUpdates": falseset? Is thenativeinstall path ignoring the setting, and what does the undocumentedautoUpdatesProtectedForNativekey actually mean? - What provision of the Terms of Service allows Anthropic to stealthily install software and experimental features, with no opt-in, while auto-updates are explicitly turned off?
The combination is what makes this serious: on the same day, the same install received an unrequested binary swap (client-side, against my setting) and an unannounced server-side experiment enrollment (against my documented showThinkingSummaries: true). Both were silent. Both were discovered only by forensics.
Layman summary
My install was silently enrolled in an A/B experiment. The enrollment token is cached in my local config and attached by the CLI to every API request as an x-cc-atis header. When Opus 4.8 (and only Opus 4.8) sees that token, the API strips the thinking text out of the response while keeping the signed thinking block. The reasoning still runs and still consumes tokens; only the human-readable text is withheld. Remove the token and the very same request returns summaries again. Nothing I did caused this, and nothing I was allowed to configure could stop it.
Technical details
- The assignment, cached client-side (
~/.claude.json,clientDataCacheSlots):
"experimentKey": "claude_code_attic_parcel_experiment",
"atis": "attic-parcel-meridian"
First cached at epoch 1783487538925 = 19:12:18 HST, the exact minute summaries died. A second slot refresh landed at 20:01:35 (1783490495244), the relaunch after the 2.1.204 update.
- The header attach, in the 2.1.204 native binary (shared fetch wrapper, first-party path, i.e. every surface: VS Code, headless
-p/SDK, TUI):
Yvi = "x-cc-atis"
function Xvi(){ let e = cL()?.atis; return typeof e === "string" && e.length > 0 ? e : void 0 }
// per-request:
if (o) { let m = Xvi(); if (m !== void 0) a.set(Yvi, m) }
// cL() reads clientDataCacheSlots / clientDataCache back out of ~/.claude.json
- The request is provably correct. Observed on the wire through a local forwarder (which logged only the body's
model/thinkingfields and response thinking lengths, never headers): the request body carries
"thinking": {"type": "adaptive", "display": "summarized"}
So this is not the #63358 client gap. The client asks for summaries exactly as documented, and the server withholds the text anyway.
- Single-variable proof, one binary (
2.1.204), one account, same prompt, minutes apart, request body identical in every run:
| Run | x-cc-atis on the wire | Opus 4.8 thinking length |
| --- | --- | --- |
| Direct (assignment cached) | yes | 0 (signature present; repeated runs: sig 512/540/564/836) |
| Identical request, header stripped in flight | no | 226 |
| Assignment purged from config + DISABLE_GROWTHBOOK=1 (direct request shape) | no (verified) | 65 |
| DISABLE_GROWTHBOOK=1 alone, cache not purged | yes | 0 |
- Model scope, same flags (
--thinking-display summarized --max-thinking-tokens 8000), same account, direct path:
| Model | thinking length |
| --- | --- |
| claude-opus-4-7 | 179 |
| claude-sonnet-5 | 253 |
| claude-haiku-4-5 | 528 |
| claude-fable-5 | 5,643 (VS Code path) |
| claude-opus-4-8 | 0 |
All four alias spellings (opus, claude-opus-4-8, opus[1m], claude-opus-4-8[1m]) resolve to claude-opus-4-8 and all return 0. Transport ruled out: HTTP/1.1 and HTTP/2 upstream legs, and accept-encoding forwarded verbatim, all behave identically once the header is controlled for.
- No user-accessible lever can prevent it. On first-party auth the CLI hard-forces
thinking.type: "adaptive"for 4.8 (capability-gated; the only override hook is a server-rejection retry), theANTHROPIC_*_SUPPORTED_CAPABILITIESenv overrides are disabled on first-party, andDISABLE_GROWTHBOOK=1alone still sends the already-cached token (measured, row 4 above). The only mitigation is manually editing the assignment out of~/.claude.jsonand settingDISABLE_GROWTHBOOK=1so the next feature refresh doesn't re-enroll; in other words, opting out of your entire client-side feature system to get back one documented feature.
Why this is unacceptable
- There is no documentation. Nothing anywhere documents that an
x-cc-atisassignment can disable thinking summaries per model. Users cannot diagnose what is not documented; it took binary disassembly and a wire proxy to find this. - It is not in the release notes. The CHANGELOG through 2.1.204 does not mention it, and no client release note could cover it, because the switch is server-evaluated. That is precisely why server-side behavior changes need their own announcement channel, and this one had none.
- Affected users were not notified. The flip happened mid-evening, between two sessions, on an unchanged binary. There is no message, no banner, no log line. The feature just dies and the user is left debugging their own setup.
- There is no opt-in. I never agreed to be an experiment subject for the removal of a feature I actively depend on and explicitly enabled.
- There is no documented opt-out.
showThinkingSummariesis your documented opt-in: the setting's own schema says: "Request API-side thinking summaries and show them in the conversation and in the transcript view (ctrl+o)." It is set totrue, the client honors it, and the server overrides it silently. Shipping a toggle that no longer toggles anything is worse than shipping no toggle. - The updater ignored my explicit setting.
"autoUpdates": falseis the documented control, it was set, and the native-path updater installed2.1.203anyway, silently and mid-session, recorded as"success"in its own log. If a documented off-switch does not switch anything off, every other setting in the product is now suspect too. - It broke my local workflow. I review this model's reasoning as part of how I work. That went dark mid-session, on the model I pay to use, while the thinking tokens kept being generated, signed, and counted.
- It broke my workaround. The launcher I built exists specifically to guarantee
display: "summarized"reaches the wire. It still does. The experiment ignores it. - It broke the workaround shared publicly with other users. phase3dev/claude-code-workarounds is linked in this thread and used by others who followed it out of the same regression. Every one of them who lands in this experiment bucket loses summaries again with no explanation, and this time no flag can bring them back.
- Users should not need to maintain public workaround repos for this in the first place. The community spent weeks reverse-engineering #63358's client gap. You fixed it in 2.1.202, which was good. Then a server-side experiment reintroduced the identical user-facing symptom within weeks, invalidating both your fix and ours.
- Targeting exactly Opus 4.8 is suspicious and concerning. Of five models tested, the only one that ignores an explicit
display: "summarized"is your flagship coding model, the one this thread is about, the one the workaround was built for, and the primary model in my workflow. Whether the blanked text is the experiment arm working as designed or a defect in an experimental serving path, I cannot tell from outside. Either way it is a decision someone configured and shipped against paying users, silently. - You are spending user trust you will not easily get back. Documentation, a release note, a notification, an opt-in, or a working opt-out, any single one of these would have prevented most of the damage. All of them are basic product hygiene. None of them happened. I am a loyal, paying user who has put real effort into this product's ecosystem, and this is the second time in short order I've had to treat my own tooling as the suspect for something Anthropic changed under my feet.
- This is a pattern, not a one-off. The AskUserQuestion 60-second-timeout behavior change (#73125) shipped the same way: silent, undocumented, discovered by users in the field. Silent behavior changes to a tool that people build workflows on is not a sustainable way to run a developer product.
What Should Happen?
claude-opus-4-8honors an explicitly requestedthinking.display: "summarized"exactly like Opus 4.7, Sonnet 5, Haiku 4.5, and Fable 5 do, regardless of experiment bucket. At minimum, users withshowThinkingSummaries: trueshould be excluded from any experiment that suppresses summaries.- Server-side experiments that change user-visible, documented behavior come with: an announcement, a changelog/release-notes entry, in-product notice to enrolled users, and a documented, supported opt-out. Ideally opt-in.
showThinkingSummarieseither works or is removed/deprecated with notice. A documented setting must not be silently overridden by an undisclosed experiment.- Answers, from someone in a position to know: Why does Opus 4.8 alone ignore the explicit display request when
x-cc-atis: attic-parcel-meridianis present? Is the blanked text the intended arm behavior ofclaude_code_attic_parcel_experiment, or a defect in that arm? Who decided to ship it with no notice? What is the supported path for users who need visible reasoning on Opus 4.8 today? - An explanation and a fix for the auto-update: why did the native updater install
2.1.203starting at 11:09:12am HST on 2026-07-07 with"autoUpdates": falseset, what doesautoUpdatesProtectedForNativemean, and what Terms of Service provision covers silently installing software and experiment enrollment on a machine whose owner has auto-updates turned off?autoUpdates: falsemust mean no self-updates, period.
Error Messages/Logs
There is no error. That is the problem. The response is a 200 with a signed, empty thinking block.
Transcript sweep across the break (per-session max thinking length, model claude-opus-4-8, entrypoint claude-vscode, CLI 2.1.202 on both sides):
17:50 HST -> 5347 chars OK
19:12 HST -> 4179 chars OK
19:12:18 HST x-cc-atis assignment cached (epoch 1783487538925)
19:58 HST -> 0 chars BROKEN
20:06 HST -> 0 chars BROKEN (21/21 thinking blocks empty, signatures 660-7436 bytes)
Cached assignment (from ~/.claude.json; identifiers redacted, experiment fields verbatim):
{ "data": { "experimentKey": "claude_code_attic_parcel_experiment", "atis": "attic-parcel-meridian" }, "at": 1783487538925 }
Silent self-update record (~/.claude/.last-update-result.json; timestamp converted from UTC to HST and treated as the start time, written with "autoUpdates": false in ~/.claude.json):
{"timestamp_hst_start":"2026-07-07 11:09:12.832 HST","path":"native","outcome":"success","status":"success","version_from":"2.1.202","version_to":"2.1.203","error_code":null}
Wire capture (local forwarder; request-body fields and response thinking stats only, headers never logged):
{"dir":"req","model":"claude-opus-4-8","thinking":{"type":"adaptive","display":"summarized"},"stream":true}
{"dir":"resp","status":200,"model":"claude-opus-4-8","stop_reason":"end_turn","thinking_blocks":[{"type":"thinking","len":0,"sig":504}]} // header present
{"dir":"resp","status":200,"model":"claude-opus-4-8","stop_reason":"end_turn","thinking_blocks":[{"type":"thinking","len":226,"sig":580,"preview":"This is a classic cognitive reflection t"}]} // same request, x-cc-atis stripped
Steps to Reproduce
Precondition: an install whose feature client has received the assignment. Check with:
jq '.clientDataCacheSlots // {} | to_entries | map(select(.value.data.atis != null) | .value.data | {experimentKey, atis})' ~/.claude.json
If enrolled (non-empty output):
- Run the matrix (any small reasoning prompt):
``sh``
for m in claude-opus-4-7 claude-sonnet-5 claude-haiku-4-5 claude-opus-4-8; do
claude -p 'A bat and ball cost $1.10; the bat costs $1.00 more than the ball. Reason step by step: what does the ball cost?' \
--model "$m" --output-format stream-json --verbose \
--max-thinking-tokens 8000 --thinking-display summarized --max-turns 1 > "out-$m.jsonl" 2>/dev/null
jq -r --arg m "$m" 'select(.type=="assistant")|(.message.content//[])[]|select(.type=="thinking")|"\($m) len=\(.thinking|length) sig=\((.signature//"")|length)"' "out-$m.jsonl"
done
Observed: claude-opus-4-7 len=179, claude-sonnet-5 len=253, claude-haiku-4-5 len=528, claude-opus-4-8 len=0 (sig 540).
- Prove the header is the trigger: route through a localhost forwarder (
ANTHROPIC_BASE_URL=http://127.0.0.1:<port>plus_CLAUDE_CODE_ASSUME_FIRST_PARTY_BASE_URL=1so the CLI still builds the true first-party request shape; without that env the CLI builds a different request and stops sending the header, which masks the bug) and drop only thex-cc-atisrequest header. Same run:len=226.
- Prove the mitigation: purge the
atisslots from~/.claude.json, launch withDISABLE_GROWTHBOOK=1, run direct:len=65, header absent. (Steps and caveats: https://github.com/phase3dev/claude-code-workarounds, 2026-07-07 update.)
For the silent self-update: check ~/.claude/.last-update-result.json against your own "autoUpdates" setting in ~/.claude.json and the version-binary creation times in ~/.local/share/claude/versions/.
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
Not applicable. This is not a client regression. CLI 2.1.202 both worked and failed on the same day, and 2.1.201-2.1.204 all behave identically once the assignment is cached.
Claude Code Version
2.1.204 (Claude Code)
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
VS Code integrated terminal
Additional Information
- Background issue: https://github.com/anthropics/claude-code/issues/63358
- Billing note: in every broken response the thinking block is generated and cryptographically signed. The reasoning tokens are produced and consumed; only the text is withheld from the person paying for them.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗