Over the past 24 hours, five issues filed against anthropics/claude-code share a structural pattern that is easy to miss when each one is read in isolation: each describes a contract that Anthropic itself documents — in product docs, in CHANGELOG entries, in CLAUDE.md examples, in the binary's own system prompt — and shows how the v2.1.142 or v2.1.143 binary silently does something else. None of these are crashes. None throw stack traces. Every one of them either exits zero or returns a confident-sounding response while the documented behavior is quietly absent.
This post walks through the five cases, shows the documented claim and the observed reality for each, and ends with three defenses that hook authors and end users can adopt without waiting for an Anthropic fix.
- #59643 —
PreToolUsehook deny reasons documented in the hooks reference do not reach the agent in v2.1.143. - #59586 — The agent-view docs say deleted sessions remain resumable; the v2.1.143 changelog says deletion now removes the transcript file. Both are still live.
- #59607 —
SKILL.local.mdandCLAUDE.local.mdoverrides described as "extends and overrides" fire after skill auto-triggers onclaude.ai/code, not before. - #59601 — A v2.1.142 session generates a committed research document with fabricated component counts (
111/42/47) rather than reading the actual source files; the real counts are258/106/76. - #59642 —
claude -pwithCLAUDE_CONFIG_DIRset to a non-default directory silently exits 0 with empty output on v2.1.123 → v2.1.143; v2.1.91 with the same setup works correctly.
Across all five, the failure is not the absence of behavior. It is the simultaneous presence of a confident success signal and a missing or substituted action behind it.
The hooks reference at code.claude.com/docs/en/hooks makes two specific promises about how a hook can communicate a deny decision back to the agent:
- A hook can exit 0 and emit JSON with
hookSpecificOutput.permissionDecisionReason— the docs state that this string is "surfaced to Claude". - A hook can exit 2 with a message on stderr — the docs state "stderr text is fed back to Claude as an error message."
The reporter set up both channels with an explicit identifying marker inside the reason text (MARKER-REASON-12345 for the JSON channel, MARKER-REASON-67890 for the stderr channel) and triggered the hook by asking the agent to run a command containing the substring echo-hookbug. Both reproduction steps were validated outside Claude Code: piping a synthetic tool_input JSON into the hook script via python3 /tmp/deny-hook.py < input.json confirmed the hook emits the expected JSON with exit 0 and writes the expected stderr text with exit 2.
What the agent receives in both cases is identical and reason-less:
Hook PreToolUse:Bash denied this tool
Neither marker appears anywhere in the tool_result that the agent sees.
This is not a malformed-hook issue. The two channels are exactly what the docs prescribe, and the hook works in isolation. The hooks reference documents X. The v2.1.143 binary performs Y. There is no error message, no warning, no fallback log entry telling the user that the reason channel is empty. Hooks that were written specifically to coach the agent ("Don't use git checkout; use git switch instead — the team standardized on switch in PR #4129") communicate nothing in v2.1.143. The agent just sees a generic denial and retries with a slightly different invocation that hits the same denial again.
Two documents shipped by Anthropic make incompatible statements about what Ctrl+X twice does to a background session:
The agent-view docs at code.claude.com/docs/en/agent-view, under "Monitor sessions with agent view," currently say:
Deleting removes the session from agent view and cleans up its worktree, including any uncommitted changes in it, so push or commit work you want to keep before deleting. The conversation transcript stays on disk and remains available through
claude --resume.
The same page's command reference describes claude rm <id> as a session-removal command that cleans up the worktree, with no mention of transcript handling.
The v2.1.143 changelog entry, however, reads:
Fixed deleting a session from agent view not removing its transcript file
In other words: the v2.1.143 binary now deletes the transcript on delete, and Anthropic considers that the correct behavior — strong enough to file under "Fixed." But the customer-facing documentation still promises the opposite. A user reads "the conversation transcript stays on disk and remains available through claude --resume," presses Ctrl+X twice to clean up the agent-view list, and then discovers in a later session that the transcripts they expected to keep are gone.
Both surfaces are live. Both are signed by Anthropic. They cannot both be right.
The Agentic OS project (github.com/simonc602/agentic-os) documents two override patterns that the local Claude Code CLI honors reliably:
CLAUDE.local.mdat the repo root, described in AGENTS.md and CLAUDE.md as user-owned rules that "extend and override" the upstreamCLAUDE.mdfor the session.SKILL.local.mdnext to anySKILL.md, described as local additions that "take precedence over the base."
On claude.ai/code (the web/mobile surface), the same files are silently ignored when the skill is configured to auto-trigger on natural-language phrases. The reporter's repro:
- Configure a skill whose
SKILL.mdincludes phrasing such as "Triggers AUTOMATICALLY when user says 'wrap up' / 'done' / 'all good'". - Add a
SKILL.local.mdnext to it with explicit override text: "DO NOT run the base workflow. Instead, do Y." - Or add a top-level
CLAUDE.local.mdwith the same intent. - In a
claude.ai/codesession, type the trigger phrase.
Observed: the base skill's workflow fires before either override is loaded. Tested twice in separate sessions — once with CLAUDE.local.md alone, once with both files — both ignored.
The override pattern is the only documented way to customize skill behavior without modifying upstream-owned files. If it doesn't work on mobile, users cannot tailor workflows for the cloud-sandbox context. The override semantics — "extends and overrides," "takes precedence over the base" — describe behavior that is real on the local CLI and absent on claude.ai/code, with no warning that the surfaces diverge.
A reporter working on a design-system monorepo (@parisgroup-ai/pageshell) asked the v2.1.142 agent to produce a research-sprint deliverable: a component inventory with counts, an anatomy document for an existing component, and a proposal for new documentation pages.
The agent produced all three without reading the codebase. The fabrications are quantified directly in the issue:
| Claim the agent made | Reality from source |
|---|---|
111 primitives, 42 composites, 47 layouts |
Navigation source: Primitives (258), Composites (106), Layouts (76) |
| "A 'página rica' (rich docs page) is a hypothesis to validate" | /demos/docs/[module]/[component] already exists with live preview, usage example, config editor, API reference, and related components |
| "⌘K is a pattern to propose implementing" | CommandDialog and related primitives already exist with a working demo at /demos/page-command |
| "The sidebar lacks real-time search" | filterDemoNavSections already implements real-time keyword filtering |
The fabricated numbers (111/42/47) were not flagged as estimates. They were committed to a research document and presented to the user as findings. The reporter caught the discrepancy only because they recognized a sidebar screenshot that showed 29+ components in a single sub-section — a single component count that was already inconsistent with the agent's monorepo-wide total.
The contract being violated here is implicit but well-documented: Claude Code's system prompt and the agent's documented operating model both describe tool use — Read, Glob, Grep — as the way to ground claims about the codebase. In this session, the model generated plausible-sounding specific numbers from training-data priors or prior-session summaries instead, and then used them as the foundation for downstream design decisions. The reporter notes this is not a one-time mistake: "várias e várias vezes" (many, many times).
The issue has been auto-flagged for closure as a duplicate of three earlier reports (#53988, #56906, #32294). That auto-classification does not change the structural observation: a v2.1.142 session committed false codebase facts to a research document while presenting them as verified findings, and the user discovered the gap only by visual inspection of an unrelated screenshot.
The reporter was using CLAUDE_CONFIG_DIR exactly as documented — to isolate a non-interactive claude -p ... worker from the user-scope ~/.claude/ (MCP servers, CLAUDE.md, skills) so the worker runs in a clean configuration scope. The setup ships credentials to the isolated config directory, sets the env variable, and runs claude -p "reply only OK" --model haiku --dangerously-skip-permissions.
On v2.1.91, this works:
OK
exit=0
On v2.1.123 through v2.1.143, the same setup silently exits 0 with empty stdout:
(empty stdout)
exit=0
The streaming result event reports total_cost_usd: 0 and all token counters at 0. With --debug, the CLI successfully reads .credentials.json, builds an Authorization: Bearer ... header, and issues a GET to /api/oauth/profile — and then the process exits without performing the chat completion. Nothing is printed to stdout or stderr.
Removing CLAUDE_CONFIG_DIR and using the default $HOME/.claude/ path with the same credentials works correctly. Pinning to @anthropic-ai/claude-code@2.1.91 works. Any newer version is silently dead.
The impact, in the reporter's words: a 24/7 worker that uses CLAUDE_CONFIG_DIR to isolate its Claude session — "a documented isolation strategy" — "is silently dead on upgrade. All chat / cron / extraction calls return empty. The lack of any error message makes the failure mode very hard to diagnose." Twenty versions later, the documented isolation strategy is broken and the binary does not say so.
Read in sequence, the five cases describe one structure: a contract written somewhere Anthropic owns — hooks reference, agent-view docs, override semantics, the agent's tool-use operating model, environment-variable docs — promises behavior X. The v2.1.142 or v2.1.143 binary performs Y. The transition from X to Y happens silently: no warning, no fallback log, no degraded-mode message, no startup banner that surfaces the divergence. In four out of five cases, the binary even emits an affirmative-looking signal (a confident response, an exit-0 termination, a Terminal notification sent string) while doing something different than what the contract specified.
The three-stage decomposition is the same in every case:
- User intent: write a deny reason, delete a transcript, override a skill, ground a claim in source files, isolate a config scope.
- Documented system commitment: the hooks reference, the agent-view page, the
.local.mdoverride semantics, the tool-use system prompt, theCLAUDE_CONFIG_DIRdocs. - Observed reality: a generic denial without reason, conflicting deletion semantics, ignored overrides, fabricated facts, silent empty output.
The pattern is not unique to these five issues. It is broad enough across the issue tracker that it is worth treating it as a class — "documented contract violations with affirmative-looking signals" — rather than as five unrelated bugs.
None of these are fixes for the underlying binary. They are practices that surface the divergence early enough to act.
Every channel where the binary tells you something happened — tool_result strings ("Bash completed", "Terminal notification sent", "Hook denied"), exit codes, debug-log entries, status indicators — is a claim, not evidence. The closer the claim is to the documented behavior, the more important it is to verify the claim against a second source.
Concrete: if you write a PreToolUse hook, log the JSON your hook emits to a file before exiting. After a deny, check whether the file shows your reason was written, and compare it with what your downstream session actually saw the agent receive. If the two don't match, the bug is upstream of your code.
Defense 2. Pin to the version that demonstrably works, even when the changelog implies the new behavior is "fixed."
The v2.1.143 changelog entry "Fixed deleting a session from agent view not removing its transcript file" sounds like progress. But it is incompatible with the live agent-view docs and may be a regression for users who relied on the documented "transcript stays on disk" semantics. Likewise, CLAUDE_CONFIG_DIR worked on v2.1.91 and has been silently broken across 20 patch versions. "Fixed" in a changelog and "still works for my use case" are two different claims.
When you upgrade, run the smallest version-specific regression check you can write for the documented behavior you actually rely on, and pin to the last version that passes.
For each documented contract you depend on — hook deny reason reaching the agent, override files being honored, isolation env variable producing isolation — write a single one-line check that proves the behavior is still real on the binary you are using. Run it after every Claude Code upgrade.
Examples:
- For #59643: a hook that emits a unique marker via both channels, and a Claude session that runs the triggering command and reports back whether the marker appeared in its tool_result.
- For #59642: a 5-line shell script that exports
CLAUDE_CONFIG_DIR=/tmp/test-isolation, copies credentials, runsclaude -p "say OK" --model haiku, and checksgrep -q '^OK$' output. - For #59586: a check that runs
Ctrl+Xtwice on a test agent-view session and confirms whether the transcript file at the expected path is gone.
These checks take minutes to write and seconds to run. They convert silent contract violations into visible test failures.
The pattern documented here — affirmative success signals over absent or substituted behavior — is the throughline that connects all five issues filed in the past 24 hours, and it is the structural observation behind a forensic case-study book on Claude Code I published a preview of last week. The preview lives as a public Gist with two free cases drawn from the book's 15 user-side reports — Claude says success, reality is silent (https://gist.github.com/yurukusa/6dd608049064ed66c54f1a545a7b47a8). The book itself is a 100-issue corpus of the same structure, organized into a three-stage decomposition and 14 defensive practices, shipping on 22 May.
If you have hit any of these five cases yourself, the issue numbers are anthropics/claude-code#59643, #59586, #59607, #59601, #59642. Each report contains the reporter's repro steps and is open at the time of writing. Adding your own observation to those threads — or filing a similar case you have observed — is the single highest-leverage action available to users right now, because the structural argument that this is a class of failures, not five unrelated bugs, gets stronger with every independent confirmation.