Claude Code v2.1.252: model-switch hooks and a symlink escape closed on file tools#

Part of the Claude Code Version Tracker series. | Official Env Vars | Official Changelog

Coming from v2.1.247, a v2.1.252 user gets two new hook events that fire around a model switch, PreModelSwitch and PostModelSwitch, and a batch of permission fixes that stop a symlink swapped after the check from redirecting a file operation outside the approved location.[1] The /cost view gains a per-session prompt-cache line, /usage gains a spend-limit bar, and a running background session can now be reattached with claude attach <id>. Most of this landed in v2.1.251; v2.1.252 itself is four fixes on top.

The bulk of this window is Anthropic tightening the gap between a permission decision and the operation it authorizes. Read, Write, and Edit followed a symlink swapped inside the working directory after the permission check passed, which could read or write outside the approved path; that is now resolved before the operation runs.[1] Grep and Glob now apply Read(...) deny rules to files reached through a symlinked search path, so a denied file cannot be read by pointing a search at a link to it. Plugin commands declared in a marketplace entry can no longer point outside the plugin directory, and the Workflow tool no longer reads (or quotes in errors) a scriptPath outside what the session may read before its permission check ran.

Two more close smaller holes. Project settings could enable detailed beta tracing or raw API body logging, and a lower-scope tracing endpoint could bypass an OTLP collector pinned by managed settings; both are blocked now.[1] And Bash permission checks that auto-approved a command assigning an arithmetic expression to a shell variable (OPTIND=1/0, RANDOM=2+2) now prompt instead.

Hooks around a model switch#

PreModelSwitch and PostModelSwitch join the registered hook events. A PreModelSwitch hook can block a switch, require confirmation before it proceeds, or annotate it; the confirmation copy warns that switching cached models means the full history gets re-read on the next message.[1] The same gate governs fast-mode promotion: when the model changes while the hooks run, the promotion is rejected as stale and the user is told to pick again. SessionStart resume hooks now also receive session staleness and the estimated re-cache cost, matching the cold-cache token accounting that surfaces in /cost.

New environment variables#

VariableWhat It Does
CLAUDE_CODE_MODEL_CATALOGForces the served model catalog off. The catalog (model families, context windows, aliases fetched for an account) is otherwise gated behind the allow_model_catalog policy, first-party claude.ai auth, and an organization; setting this variable short-circuits all of that and reports the catalog as env_off.
CLAUDE_CODE_REMOTE_TOOLS_POLICYSelects how tools are served to Remote Control clients. Unset or parity keeps the default; any other value switches to interim serving.
CLAUDE_CODE_DISABLE_MCP_TASK_BACKGROUNDTurns off automatic backgrounding of MCP tool tasks. It sits with the other MCP subagent knobs (CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS, CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION), so a long-running MCP call stays in the foreground when it is set.
CLAUDE_CODE_AUTO_BACKGROUND_WORKER_CHECKIN_SECONDSSets how often, in seconds, a dispatched background worker reports progress ("still running", last tool call, minutes elapsed). Applies when CLAUDE_AUTO_BACKGROUND_TASKS is on; the coordinator path uses its own CLAUDE_CODE_COORDINATOR_WORKER_CHECKIN_SECONDS.

Cost and usage get more visible#

/cost now carries a per-session prompt-cache line with hit ratio, misses, tokens re-cached, and whether the cache is warm or cold, and a matching prompt_cache object (fields like hit_ratio, misses, cache_write_tokens, miss_recache_tokens, expires_at, recache_tokens_if_cold) is available to status-line scripts.[1] /usage adds a spend-limit bar, and rate_limits.spend_limit joins the status-line fields alongside the five-hour, seven-day, and overage figures.

v2.1.252's own four fixes are narrower: Bash commands failing with "task output swap refused" on some Macs, "always allow" not saving in a project with no .claude/settings.local.json, Remote Control sessions hosted by Claude Desktop or VS Code stalling on a degraded claude.ai connection, and a background task notification with very large failure output pushing the request past the API size limit.[1]

What these tell us#

The through-line of this window is the boundary between a check and the action it guards. Symlinks resolved after approval, deny rules that stopped at a link, plugin and workflow paths that reached outside their scope, and arithmetic shell assignments that read as safe are all the same shape: a decision made against one target, an operation carried out against another. Closing them tightens the permission model without changing what a user is asked.

The new surface points at more moving parts running at once. Model-switch hooks give an organization a place to gate or annotate a switch, the background-worker check-in interval and the MCP backgrounding toggle expose the dispatch loop, and the prompt-cache accounting in /cost puts a number on what a switch or a long gap costs when the cache goes cold. The served model catalog and its kill switch continue the pattern of account-scoped configuration reaching the client through gated policy rather than static defaults.

This analysis is conducted for educational and research purposes under fair use principles. All trademarks and software referenced belong to their respective owners. This content is not intended to infringe on any intellectual property rights, circumvent any protections, or encourage unauthorized access to proprietary systems.

Sources#

  1. Claude Code Official Changelog, v2.1.251 and v2.1.252 release notes