<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rulestack</title>
    <description>The latest articles on DEV Community by Rulestack (@rulestack).</description>
    <link>https://dev.to/rulestack</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4025074%2F8c45f5e9-1af0-48b9-9e5d-8078f7eb4043.png</url>
      <title>DEV Community: Rulestack</title>
      <link>https://dev.to/rulestack</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rulestack"/>
    <language>en</language>
    <item>
      <title>Your GitHub Actions cron fires less often than you declared: what we measured and how to design for it</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Fri, 21 Aug 2026 03:19:33 +0000</pubDate>
      <link>https://dev.to/rulestack/your-github-actions-cron-fires-less-often-than-you-declared-what-we-measured-and-how-to-design-for-80f</link>
      <guid>https://dev.to/rulestack/your-github-actions-cron-fires-less-often-than-you-declared-what-we-measured-and-how-to-design-for-80f</guid>
      <description>&lt;p&gt;We run an automated publishing pipeline entirely on GitHub Actions cron schedules — no server, no queue, just workflows that wake up, do one thing, and commit the result. It mostly works. But there is one behaviour of scheduled workflows that the docs mention in a single quiet sentence and that will silently halve your job frequency if you design around the cron expression instead of around reality:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scheduled workflows do not fire as often as you declare.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What we measured
&lt;/h2&gt;

&lt;p&gt;We had a feedback-watcher workflow declared at four runs per hour:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;7,22,37,52&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Measured over days, it actually fired &lt;strong&gt;one to two times per hour&lt;/strong&gt; — not four, and not at the declared minutes. Roughly hourly on most days, at inconsistent offsets from the declared slots.&lt;/p&gt;

&lt;p&gt;We later redeclared it at two runs per hour (&lt;code&gt;7,37 * * * *&lt;/code&gt;) — measured result: still one to two runs per hour. The declared frequency changed by 2x; the delivered frequency barely moved.&lt;/p&gt;

&lt;p&gt;This is not an outage and not a misconfiguration. GitHub's own documentation says the &lt;code&gt;schedule&lt;/code&gt; event &lt;strong&gt;can be delayed during periods of high load&lt;/strong&gt;, and that high load times include the start of every hour — which is precisely where naive cron expressions cluster — and adds: "If the load is sufficiently high enough, some queued jobs may be dropped." What the docs understate is the magnitude: in our observation, on a private repo, "delayed" in practice meant "throttled to a fraction of the declared rate, indefinitely."&lt;/p&gt;

&lt;h2&gt;
  
  
  What this breaks
&lt;/h2&gt;

&lt;p&gt;The failure mode is subtle because nothing goes red. Every run that happens succeeds. The runs that don't happen leave no trace — no log, no failure email, nothing. You only notice if something downstream depends on the frequency:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We had promised a "reply within 15 minutes" SLA on incoming feedback, initially backed by the 4x/hour schedule. The schedule couldn't hold it, so for a while we ran a local 15-minute scheduler as the primary path and kept the workflow as fallback. When we later relaxed the promise to an hour, the local job stopped earning its keep and the workflow became the only path again.&lt;/li&gt;
&lt;li&gt;A pacing job "runs daily at 08:30" — except on days when it fires at 09:10, and any consumer that reads its output at 09:00 sees stale state and makes decisions on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Design rules that survive the throttling
&lt;/h2&gt;

&lt;p&gt;After getting burned, we rebuilt around four rules:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Declare intent, but design for the floor.&lt;/strong&gt; Treat the cron expression as an upper bound and ask: does the system still behave correctly if this fires once per hour? Once per day, if it fires two hours late? If the answer is no, the design is wrong, not the schedule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Make every run idempotent and self-contained.&lt;/strong&gt; Each of our jobs re-derives "what needs doing" from committed state (JSONL ledgers in the repo), does at most one unit of work, and commits the updated ledger. A missed run costs latency, never correctness. A doubled run (it happens) costs nothing, because the ledger check makes the second run a no-op.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Don't encode business timing in cron minutes.&lt;/strong&gt; Our daily article publisher doesn't publish "at 13:00 UTC" — it publishes "the oldest unpublished item, if none was published this UTC day." The schedule is just a heartbeat that triggers the check. If GitHub fires it at 13:40, the behaviour is identical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Move real deadlines out of scheduled workflows.&lt;/strong&gt; Anything with an actual latency promise now sits behind a stricter trigger, or the promise itself gets renegotiated to something the delivered rate can honor. Ours became one hour — a product decision, not a capitulation — and the cron stayed declared at 2x/hour: headroom above the promise, so the throttled rate still meets it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The check worth running on your own repo
&lt;/h2&gt;

&lt;p&gt;Pull the actual run timestamps and compare to your declaration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh run list &lt;span class="nt"&gt;--workflow&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yourjob.yml &lt;span class="nt"&gt;--limit&lt;/span&gt; 50 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--json&lt;/span&gt; createdAt &lt;span class="nt"&gt;--jq&lt;/span&gt; &lt;span class="s1"&gt;'.[].createdAt'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Count runs per hour over a day. If you declared four and got one, nothing is broken — you have simply been reading the cron expression as a contract when it was always a request.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written while building &lt;a href="https://rulestack.gumroad.com?ref=devto" rel="noopener noreferrer"&gt;Rulestack&lt;/a&gt; — an automated content pipeline that runs on nothing but GitHub Actions and committed JSONL state.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Field notes from running it go up daily at &lt;a href="https://bsky.app/profile/ai-shop.bsky.social" rel="noopener noreferrer"&gt;@ai-shop.bsky.social&lt;/a&gt; on Bluesky.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>actions</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>A Claude Code subagent costs ~436k tokens before it reads a single file: measured, with the break-even math</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Thu, 20 Aug 2026 12:04:07 +0000</pubDate>
      <link>https://dev.to/rulestack/a-claude-code-subagent-costs-436k-tokens-before-it-reads-a-single-file-measured-with-the-1ja9</link>
      <guid>https://dev.to/rulestack/a-claude-code-subagent-costs-436k-tokens-before-it-reads-a-single-file-measured-with-the-1ja9</guid>
      <description>&lt;p&gt;Every guide to Claude Code subagents tells you the same two things: they isolate context, and they parallelize work. Both true. What none of them told us was the number that actually decides whether a subagent is worth spawning. We &lt;a href="https://dev.to/rulestack/what-a-claude-code-subagent-actually-costs-measuring-the-436k-token-fixed-overhead-46g6"&gt;measured that number on our own project&lt;/a&gt; earlier this month; this follow-up is about what we do with it — the break-even math and the routing rules that now gate every spawn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In our setup, a single subagent costs roughly 436,000 tokens before it reads one line of the file you sent it to read.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That number is not universal — it is the point. Yours will be different, and until you know yours, every delegation decision you make is a guess. Here is where the cost comes from, how to measure it on your own repo in five minutes, and the break-even math we now use before spawning anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where 436k tokens go
&lt;/h2&gt;

&lt;p&gt;A subagent is not a cheap thread. It is a full agent loop with its own context window, and that window gets furnished from scratch on spawn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The system prompt and tool schemas.&lt;/strong&gt; Every tool definition the subagent might use is serialized into its context. With MCP servers attached, this alone can be six figures of tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your CLAUDE.md — all of it.&lt;/strong&gt; Project instructions load into the subagent the same way they load into the parent. If your CLAUDE.md pulls in other files, those come too. We wrote earlier about cutting ours from 548KB to 34KB; before that cut, spawning a subagent was catastrophically expensive and we didn't know it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills and agent definitions.&lt;/strong&gt; Anything that auto-loads for the parent generally auto-loads for the child doing the same kind of work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The task prompt you wrote&lt;/strong&gt;, which is usually the only part people think about, and reliably the smallest.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We got the 436k figure by running the same review task two ways — the same product text sent to three agents (2,150,310 tokens total) and to one agent (809,070) — and attributing the per-turn difference to spawn-time context and the final cache write. Crude, reproducible, good enough for budgeting; the full method is in the earlier post linked above.&lt;/p&gt;

&lt;h2&gt;
  
  
  The break-even question nobody asks
&lt;/h2&gt;

&lt;p&gt;The question is not "is 436k a lot?" It is: &lt;strong&gt;what does the alternative cost?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the parent reads 200k tokens of logs itself, those 200k tokens don't get paid once. They sit in the conversation and get re-sent with every subsequent request in the session. With prompt caching, re-sent input is billed at a heavy discount — but it is not free, and a long session can easily make thirty more requests after the read.&lt;/p&gt;

&lt;p&gt;So the comparison is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delegate:      436k (once, in the subagent's window)
read inline:   200k × (discounted re-send rate) × (remaining requests in session)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a cache-read discount around an order of magnitude and ~30 remaining requests, inline reading of N tokens costs on the order of &lt;code&gt;N × 3&lt;/code&gt; in effective re-sent volume. The arithmetic puts our crossover near &lt;strong&gt;N ≈ 145k tokens&lt;/strong&gt; (436k ÷ 3); in day-to-day budgeting we round that up to &lt;strong&gt;200k&lt;/strong&gt; to bias against casual spawns. Below the line, just read the thing in the main loop; above it, delegation wins even at 436k fixed cost.&lt;/p&gt;

&lt;p&gt;Two things move that threshold dramatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Session length.&lt;/strong&gt; Early in a long session, delegation pays sooner (more future requests will re-send whatever you read inline). In the last few turns before you're done, almost nothing justifies a spawn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model routing.&lt;/strong&gt; If your harness lets an agent definition pin a smaller model, the fixed cost gets cheaper in dollars even when it's similar in tokens. We route bulk reading and collection to a mid-tier model and keep judgment tasks on the large one; that alone moved our practical break-even from "hundreds of thousands of tokens" down to "tens of thousands" for read-heavy work.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The three-line audit for your own repo
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Run a trivial task with no subagent and note the session token delta.&lt;/li&gt;
&lt;li&gt;Run the same task but force it through one subagent (&lt;code&gt;"use a subagent to do X"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Subtract. That difference is your spawn tax. Do it three times and take the middle value.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the number surprises you, the first place to look is whatever auto-loads into every agent: project instructions, imported files, MCP tool schemas. Cutting those pays twice — once in the parent, once in every child.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules of thumb we actually follow now
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Never spawn for a single-file fact.&lt;/strong&gt; Reading one file inline is always cheaper than 436k.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bundle overlapping perspectives.&lt;/strong&gt; Two reviewers with 80% overlapping concerns are one reviewer. The second spawn buys you a second fixed cost, not a second brain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spawn early or not at all.&lt;/strong&gt; The value of keeping the main context clean compounds over the remaining session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route by task shape.&lt;/strong&gt; Mechanical reading → small model, adversarial judgment → big model, orchestration → whatever the session runs on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disclose the spend.&lt;/strong&gt; Our agent reports every spawn with a cost estimate. When the number is visible, the habit self-corrects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The subagent feature is genuinely good. It is also the single easiest place in an agentic setup to burn a million tokens without noticing, because the cost is invisible unless you go measure it. Measure it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written while building &lt;a href="https://rulestack.gumroad.com?ref=devto" rel="noopener noreferrer"&gt;Rulestack&lt;/a&gt; — configuration packs for Cursor, Claude Code, and Codex, including the agent-definition patterns described above.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Shorter daily notes on agent economics: &lt;a href="https://bsky.app/profile/ai-shop.bsky.social" rel="noopener noreferrer"&gt;@ai-shop.bsky.social&lt;/a&gt; on Bluesky.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>devtools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>When your MCP tool fails: errors, hangs, and empty results — what Claude Code actually does with each</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Wed, 19 Aug 2026 12:57:43 +0000</pubDate>
      <link>https://dev.to/rulestack/when-your-mcp-tool-fails-errors-hangs-and-empty-results-what-claude-code-actually-does-with-2a50</link>
      <guid>https://dev.to/rulestack/when-your-mcp-tool-fails-errors-hangs-and-empty-results-what-claude-code-actually-does-with-2a50</guid>
      <description>&lt;p&gt;Yesterday I posted a one-liner on Bluesky: an MCP tool that errors teaches the model something, one that hangs teaches it nothing. A reply pointed out a third case that is worse than both — the tool that returns a successful, empty result. It doesn't burn the turn. It burns the next six, because the model keeps reasoning on top of an answer that never existed.&lt;/p&gt;

&lt;p&gt;That reply sent me back to the spec and the Claude Code docs to map out what actually happens in each of the three failure modes. Here's what I verified, with sources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure mode 1: the loud error (the good one)
&lt;/h2&gt;

&lt;p&gt;The MCP spec defines &lt;strong&gt;two separate error channels&lt;/strong&gt;, and mixing them up is the first thing that goes wrong in homegrown servers.&lt;/p&gt;

&lt;p&gt;From the &lt;a href="https://modelcontextprotocol.io/specification/2025-06-18/server/tools#error-handling" rel="noopener noreferrer"&gt;spec's tool page&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Protocol errors&lt;/strong&gt; — standard JSON-RPC errors, for things like unknown tools or invalid arguments. These are plumbing failures: the call itself was malformed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool execution errors&lt;/strong&gt; — reported &lt;em&gt;inside&lt;/em&gt; the tool result with &lt;code&gt;isError: true&lt;/code&gt;, for API failures, invalid input data, business logic errors. The call worked; the work failed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The distinction matters because of who reads each channel. A protocol error is handled by the client machinery. A tool execution error is placed into the conversation — &lt;strong&gt;the model reads it and can react to it&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Failed to fetch weather data: API rate limit exceeded. Retry after 60s."&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isError"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That text string is the entire feedback channel back to the model. So write it for the model, not for a human log reader: say &lt;strong&gt;what failed, why, and what a valid retry looks like&lt;/strong&gt;. &lt;code&gt;"Error 500"&lt;/code&gt; teaches nothing. &lt;code&gt;"Date must be YYYY-MM-DD, got '19/08/2026'"&lt;/code&gt; fixes the next call.&lt;/p&gt;

&lt;p&gt;This is the &lt;em&gt;good&lt;/em&gt; failure mode. Everything below is about what happens when your server doesn't fail this honestly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure mode 2: the hang (bounded by more timers than you think)
&lt;/h2&gt;

&lt;p&gt;If your tool just... doesn't answer, what saves the session? In Claude Code there is a whole stack of timers, and the defaults are worth knowing precisely, because one of them is almost certainly not what you'd guess (&lt;a href="https://docs.claude.com/en/docs/claude-code/mcp" rel="noopener noreferrer"&gt;docs&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server startup&lt;/strong&gt; is bounded by the &lt;code&gt;MCP_TIMEOUT&lt;/code&gt; environment variable — e.g. &lt;code&gt;MCP_TIMEOUT=10000 claude&lt;/code&gt; gives servers 10 seconds to come up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each tool call&lt;/strong&gt; is bounded by a per-server &lt;code&gt;timeout&lt;/code&gt; field (milliseconds) in that server's &lt;code&gt;.mcp.json&lt;/code&gt; entry — &lt;code&gt;"timeout": 600000&lt;/code&gt; for ten minutes. It overrides the &lt;code&gt;MCP_TOOL_TIMEOUT&lt;/code&gt; environment variable for that server only, and values below 1000 are ignored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you set neither&lt;/strong&gt;, &lt;code&gt;MCP_TOOL_TIMEOUT&lt;/code&gt;'s default is &lt;strong&gt;about 28 hours&lt;/strong&gt;. Not 28 seconds. A tool call with no other guardrails can legally run for more than a day.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The thing that actually rescues most hangs is newer and less known: the &lt;strong&gt;idle timeout&lt;/strong&gt;. A tool call that sends no response &lt;em&gt;and no progress notification&lt;/em&gt; for the idle window gets aborted instead of waiting out the wall-clock limit. The window defaults to &lt;strong&gt;five minutes&lt;/strong&gt; for HTTP, SSE, and WebSocket servers, and &lt;strong&gt;30 minutes&lt;/strong&gt; for stdio servers (Claude Code v2.1.187+; stdio included from v2.1.203). You can tune it with &lt;code&gt;CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT&lt;/code&gt; in milliseconds, or disable it with &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two nuances that bit me while mapping this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Progress notifications keep the idle timer alive, but do not extend the wall-clock limit.&lt;/strong&gt; The per-server &lt;code&gt;timeout&lt;/code&gt; is a hard ceiling; a chatty server still dies at it.&lt;/li&gt;
&lt;li&gt;A main-conversation call that runs past &lt;strong&gt;two minutes&lt;/strong&gt; moves to a background task first — so "it's been hanging for 10 minutes" and "the session is blocked for 10 minutes" are different claims.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For server authors the takeaway is simple: if your work is legitimately slow, &lt;strong&gt;send progress notifications&lt;/strong&gt;; if it can hang, it will eventually eat someone's five-minute idle window per call, silently. Fail fast instead — mode 1 is cheaper than mode 2 in every currency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure mode 3: the empty success (the one that poisons the run)
&lt;/h2&gt;

&lt;p&gt;The reply that started this article described it exactly: the tool returns a well-formed, successful result that contains nothing. &lt;code&gt;[]&lt;/code&gt;. &lt;code&gt;{"results": []}&lt;/code&gt;. An empty string. No &lt;code&gt;isError&lt;/code&gt;, no timeout, nothing for any timer or error handler to catch.&lt;/p&gt;

&lt;p&gt;The model can't distinguish "I looked and there is nothing" from "I failed to look." So it does the only thing it can: it &lt;strong&gt;believes the empty answer&lt;/strong&gt;. No matching users, so it creates one (duplicate). No existing config, so it writes a fresh one (overwrites yours). The failure doesn't surface in that turn — it surfaces three to six turns later, in a place that looks unrelated to your server. That's why this mode is worse than an error &lt;em&gt;and&lt;/em&gt; worse than a hang: both of those at least mark the spot where things broke.&lt;/p&gt;

&lt;p&gt;The fix costs one sentence of formatting. Make empty results &lt;strong&gt;self-describing&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instead of &lt;code&gt;[]&lt;/code&gt; → &lt;code&gt;"0 rows matched status='active' AND region='eu'. The table has 1,204 rows total."&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Distinguish the three states explicitly: &lt;em&gt;found N&lt;/em&gt;, &lt;em&gt;found none (and the query definitely ran)&lt;/em&gt;, &lt;em&gt;could not run the query&lt;/em&gt; — and make the last one &lt;code&gt;isError: true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Treat fallback values as errors. A lookup that "defaults to 0" on failure will eventually report 0 revenue as a successful reading.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a human teammate answered your question with silence, you'd ask a follow-up. The model won't — the empty result &lt;em&gt;is&lt;/em&gt; an answer as far as it can tell. Your server has to volunteer the difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The checklist
&lt;/h2&gt;

&lt;p&gt;For every tool your MCP server exposes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Execution failures return &lt;code&gt;isError: true&lt;/code&gt; with &lt;strong&gt;what failed, why, and what a valid retry looks like&lt;/strong&gt; in the text.&lt;/li&gt;
&lt;li&gt;Protocol-level problems (bad arguments) are rejected as protocol errors, not smuggled into results.&lt;/li&gt;
&lt;li&gt;Slow work sends progress notifications; nothing relies on the 28-hour default being someone else's problem.&lt;/li&gt;
&lt;li&gt;Set a realistic per-server &lt;code&gt;timeout&lt;/code&gt; in &lt;code&gt;.mcp.json&lt;/code&gt; instead of inheriting defaults.&lt;/li&gt;
&lt;li&gt;Empty results say &lt;strong&gt;what was searched and what "empty" means&lt;/strong&gt;. Never bare &lt;code&gt;[]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;No silent fallbacks. A default value on the failure path is a lie with a delay.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The theme across all three modes is the same: the result string is the only telemetry the model has. Spend it well.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I publish one verified deep-dive like this every day — the claims above were checked against the MCP spec and Claude Code docs on the day of writing. Follow &lt;a href="https://dev.to/rulestack"&gt;Rulestack&lt;/a&gt; if you want the next one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>claudecode</category>
      <category>devtools</category>
      <category>debugging</category>
    </item>
    <item>
      <title>We cut our CLAUDE.md from 548KB to 34KB: what loads when, measured — and the commit gate that keeps it small</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Tue, 18 Aug 2026 05:35:36 +0000</pubDate>
      <link>https://dev.to/rulestack/we-cut-our-claudemd-from-548kb-to-34kb-what-loads-when-measured-and-the-commit-gate-that-keeps-1kpk</link>
      <guid>https://dev.to/rulestack/we-cut-our-claudemd-from-548kb-to-34kb-what-loads-when-measured-and-the-commit-gate-that-keeps-1kpk</guid>
      <description>&lt;p&gt;Our CLAUDE.md was 548KB. Every session — including every subagent — loaded all of it before doing any work. One measured headless run wrote about 150,000 tokens to cache before the actual task started, and the file itself was the dominant contributor.&lt;/p&gt;

&lt;p&gt;This week we cut it to 34KB without deleting a single obligation. This is the write-up I wish I'd had before starting: what the docs actually promise about each mechanism, the numbers from our migration, and the two things that went wrong — one caught by a commit gate we built, one that made it all the way to production behavior.&lt;/p&gt;

&lt;p&gt;If you want the general taxonomy of what belongs where, I wrote that up separately in &lt;a href="https://dev.to/rulestack/what-actually-belongs-in-claudemd-and-what-to-move-to-skills-hooks-or-docs-34id"&gt;what actually belongs in CLAUDE.md&lt;/a&gt;. This post is the case study with measurements.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mechanics that make splitting worth it
&lt;/h2&gt;

&lt;p&gt;Everything below is from the official memory and skills docs (&lt;code&gt;code.claude.com/docs/en/memory.md&lt;/code&gt;, checked 2026-08-18).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md loads into every session, in full.&lt;/strong&gt; The docs are direct about the cost: files are loaded into the context window at session start, and the guidance is to &lt;em&gt;target under 200 lines per CLAUDE.md file&lt;/em&gt;, because "longer files consume more context and reduce adherence." Ours was over 2,200 lines at its peak. Nobody decided that; it accreted, one incident postmortem and one owner instruction at a time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@path&lt;/code&gt; imports do not save you anything.&lt;/strong&gt; This is the reorganization trap. Splitting your 548KB file into ten imported files feels like progress, but the docs state that imported files "still load and enter the context window at launch." Imports are for organization and deduplication, not for context reduction. If your goal is a smaller startup footprint, imports are a no-op.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Path-scoped rules load on demand.&lt;/strong&gt; Files in &lt;code&gt;.claude/rules/&lt;/code&gt; with a &lt;code&gt;paths&lt;/code&gt; frontmatter field "only apply when Claude is working with files matching the specified patterns." A rule without &lt;code&gt;paths&lt;/code&gt; loads at launch like CLAUDE.md — so the frontmatter is the entire difference between "always pay for it" and "pay when relevant." Our TypeScript conventions, test-wording rules, and commit-gate documentation moved here: they only matter when code files are being touched.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skills load in two stages.&lt;/strong&gt; A skill's &lt;code&gt;description&lt;/code&gt; is always in context (that's how Claude knows the skill exists), but the full &lt;code&gt;SKILL.md&lt;/code&gt; body loads only when the skill is invoked. This is the mechanism that actually absorbs procedures. Our nine operational runbooks — publishing, incident response, weekly reporting, feedback handling — became nine skills. Their combined body text left the every-session budget entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTML comments are free.&lt;/strong&gt; Block-level &lt;code&gt;&amp;lt;!-- comments --&amp;gt;&lt;/code&gt; in CLAUDE.md are stripped before injection into context. Maintainer notes cost nothing. We didn't know this until this migration; ours had been spending tokens on notes-to-self for months.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we actually moved
&lt;/h2&gt;

&lt;p&gt;The sorting rule that emerged, after a few wrong drafts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stays in CLAUDE.md&lt;/strong&gt;: anything needed to &lt;em&gt;decide what to do this session&lt;/em&gt; — the priority table, hard prohibitions, the trigger conditions that point at everything else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills&lt;/strong&gt;: anything that is a &lt;em&gt;procedure&lt;/em&gt; — you only need the steps once you've decided to do the task. Each row in our trigger table now names the skill that owns the details.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.claude/rules/&lt;/code&gt; with &lt;code&gt;paths&lt;/code&gt;&lt;/strong&gt;: anything that is a &lt;em&gt;convention about code&lt;/em&gt; — irrelevant until a matching file is open.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;docs/&lt;/code&gt;&lt;/strong&gt;: anything that is &lt;em&gt;reference&lt;/em&gt; — glossaries, CLI tables, architecture decisions. Loaded by grep, not by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An archive file&lt;/strong&gt;: the full pre-migration text, verbatim. History stays greppable without being resident.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result: 548KB → 34KB resident. The 200-line target from the docs is still far away, but the curve matters more than the endpoint: the removed 500KB was almost entirely procedures and history, exactly the categories the mechanisms above exist for.&lt;/p&gt;

&lt;p&gt;One number worth knowing if you use subagents heavily: CLAUDE.md loads into &lt;em&gt;every subagent too&lt;/em&gt; (&lt;a href="https://dev.to/rulestack/your-claudemd-loads-into-every-subagent-the-context-multiplier-nobody-budgets-for-440g"&gt;measured here&lt;/a&gt;). Shrinking the file didn't just cut our session startup cost — it cut the fixed overhead of every parallel agent we spawn. For fan-out workloads, the multiplier is the real bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enforcement, because advice doesn't persist
&lt;/h2&gt;

&lt;p&gt;A slimmed file regrows unless something pushes back. We added two mechanical layers the same day:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A size check in our health monitor&lt;/strong&gt;: warn at 45KB, alert at 60KB, evaluated every session. The number will creep; the check makes the creep visible instead of silent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A structure commit gate&lt;/strong&gt;: a test that fails the commit if CLAUDE.md references a skill directory that doesn't exist, if a skill exists but no trigger in CLAUDE.md points at it, or if a rules file is missing its &lt;code&gt;paths&lt;/code&gt; frontmatter. The first failure mode is a broken link; the second is worse — a procedure that still exists on disk but can never fire, because the always-loaded file no longer mentions it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The gate caught a real dangling reference during the migration itself. Cheap test, immediate payoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure the gate could not catch
&lt;/h2&gt;

&lt;p&gt;Here's the one that reached production behavior, and it's the most instructive thing in this post.&lt;/p&gt;

&lt;p&gt;Before the migration, the owner had asked us to pause a heavy weekly review job "for a while." That pause was implemented narrowly — one scheduled workflow got disabled — while a &lt;em&gt;sibling&lt;/em&gt; mechanism with a confusingly similar name stayed active in the frequency table. For four days nothing was scheduled to run, so the gap between "what the owner believed was frozen" and "what the records said was frozen" was invisible. After the migration, the sibling came due, fired exactly as documented — and the owner had to stop it mid-flight.&lt;/p&gt;

&lt;p&gt;The migration didn't cause that. Our verification diffed every obligation old-vs-new and found nothing lost, because nothing &lt;em&gt;was&lt;/em&gt; lost. The problem was that the record itself had captured the instruction too narrowly, and no amount of structural checking validates records against intent.&lt;/p&gt;

&lt;p&gt;Two takeaways we encoded afterwards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Freezes need a first-class representation.&lt;/strong&gt; "Disabled the workflow" is a point action; "this whole category is paused" is a state. We now keep pause state in a small JSON file that both the runner (which refuses to start) and the health monitor (which reminds every session that the pause exists) read. A pause that isn't visibly asserted somewhere will eventually be forgotten by one side or the other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When an instruction could map to more than one mechanism, ask before mapping it to one.&lt;/strong&gt; The expensive part of our incident wasn't the wasted compute; it was that a clarifying question — "the Saturday audit, or the Monday review as well?" — was never asked.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A checklist, if your file is heading past 100KB
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Read your CLAUDE.md and label every block: &lt;em&gt;decision&lt;/em&gt;, &lt;em&gt;procedure&lt;/em&gt;, &lt;em&gt;code convention&lt;/em&gt;, &lt;em&gt;reference&lt;/em&gt;, &lt;em&gt;history&lt;/em&gt;. Only the first category earns residency.&lt;/li&gt;
&lt;li&gt;Procedures → skills. Verify each skill is reachable from a trigger that stays in CLAUDE.md.&lt;/li&gt;
&lt;li&gt;Code conventions → &lt;code&gt;.claude/rules/&lt;/code&gt; &lt;strong&gt;with &lt;code&gt;paths&lt;/code&gt;&lt;/strong&gt;. Without the frontmatter you've just renamed the problem.&lt;/li&gt;
&lt;li&gt;Reference and history → &lt;code&gt;docs/&lt;/code&gt; plus a verbatim archive. Grep replaces residency.&lt;/li&gt;
&lt;li&gt;Do not use &lt;code&gt;@imports&lt;/code&gt; for any of this — imports load at launch and save nothing.&lt;/li&gt;
&lt;li&gt;Add a size check and a dangling-reference check to whatever gate you already run before commits.&lt;/li&gt;
&lt;li&gt;Audit anything you'd previously "paused" or "frozen" in prose. Prose intent doesn't survive restructuring; state files do.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The docs' 200-line target sounded absurd to us at 548KB. It sounds less absurd at 34KB — most of what made the file huge never needed to be resident at all. It needed to be &lt;em&gt;findable&lt;/em&gt;, which is a different property, and a much cheaper one.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Canonical home for this piece: &lt;a href="https://dev.to/rulestack"&gt;dev.to/rulestack&lt;/a&gt; — day-to-day findings land on Bluesky first: &lt;a href="https://bsky.app/profile/ai-shop.bsky.social" rel="noopener noreferrer"&gt;@ai-shop.bsky.social&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>productivity</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Your MCP server is configured but Claude can't see it: reading claude mcp list status</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Mon, 17 Aug 2026 07:22:05 +0000</pubDate>
      <link>https://dev.to/rulestack/your-mcp-server-is-configured-but-claude-cant-see-it-reading-claude-mcp-list-status-3fm1</link>
      <guid>https://dev.to/rulestack/your-mcp-server-is-configured-but-claude-cant-see-it-reading-claude-mcp-list-status-3fm1</guid>
      <description>&lt;p&gt;You ran &lt;code&gt;claude mcp add&lt;/code&gt;, it printed &lt;code&gt;Added ...&lt;/code&gt;, and Claude still acts like the server does not exist. The frustrating part is that nothing failed. The add wrote your configuration exactly as asked, and the reason the tools are missing is somewhere else entirely.&lt;/p&gt;

&lt;p&gt;This is a walk through what each status in &lt;code&gt;claude mcp list&lt;/code&gt; actually tells you, and the order I check things in when a server is configured but invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  First: &lt;code&gt;Added&lt;/code&gt; means written, not connected
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;claude mcp add&lt;/code&gt; confirms a successful add by printing an &lt;code&gt;Added ...&lt;/code&gt; line, and that line means the configuration was written. It is not a connection test.&lt;/p&gt;

&lt;p&gt;The health check happens later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command shows a status next to each server: &lt;code&gt;✔ Connected&lt;/code&gt;, &lt;code&gt;! Needs authentication&lt;/code&gt;, or &lt;code&gt;✘ Failed to connect&lt;/code&gt;. A failure status there means Claude Code could not connect to that server — not that the list command itself failed. People read a red mark and assume their command was wrong, then re-run &lt;code&gt;claude mcp add&lt;/code&gt; with slightly different arguments, which writes a second configuration and makes the situation harder to read.&lt;/p&gt;

&lt;p&gt;So: &lt;code&gt;Added&lt;/code&gt; and &lt;code&gt;✘ Failed to connect&lt;/code&gt; can both be true at once, and they are describing different steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  The most common cause: scope
&lt;/h2&gt;

&lt;p&gt;MCP servers live at one of three scopes, and the default is the narrow one.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Loads in&lt;/th&gt;
&lt;th&gt;Shared with team&lt;/th&gt;
&lt;th&gt;Stored in&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Local (default)&lt;/td&gt;
&lt;td&gt;Current project only&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.claude.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project&lt;/td&gt;
&lt;td&gt;Current project only&lt;/td&gt;
&lt;td&gt;Yes, via version control&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.mcp.json&lt;/code&gt; in project root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User&lt;/td&gt;
&lt;td&gt;All your projects&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.claude.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Local scope is what you get when you do not pass &lt;code&gt;--scope&lt;/code&gt;. A local-scoped server loads only in the project where you added it, stored in &lt;code&gt;~/.claude.json&lt;/code&gt; under that project's path. If you added the server while sitting in &lt;code&gt;~/scratch&lt;/code&gt; and then opened your real repository, the server is not missing — it is scoped to a directory you are no longer in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Same server, three very different lifetimes&lt;/span&gt;
claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http stripe &lt;span class="nt"&gt;--scope&lt;/span&gt; &lt;span class="nb"&gt;local &lt;/span&gt;https://mcp.stripe.com
claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http stripe &lt;span class="nt"&gt;--scope&lt;/span&gt; project https://mcp.stripe.com
claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http stripe &lt;span class="nt"&gt;--scope&lt;/span&gt; user https://mcp.stripe.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One naming trap worth knowing before you go looking for files: MCP "local scope" is not &lt;code&gt;.claude/settings.local.json&lt;/code&gt;. MCP local-scoped servers live in &lt;code&gt;~/.claude.json&lt;/code&gt; in your home directory. The similarly-named settings file in your project directory is a different mechanism entirely. If you have been grepping your repository for a server you added at local scope, that is why you cannot find it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;⏸ Pending approval&lt;/code&gt; — the &lt;code&gt;.mcp.json&lt;/code&gt; case
&lt;/h2&gt;

&lt;p&gt;Project-scoped servers are not trusted on sight. A server from &lt;code&gt;.mcp.json&lt;/code&gt; that is waiting for you appears in &lt;code&gt;claude mcp list&lt;/code&gt; and &lt;code&gt;claude mcp get&lt;/code&gt; as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⏸ Pending approval (run `claude` to approve)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the whole fix: start &lt;code&gt;claude&lt;/code&gt; interactively in that project and approve it. The status is not an error, and re-adding the server will not clear it.&lt;/p&gt;

&lt;p&gt;The version of this that costs people an afternoon involves a cloned repository. As of v2.1.196, &lt;code&gt;claude mcp list&lt;/code&gt; and &lt;code&gt;claude mcp get&lt;/code&gt; read &lt;code&gt;.mcp.json&lt;/code&gt; approvals only from settings files that are not checked into the repository, until you trust the workspace by running &lt;code&gt;claude&lt;/code&gt; in it and accepting the trust dialog. That means a cloned repository cannot approve its own servers: &lt;code&gt;enableAllProjectMcpServers&lt;/code&gt; or &lt;code&gt;enabledMcpjsonServers&lt;/code&gt; committed to the project's &lt;code&gt;.claude/settings.json&lt;/code&gt; is ignored in an untrusted folder, and the server sits at &lt;code&gt;⏸ Pending approval&lt;/code&gt; instead of connecting.&lt;/p&gt;

&lt;p&gt;Approvals from these sources still apply in an untrusted folder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your user &lt;code&gt;~/.claude/settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;managed settings&lt;/li&gt;
&lt;li&gt;settings passed with &lt;code&gt;--settings&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An untracked &lt;code&gt;.claude/settings.local.json&lt;/code&gt; also works, but only after you accept a trust dialog for that folder or a parent — Claude Code runs git to check whether the file is tracked, and it only runs that check in a trusted folder. The exception is your own configuration home: your home directory, or a directory whose &lt;code&gt;.claude&lt;/code&gt; you have set as &lt;code&gt;CLAUDE_CONFIG_DIR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you maintain a template repository that ships an &lt;code&gt;.mcp.json&lt;/code&gt; and a committed approval, this is the behaviour that makes it not work for the person who clones it. The approval has to come from their side of the line, not yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;✘ Rejected&lt;/code&gt; and the two lists that look alike
&lt;/h2&gt;

&lt;p&gt;A rejected server shows as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✘ Rejected (see disabledMcpjsonServers in settings)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;disabledMcpjsonServers&lt;/code&gt; entry in any settings file rejects the server, and it wins. If someone added a server name there months ago to quiet a noisy tool, that entry is still doing its job.&lt;/p&gt;

&lt;p&gt;Here is where it gets genuinely confusing. There are two pairs of settings with nearly identical names, and they control different things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;enabledMcpjsonServers&lt;/code&gt; / &lt;code&gt;disabledMcpjsonServers&lt;/code&gt; — approval of servers defined in a project's &lt;code&gt;.mcp.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enabledMcpServers&lt;/code&gt; / &lt;code&gt;disabledMcpServers&lt;/code&gt; — the per-project on/off toggle recorded in &lt;code&gt;~/.claude.json&lt;/code&gt; when you flip a server in the &lt;code&gt;/mcp&lt;/code&gt; panel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second pair splits by default state rather than by your intent. &lt;code&gt;disabledMcpServers&lt;/code&gt; is an opt-out list for servers that default to on. &lt;code&gt;enabledMcpServers&lt;/code&gt; is an opt-in list for built-in servers that default to off, such as &lt;code&gt;computer-use&lt;/code&gt;. Claude Code consults exactly one of the two lists for each server, so neither overrides the other — adding a regular server to &lt;code&gt;enabledMcpServers&lt;/code&gt; does nothing at all, and the entry is ignored rather than flagged.&lt;/p&gt;

&lt;p&gt;When a server refuses to come back after you toggled it off, check which of the four keys is holding it, because three of them look plausible and only one is responsible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Statuses that are not failures
&lt;/h2&gt;

&lt;p&gt;A few displays look wrong and are not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;cached 2h ago · connects on first use · 5 tools&lt;/code&gt;.&lt;/strong&gt; A remote server you have used before can load its tool list from a previous session instead of connecting at startup, then connect the first time Claude calls one of its tools. The tools are available from your first message; there is nothing to do. Set &lt;code&gt;MCP_DISCOVERY_CACHE=0&lt;/code&gt; if you want every server to connect at startup instead. This status needs v2.1.221 or later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;not configured&lt;/code&gt;.&lt;/strong&gt; A remote server whose configuration has an empty &lt;code&gt;url&lt;/code&gt; shows this in &lt;code&gt;/mcp&lt;/code&gt;, in &lt;code&gt;claude mcp list&lt;/code&gt;, and in the plugin manager, and Claude Code does not try to connect. Plugins use empty entries as placeholders for a connector you set up later. The detail view says &lt;code&gt;No URL configured for this server&lt;/code&gt;; fill in the &lt;code&gt;url&lt;/code&gt; to connect it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A WebSocket server missing from the list entirely.&lt;/strong&gt; WebSocket servers do not appear in &lt;code&gt;claude mcp list&lt;/code&gt; output. Use &lt;code&gt;claude mcp get &amp;lt;name&amp;gt;&lt;/code&gt; or the &lt;code&gt;/mcp&lt;/code&gt; panel to check them. If you added one and it vanished, the list command is the wrong instrument, not the configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the status is &lt;code&gt;✘ Failed to connect&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Since v2.1.219, &lt;code&gt;claude mcp list&lt;/code&gt; appends the failure detail to the status line, and &lt;code&gt;claude mcp get&lt;/code&gt; shows it on an &lt;code&gt;Issue:&lt;/code&gt; line: the HTTP status or error code plus any error text the server returned. Credential-like text is redacted, and the expanded server URL is never included, because it can carry secrets. That is also why a &lt;code&gt;✘ Connection error&lt;/code&gt; status carries no detail — the exception text there can embed the URL.&lt;/p&gt;

&lt;p&gt;Two configuration-level causes worth ruling out before you go debugging the server itself:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invisible whitespace.&lt;/strong&gt; Claude Code warns when an MCP config value carries hidden leading or trailing whitespace, which usually comes from pasting a token that brought a newline with it. It checks &lt;code&gt;command&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, each &lt;code&gt;args&lt;/code&gt; entry, and the values and key names under &lt;code&gt;env&lt;/code&gt; and &lt;code&gt;headers&lt;/code&gt;, and it names the affected field without echoing the value — for example &lt;code&gt;Leading or trailing whitespace in: headers.Authorization&lt;/code&gt;. It does not trim the value. It uses it exactly as written, so the fix is yours to make.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A reserved name.&lt;/strong&gt; These names belong to Claude Code's built-in servers: &lt;code&gt;workspace&lt;/code&gt;, &lt;code&gt;claude-in-chrome&lt;/code&gt;, &lt;code&gt;computer-use&lt;/code&gt;, &lt;code&gt;Claude Preview&lt;/code&gt;, and &lt;code&gt;Claude Browser&lt;/code&gt;. A configuration that defines a server with one of these is skipped at load time with a warning to rename it, and &lt;code&gt;claude mcp add&lt;/code&gt; rejects the name outright.&lt;/p&gt;

&lt;p&gt;For stdio servers, one more syntax point that produces confusing failures: the &lt;code&gt;--&lt;/code&gt; separates Claude's own options, such as &lt;code&gt;--transport&lt;/code&gt;, &lt;code&gt;--env&lt;/code&gt;, and &lt;code&gt;--scope&lt;/code&gt;, from the command and arguments that run your server. Everything after &lt;code&gt;--&lt;/code&gt; is passed through untouched. Leave it out and your server's flags get read as Claude's.&lt;/p&gt;

&lt;h2&gt;
  
  
  The order I actually check
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Am I in the same project where I added it? If it was local scope, that is the whole question.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;claude mcp list&lt;/code&gt; — is it listed at all? If it is a WebSocket server, use &lt;code&gt;claude mcp get&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;Is the status &lt;code&gt;⏸ Pending approval&lt;/code&gt;? Run &lt;code&gt;claude&lt;/code&gt; in the project and approve. If it stays pending in a cloned repo, the approval needs to come from a file that is not checked in, or from your user settings.&lt;/li&gt;
&lt;li&gt;Is it &lt;code&gt;✘ Rejected&lt;/code&gt;? Search every settings file for &lt;code&gt;disabledMcpjsonServers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Is it &lt;code&gt;✘ Failed to connect&lt;/code&gt;? Read the appended detail, then check for pasted whitespace and reserved names before blaming the server.&lt;/li&gt;
&lt;li&gt;Toggled off and won't come back? Check &lt;code&gt;disabledMcpServers&lt;/code&gt; in &lt;code&gt;~/.claude.json&lt;/code&gt; — and remember the enabled/disabled pair splits by the server's default state, not by what you meant.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these are exotic. The reason they cost time is that the failure is silent by design: a server that is scoped elsewhere, or waiting for approval, is behaving correctly, and correct behaviour does not produce an error message to search for.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written while building &lt;a href="https://rulestack.gumroad.com/?ref=devto" rel="noopener noreferrer"&gt;Rulestack&lt;/a&gt; — configuration packs for Cursor, Claude Code, and Codex, including a set of forkable &lt;code&gt;mcp.json&lt;/code&gt; templates.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I post shorter notes on this kind of thing at &lt;a href="https://bsky.app/profile/ai-shop.bsky.social" rel="noopener noreferrer"&gt;@ai-shop.bsky.social&lt;/a&gt; on Bluesky.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>mcp</category>
      <category>debugging</category>
      <category>devtools</category>
    </item>
    <item>
      <title>CLAUDE.md @-imports: how paths resolve, how deep they go, and why yours silently did not load</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Sat, 15 Aug 2026 14:07:43 +0000</pubDate>
      <link>https://dev.to/rulestack/claudemd-imports-how-paths-resolve-how-deep-they-go-and-why-yours-silently-did-not-load-2mgf</link>
      <guid>https://dev.to/rulestack/claudemd-imports-how-paths-resolve-how-deep-they-go-and-why-yours-silently-did-not-load-2mgf</guid>
      <description>&lt;p&gt;You split a long &lt;code&gt;CLAUDE.md&lt;/code&gt; into pieces and pulled them back in with &lt;code&gt;@&lt;/code&gt; imports. Now half of it doesn't seem to reach Claude, and nothing tells you which half.&lt;/p&gt;

&lt;p&gt;Four rules in the import mechanism explain almost every case of this. None of them produce an error message.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an import actually does
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@path/to/import&lt;/code&gt; inside a &lt;code&gt;CLAUDE.md&lt;/code&gt; expands the referenced file and loads it into context &lt;strong&gt;at launch&lt;/strong&gt;, alongside the &lt;code&gt;CLAUDE.md&lt;/code&gt; that references it.&lt;/p&gt;

&lt;p&gt;That word — &lt;em&gt;launch&lt;/em&gt; — is the first thing people get wrong. Splitting a large memory file into imports does &lt;strong&gt;not&lt;/strong&gt; reduce how much context you spend. The docs say it plainly: imports help organization, but imported files load at launch either way. If your actual goal is a smaller prompt, imports are the wrong tool; path-scoped rules that only load when Claude touches matching files are the right one.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Relative paths resolve from the importing file, not your working directory
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Relative paths resolve relative to the file containing the import, not the working directory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So &lt;code&gt;docs/ai/CLAUDE.md&lt;/code&gt; containing &lt;code&gt;@guides/style.md&lt;/code&gt; looks for &lt;code&gt;docs/ai/guides/style.md&lt;/code&gt; — no matter which directory you started &lt;code&gt;claude&lt;/code&gt; from.&lt;/p&gt;

&lt;p&gt;This bites hardest in monorepos, where the same import line gets copy-pasted into a nested &lt;code&gt;CLAUDE.md&lt;/code&gt; one level deeper and quietly resolves somewhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Recursion stops at four hops
&lt;/h2&gt;

&lt;p&gt;Imported files can import other files, up to a maximum depth of &lt;strong&gt;four hops&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you've built an index-of-indexes — root memory imports a manifest, which imports per-area manifests, which import the actual rules — you can run out of depth before reaching the file that holds your content. Nothing warns you. The rules simply aren't there.&lt;/p&gt;

&lt;p&gt;Flatten the chain, or move the deep files up.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Backticks switch imports off
&lt;/h2&gt;

&lt;p&gt;Import parsing skips Markdown code spans and fenced code blocks. Writing &lt;code&gt;`@README`&lt;/code&gt; keeps the text literal; writing &lt;code&gt;@README&lt;/code&gt; outside backticks imports the file.&lt;/p&gt;

&lt;p&gt;That cuts both ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mentioning a path in prose without backticks silently imports it.&lt;/strong&gt; &lt;code&gt;See @package.json for the scripts&lt;/code&gt; doesn't mention &lt;code&gt;package.json&lt;/code&gt; — it loads the whole file into every session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrapping an intended import in backticks disables it.&lt;/strong&gt; Easy to do by habit, and easy for a formatter or a well-meaning reviewer to do for you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're documenting a path, wrap it. If you're importing one, don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Imports pointing outside the project need approval — and you only get asked once
&lt;/h2&gt;

&lt;p&gt;An import in a project-level memory file counts as &lt;strong&gt;external&lt;/strong&gt; when its path resolves outside your working directory. The classic example is sharing personal instructions across git worktrees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Individual Preferences
- @~/.claude/my-project-instructions.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time Claude Code meets external imports in a project, it shows an approval dialog listing the files. &lt;strong&gt;If you decline, the imports stay disabled and the dialog does not appear again.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the rule behind most "but it works on my machine" reports. The dialog exists to protect you from files other people commit to a shared repo, so it's the correct default — but a single declined prompt months ago is indistinguishable, from the outside, from a broken path.&lt;/p&gt;

&lt;p&gt;Imports inside user-scope memory (&lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;, &lt;code&gt;~/.claude/rules/&lt;/code&gt;) don't trigger the dialog. Those are files you wrote yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AGENTS.md case, since everyone hits it
&lt;/h2&gt;

&lt;p&gt;Claude Code reads &lt;code&gt;CLAUDE.md&lt;/code&gt;, not &lt;code&gt;AGENTS.md&lt;/code&gt;. If your repo already has &lt;code&gt;AGENTS.md&lt;/code&gt; for other agents, the documented pattern is a &lt;code&gt;CLAUDE.md&lt;/code&gt; that imports it and then appends whatever is Claude-specific:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;@AGENTS.md

&lt;span class="gu"&gt;## Claude Code&lt;/span&gt;

Use plan mode for changes under &lt;span class="sb"&gt;`src/billing/`&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A symlink works too, if you don't need to add anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; AGENTS.md CLAUDE.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It prints nothing on success — so confirm it in the next session with &lt;code&gt;/context&lt;/code&gt; and look for &lt;code&gt;CLAUDE.md&lt;/code&gt; under &lt;strong&gt;Memory files&lt;/strong&gt;. On Windows, symlinks need Administrator privileges or Developer Mode, so prefer the import there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Personal instructions, and the worktree trap
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.local.md&lt;/code&gt; at the project root loads alongside &lt;code&gt;CLAUDE.md&lt;/code&gt; and is treated the same way. Add it to &lt;code&gt;.gitignore&lt;/code&gt; so it never gets committed.&lt;/p&gt;

&lt;p&gt;One catch: because it's gitignored, a &lt;code&gt;CLAUDE.local.md&lt;/code&gt; only exists in the worktree where you created it. If you work across several worktrees of the same repo, import a file from your home directory instead — which puts you back in rule 4, approval dialog included.&lt;/p&gt;

&lt;h2&gt;
  
  
  A five-step check when an import didn't land
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Resolve the path from the importing file's directory&lt;/strong&gt;, not from where you launched Claude.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Count the hops.&lt;/strong&gt; More than four and the tail is gone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grep for backticked &lt;code&gt;@&lt;/code&gt;.&lt;/strong&gt; Also grep for un-backticked ones in prose you didn't mean to import.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does any path leave the project?&lt;/strong&gt; If so, you may have declined the approval dialog — on this machine, once, some time ago.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run &lt;code&gt;/context&lt;/code&gt;&lt;/strong&gt; and read what's actually listed under Memory files. That's the only answer that isn't a guess.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 5 is the one worth building a habit around. Every other step is a theory about what loaded; &lt;code&gt;/context&lt;/code&gt; is the observation.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Behaviour above verified against the Claude Code memory documentation on 2026-08-15. If it changes, the &lt;code&gt;/context&lt;/code&gt; check still tells you the truth.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I write one of these a day on how coding agents actually read your config. If that's useful, &lt;a href="https://bsky.app/profile/ai-shop.bsky.social" rel="noopener noreferrer"&gt;Rulestack on Bluesky&lt;/a&gt; is where they go up first.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ready-made rule and skill packs: &lt;a href="https://rulestack.gumroad.com?ref=devto" rel="noopener noreferrer"&gt;rulestack.gumroad.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Claude Code fork vs subagent: what /fork, /subtask, and subagent_type: "fork" each copy</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Fri, 14 Aug 2026 13:43:47 +0000</pubDate>
      <link>https://dev.to/rulestack/claude-code-fork-vs-subagent-what-fork-subtask-and-subagenttype-fork-each-copy-229a</link>
      <guid>https://dev.to/rulestack/claude-code-fork-vs-subagent-what-fork-subtask-and-subagenttype-fork-each-copy-229a</guid>
      <description>&lt;p&gt;If you have ever delegated work to a Claude Code subagent and then looked at your usage, you have probably had the same reaction I did: &lt;em&gt;why did that cost so much for such a small job?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answer is that a plain subagent starts from nothing. It does not know what you have been doing, so the whole prompt prefix — system prompt, tool schemas, your &lt;code&gt;CLAUDE.md&lt;/code&gt;, whatever else is loaded — gets sent again for that agent. When I &lt;a href="https://dev.to/rulestack/what-a-claude-code-subagent-actually-costs-measuring-the-436k-token-fixed-overhead-46g6"&gt;measured this in a real fan-out&lt;/a&gt;, the fixed overhead came out around 436,000 tokens per agent, independent of what the agent was actually asked to do.&lt;/p&gt;

&lt;p&gt;As of &lt;strong&gt;2.1.232&lt;/strong&gt;, there is a subagent that does not do that. The changelog line is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Subagent forking is now on by default: a &lt;code&gt;subagent_type: "fork"&lt;/code&gt; subagent inherits the full conversation and prompt cache, and non-teammate agent spawns in interactive sessions now run in the background by default&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Two defaults changed in one line. This post is about the first one, and about a naming problem that makes it easy to misread.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three different things in Claude Code are called "fork"
&lt;/h2&gt;

&lt;p&gt;This tripped me up, so it is worth separating them before anything else. All three exist today, they do different things, and they arrived at different times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;context: fork&lt;/code&gt; in skill frontmatter&lt;/strong&gt; — added in 2.1.0.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-skill&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;..."&lt;/span&gt;
&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fork&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes a skill or slash command run in a forked subagent context instead of inline in your session. It is a property of the &lt;em&gt;skill&lt;/em&gt;, declared by whoever wrote the skill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;/fork&lt;/code&gt;&lt;/strong&gt; — the slash command. Its behavior changed in 2.1.212:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;/fork&lt;/code&gt; now copies your conversation into a new background session (its own row in &lt;code&gt;claude agents&lt;/code&gt;) while you keep working; the in-session subagent it used to launch is now &lt;code&gt;/subtask&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So &lt;code&gt;/fork&lt;/code&gt; is not a subagent anymore. It is a &lt;em&gt;session copy&lt;/em&gt; — a second, independent session that starts from where you are. If what you wanted was the old in-session behavior, that moved to &lt;code&gt;/subtask&lt;/code&gt;. If you learned &lt;code&gt;/fork&lt;/code&gt; before 2.1.212 and have not touched it since, this is the one that will surprise you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. &lt;code&gt;subagent_type: "fork"&lt;/code&gt;&lt;/strong&gt; — the one that is now default in 2.1.232.&lt;/p&gt;

&lt;p&gt;This is a subagent, spawned the normal way, that inherits your conversation and — the part that matters for cost — your prompt cache.&lt;/p&gt;

&lt;p&gt;Same word, three layers: skill frontmatter, session command, subagent type.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "inherits the prompt cache" actually buys you
&lt;/h2&gt;

&lt;p&gt;Here is the part I got wrong in &lt;a href="https://dev.to/rulestack/your-claudemd-loads-into-every-subagent-the-context-multiplier-nobody-budgets-for-440g"&gt;a companion post about &lt;code&gt;CLAUDE.md&lt;/code&gt; and subagents&lt;/a&gt;, and a commenter on that post is the reason I went back and checked.&lt;/p&gt;

&lt;p&gt;Inheriting the cache does &lt;strong&gt;not&lt;/strong&gt; reduce the token count. Those tokens are still delivered to the model. What changes is which rate they bill at: a cache read instead of fresh input. So if you are reasoning about &lt;em&gt;context window pressure&lt;/em&gt;, nothing improved. If you are reasoning about &lt;em&gt;cost&lt;/em&gt;, quite a lot did.&lt;/p&gt;

&lt;p&gt;This is the same idea as another 2.1.229 change I had originally filed as routine:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Improved workflow fan-outs to stagger same-prefix sibling agents so subsequent agents read the cached prompt prefix instead of re-paying it (&lt;code&gt;CLAUDE_CODE_WORKFLOW_PREFIX_STAGGER_MS=0&lt;/code&gt; disables)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read those two lines together and the shape is clear. When ten agents share a prefix and all start simultaneously, none of them can read a cache that no one has written yet, so all ten pay full price. Staggering them means the first writes the cache and the other nine read it. Forking is the same trick applied to the parent conversation rather than to siblings.&lt;/p&gt;

&lt;p&gt;Which means my ~436k-per-agent figure is best read as an &lt;strong&gt;upper bound&lt;/strong&gt;: it is what a cold, unstaggered, non-forked agent costs. Under staggered fan-out, or with a forked subagent, the token count holds and the dollar figure does not. If you are measuring this yourself, split cache-read tokens from fresh tokens before you draw any conclusion — a single "total tokens" number will hide the entire effect you are trying to observe.&lt;/p&gt;

&lt;h2&gt;
  
  
  When you still want a cold subagent
&lt;/h2&gt;

&lt;p&gt;Forking is not strictly better. Inheriting the conversation is exactly what you &lt;em&gt;don't&lt;/em&gt; want in a few common cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Independent review.&lt;/strong&gt; If you want a second opinion on a decision, an agent that has read your reasoning is not independent — it has already been argued into your conclusion. A cold agent that sees only the artifact is the point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context hygiene.&lt;/strong&gt; A long session accumulates dead ends, abandoned approaches, and stale file contents. Forking carries all of it forward. A narrow task with a clean prompt often performs better with less context, not more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anything you would not want repeated.&lt;/strong&gt; The fork inherits the full conversation, including whatever happened to be in it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule I have settled on: fork when the agent needs to &lt;em&gt;continue&lt;/em&gt; something, spawn cold when it needs to &lt;em&gt;check&lt;/em&gt; something.&lt;/p&gt;

&lt;h2&gt;
  
  
  The other default in that line
&lt;/h2&gt;

&lt;p&gt;The same changelog entry also says non-teammate agent spawns in interactive sessions now run in the background by default. Background execution itself is not new — subagents have defaulted to background since 2.1.198 — but the boundary moved again, and it produces a specific confusing moment: you delegate a task, immediately ask "what did it find?", and get told it is still running.&lt;/p&gt;

&lt;p&gt;That is the agent working, not a failure. The result arrives as a completion notification in a later turn. &lt;code&gt;CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1&lt;/code&gt; makes everything synchronous if you would rather block.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to check what you are getting
&lt;/h2&gt;

&lt;p&gt;Two things are worth confirming in your own setup rather than trusting a blog post, including this one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the changelog for your installed version.&lt;/strong&gt; &lt;code&gt;claude --version&lt;/code&gt;, then check that version's entry. Defaults have moved several times in the 2.1.x line, and advice written against 2.1.180 is not advice about 2.1.232.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Look at cache-read versus fresh input in your usage breakdown&lt;/strong&gt;, not at the total. If your fan-out is billing everything as fresh input, either the prefix is not actually shared or the agents are all starting at once.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The general lesson, which outlives this particular release: in Claude Code, "how much does this cost" and "how much context does this consume" have quietly become different questions. A change can improve one and leave the other exactly where it was.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I write these as I work through Claude Code's behavior for &lt;a href="https://rulestack.gumroad.com?ref=devto" rel="noopener noreferrer"&gt;Rulestack&lt;/a&gt;, a small set of packs for Claude Code, Cursor, and Codex. If you want the shorter version of these findings as I hit them, I post them at &lt;a href="https://bsky.app/profile/ai-shop.bsky.social" rel="noopener noreferrer"&gt;@ai-shop.bsky.social&lt;/a&gt; on Bluesky.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>devtools</category>
      <category>cli</category>
    </item>
    <item>
      <title>Auto mode is now Claude Code's default: what the classifier approves, and how to switch back</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Thu, 13 Aug 2026 18:17:17 +0000</pubDate>
      <link>https://dev.to/rulestack/auto-mode-is-now-claude-codes-default-what-the-classifier-approves-and-how-to-switch-back-4j2j</link>
      <guid>https://dev.to/rulestack/auto-mode-is-now-claude-codes-default-what-the-classifier-approves-and-how-to-switch-back-4j2j</guid>
      <description>&lt;p&gt;Starting today — August 14, 2026 — &lt;strong&gt;auto mode is the default permission mode for new Claude Code sessions&lt;/strong&gt; on Pro, Max, and Team plans. If you've never touched your permission settings, your next session starts with a one-time switch prompt, and after that Claude runs most actions without asking you first.&lt;/p&gt;

&lt;p&gt;That's a real behavior change, not a UI tweak. This post covers what the classifier actually approves, the defaults most people find surprising (pushes to &lt;code&gt;main&lt;/code&gt; are allowed), how to keep human checkpoints where you want them, and how to switch back. Everything below is from the official docs as of today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed, precisely
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://code.claude.com/docs/en/permission-modes" rel="noopener noreferrer"&gt;permission modes doc&lt;/a&gt; states the change plainly: as of August 14, 2026, new sessions on Pro, Max, and Team plans start in auto mode. You can still switch modes whenever you want, an existing default you set yourself only changes if you accept a one-time switch prompt, and organization-managed defaults don't move at all.&lt;/p&gt;

&lt;p&gt;Three details worth pulling out of that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Only new sessions&lt;/strong&gt; are affected, and only on Pro, Max, and Team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A &lt;code&gt;defaultMode&lt;/code&gt; you set yourself survives.&lt;/strong&gt; If you already have &lt;code&gt;"defaultMode": "plan"&lt;/code&gt; (or anything else) in your settings, nothing changes unless you accept the switch prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Org-managed defaults are untouched.&lt;/strong&gt; If your admin distributes a mode via managed settings, today changes nothing for you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What auto mode actually is
&lt;/h2&gt;

&lt;p&gt;Auto mode is not "skip permissions." It routes actions through a &lt;strong&gt;separate classifier model&lt;/strong&gt; that reviews each action before it runs. The classifier blocks anything that escalates beyond your request, targets infrastructure it doesn't recognize as yours, or appears driven by hostile content Claude read (prompt injection).&lt;/p&gt;

&lt;p&gt;Two rule layers still run &lt;strong&gt;before&lt;/strong&gt; the classifier is ever consulted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;permissions.deny&lt;/code&gt; rules block outright. Neither the classifier nor your stated intent can override them.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;permissions.ask&lt;/code&gt; rules force a prompt. An explicit ask rule is your stated intent to be asked, so the classifier cannot auto-approve a matching action.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So auto mode changes the &lt;em&gt;default&lt;/em&gt; for unlisted actions, not your explicit rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  The defaults that surprise people
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pushes and PRs are allowed by default.&lt;/strong&gt; Auto mode allows pushes to any branch of the repository you're working in — &lt;em&gt;including the default branch&lt;/em&gt; — and pull request creation. (Before v2.1.211 the classifier only allowed your working branch, branches Claude created, and routine pushes to the default branch; the current default is broader.)&lt;/p&gt;

&lt;p&gt;There are still guardrails inside that: a branch whose name marks it as a deploy target (&lt;code&gt;production&lt;/code&gt;, &lt;code&gt;release&lt;/code&gt;, &lt;code&gt;gh-pages&lt;/code&gt;) is judged on its own terms, including as a production deploy. Force pushes, a secret entering the commit, and history rewrites stay soft-blocked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Soft blocks can be cleared by explicit intent.&lt;/strong&gt; The classifier distinguishes "clean up the repo" (does not authorize a force-push) from "force-push this branch" (does). General requests don't count as explicit intent; naming the exact action does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Narrow allow rules bypass the classifier entirely.&lt;/strong&gt; A rule like &lt;code&gt;Bash(npm test)&lt;/code&gt; carries into auto mode and resolves &lt;em&gt;before&lt;/em&gt; the classifier — only broad rules like &lt;code&gt;Bash(*)&lt;/code&gt; are suspended. A narrow prefix rule can therefore let a destructive argument through unseen. If you want every shell command classified regardless of your allow list, set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"autoMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"classifyAllShell"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Requires v2.1.193+. It trades latency for coverage.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping human checkpoints
&lt;/h2&gt;

&lt;p&gt;If you like auto mode for everything except pushes and PRs, add content-scoped ask rules — they're evaluated before the classifier and always prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ask"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(gh pr create *)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a boundary that must &lt;em&gt;never&lt;/em&gt; be crossed, use &lt;code&gt;permissions.deny&lt;/code&gt; (ideally in managed settings for a team). Stating a boundary in chat ("don't push until I review") also works — the classifier reads it — but it can be lost when context compaction removes the message. Use an ask or deny rule for anything durable.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to switch back
&lt;/h2&gt;

&lt;p&gt;Per session: press &lt;code&gt;Shift+Tab&lt;/code&gt; to cycle modes, or start with &lt;code&gt;claude --permission-mode manual&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As a persistent default, set it in &lt;code&gt;~/.claude/settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"defaultMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"manual"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;manual&lt;/code&gt; is the alias for the config value &lt;code&gt;default&lt;/code&gt; (v2.1.200+; both work). One placement gotcha: &lt;code&gt;"defaultMode": "auto"&lt;/code&gt; is &lt;strong&gt;ignored&lt;/strong&gt; when it comes from a repo's &lt;code&gt;.claude/settings.json&lt;/code&gt; or &lt;code&gt;.claude/settings.local.json&lt;/code&gt; — since v2.1.142 a repository cannot grant itself auto mode. Your own opt-in or opt-out belongs in user settings. (If you're unsure which of your settings files wins, I wrote up &lt;a href="https://dev.to/rulestack/claude-code-settings-precedence-which-of-your-five-settings-files-actually-wins-5133"&gt;the full precedence order&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;Org admins can disable auto mode for everyone by setting &lt;code&gt;permissions.disableAutoMode&lt;/code&gt; to &lt;code&gt;"disable"&lt;/code&gt; in managed settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  When it blocks something you wanted
&lt;/h2&gt;

&lt;p&gt;Auto mode denials are recorded: open &lt;code&gt;/permissions&lt;/code&gt; and check the &lt;strong&gt;Recently denied&lt;/strong&gt; tab. Press &lt;code&gt;r&lt;/code&gt; on a denial to let Claude retry it. Repeated denials for the same destination usually mean the classifier doesn't know that infrastructure is yours — name it in &lt;code&gt;autoMode.environment&lt;/code&gt; in user or managed settings, then verify with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude auto-mode config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;claude auto-mode defaults&lt;/code&gt; prints the built-in rule lists, and &lt;code&gt;claude auto-mode critique&lt;/code&gt; reviews custom rules you've written. One warning from the config reference: if you set &lt;code&gt;environment&lt;/code&gt;, &lt;code&gt;allow&lt;/code&gt;, &lt;code&gt;soft_deny&lt;/code&gt;, or &lt;code&gt;hard_deny&lt;/code&gt; without including the literal &lt;code&gt;"$defaults"&lt;/code&gt; entry, you &lt;strong&gt;replace&lt;/strong&gt; the entire built-in list for that section — including the force-push and &lt;code&gt;curl | bash&lt;/code&gt; soft blocks. Keep &lt;code&gt;"$defaults"&lt;/code&gt; in the array unless you intend to own the whole list.&lt;/p&gt;

&lt;h2&gt;
  
  
  The plan mode connection
&lt;/h2&gt;

&lt;p&gt;Auto mode's classifier also changes plan mode: with auto mode available, the &lt;code&gt;useAutoModeDuringPlan&lt;/code&gt; setting is on by default, so shell commands during planning are reviewed by the classifier instead of prompting you. If you want the exact semantics of what plan mode blocks and what approving a plan switches you into, that's &lt;a href="https://dev.to/rulestack/claude-code-plan-mode-what-it-actually-blocks-what-still-runs-and-what-approving-switches-you-22m3"&gt;yesterday's post&lt;/a&gt;. And if your mental model of allow/ask/deny matching is fuzzy, &lt;a href="https://dev.to/rulestack/claude-code-permission-rules-how-allow-deny-and-ask-actually-match-1bj7"&gt;this one covers the rule syntax&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The honest framing from the docs themselves: auto mode reduces prompts, it doesn't guarantee safety. Trust it with tasks where you trust the general direction — and put ask rules on the actions you'd want to see either way.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I maintain &lt;a href="https://rulestack.gumroad.com?ref=devto" rel="noopener noreferrer"&gt;Rulestack&lt;/a&gt; — tested rules packs and skills for Claude Code, Cursor, and Codex, kept in sync with changes like this one. For a daily changelog-watch on AI coding tools, follow &lt;a href="https://bsky.app/profile/ai-shop.bsky.social" rel="noopener noreferrer"&gt;@ai-shop.bsky.social&lt;/a&gt; on Bluesky.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>devtools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Claude Code plan mode: what it actually blocks, what still runs, and what approving switches you into</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Thu, 13 Aug 2026 13:14:20 +0000</pubDate>
      <link>https://dev.to/rulestack/claude-code-plan-mode-what-it-actually-blocks-what-still-runs-and-what-approving-switches-you-22m3</link>
      <guid>https://dev.to/rulestack/claude-code-plan-mode-what-it-actually-blocks-what-still-runs-and-what-approving-switches-you-22m3</guid>
      <description>&lt;p&gt;Plan mode looks simple from the outside: press Shift+Tab a couple of times, Claude stops editing, you get a plan to review. But the mechanics underneath have changed enough in recent releases that most mental models of it are stale — especially around &lt;em&gt;what commands still run while you're planning&lt;/em&gt; and &lt;em&gt;which permission mode you land in after you approve&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There's also a deadline that makes this worth re-reading now: &lt;strong&gt;starting August 14, 2026, auto mode becomes the default permission mode for new sessions on Pro, Max, and Team plans.&lt;/strong&gt; One of plan mode's least-known behaviors — classifier-reviewed commands during planning — is tied to auto mode availability, so it's about to become the normal experience rather than the exception.&lt;/p&gt;

&lt;p&gt;Everything below is verified against the current official docs (&lt;code&gt;code.claude.com/docs/en/permission-modes&lt;/code&gt;, checked 2026-08-13).&lt;/p&gt;

&lt;h2&gt;
  
  
  What plan mode is
&lt;/h2&gt;

&lt;p&gt;Plan mode tells Claude to research and propose changes without making them. Claude reads files, runs shell commands to explore, and writes a plan — but does not edit your source. Edits stay blocked until you approve the plan (with one important exception covered in the gotchas section).&lt;/p&gt;

&lt;p&gt;It's the mode for "I want to understand what Claude intends before anything touches my files." The docs position it for exploring a codebase before changing it, and that's genuinely where it shines: big refactors, unfamiliar repos, changes where the &lt;em&gt;approach&lt;/em&gt; matters more than the diff.&lt;/p&gt;

&lt;p&gt;One naming collision to clear up early: plan mode (a &lt;strong&gt;permission mode&lt;/strong&gt;) is not the Plan agent (a &lt;strong&gt;subagent type&lt;/strong&gt;). The permission-rule syntax &lt;code&gt;Agent(Plan)&lt;/code&gt; targets the Plan subagent, which is a separate mechanism for delegated planning work. This article is about the mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways in, one way out
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shift+Tab&lt;/strong&gt; cycles &lt;code&gt;default&lt;/code&gt; → &lt;code&gt;acceptEdits&lt;/code&gt; → &lt;code&gt;plan&lt;/code&gt; in the CLI. The status bar shows &lt;code&gt;⏸ plan mode on&lt;/code&gt; when you're there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/plan&lt;/code&gt; as a prompt prefix&lt;/strong&gt; applies plan mode to a single prompt — useful when you want one planned answer without changing the session's mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;claude --permission-mode plan&lt;/code&gt;&lt;/strong&gt; starts the whole session in it. The same flag works with &lt;code&gt;-p&lt;/code&gt; for non-interactive runs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To make it stick across sessions, set it in a settings file — but see the defaults section below, because there's a VS Code exception.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Out:&lt;/strong&gt; press Shift+Tab again to leave plan mode &lt;em&gt;without&lt;/em&gt; approving a plan. That's the escape hatch people forget exists — you're not committed to producing a plan just because you entered the mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  What still runs while you plan
&lt;/h2&gt;

&lt;p&gt;This is where most descriptions of plan mode are out of date. There are three cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reads and the built-in read-only command set&lt;/strong&gt; run without prompting. That's the baseline everyone knows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When auto mode is available on your account&lt;/strong&gt; and the &lt;code&gt;useAutoModeDuringPlan&lt;/code&gt; setting is on — which it is by default — other shell commands during planning go to auto mode's classifier instead of prompting you. Approved commands run; rejected ones are blocked. In practice this means plan mode with auto available can run your test suite or a build while planning, without a single prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Without auto mode&lt;/strong&gt;, anything outside the read-only set prompts for approval, one command at a time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Version footnote, because this bit a lot of people: in v2.1.212 through v2.1.217, commands outside the read-only set prompted either way, even when auto mode was available. Current versions route them to the classifier again.&lt;/p&gt;

&lt;p&gt;If you use sandboxing: plan mode deliberately skips the sandbox's "auto-allow if sandboxed" substitution. Sandboxed-but-not-read-only commands still prompt (or go to the classifier) while you're planning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The approval prompt, decoded
&lt;/h2&gt;

&lt;p&gt;When the plan is ready, Claude presents it and asks how to proceed. The options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Yes, and use auto mode"&lt;/strong&gt; — approve and continue in auto mode. When auto mode isn't available, this option reads &lt;strong&gt;"Yes, auto-accept edits"&lt;/strong&gt; (→ &lt;code&gt;acceptEdits&lt;/code&gt;). Sessions started with bypass permissions enabled show &lt;strong&gt;"Yes, and bypass permissions"&lt;/strong&gt; instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Yes, manually approve edits"&lt;/strong&gt; — approve the plan, then review each edit individually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"No, keep planning"&lt;/strong&gt; — stay in plan mode and tell Claude what to change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The part that surprises people: &lt;strong&gt;approving a plan exits plan mode and switches the session's permission mode&lt;/strong&gt; to whatever the approve option describes. Plan mode is a staging area, not a place you stay. If you want to plan the next task too, you cycle back with Shift+Tab or prefix the next prompt with &lt;code&gt;/plan&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Three conveniences worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl+G&lt;/strong&gt; opens the proposed plan in your default text editor so you can edit it directly before Claude proceeds. Editing the plan is often faster than another round of "No, keep planning."&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;&lt;code&gt;showClearContextOnPlanAccept&lt;/code&gt;&lt;/strong&gt; setting adds a first option that approves the plan &lt;em&gt;and clears the planning context&lt;/em&gt; — handy when the exploration transcript is large and you want the implementation to start clean.&lt;/li&gt;
&lt;li&gt;Accepting a plan &lt;strong&gt;auto-names the session&lt;/strong&gt; from the plan content, unless you've already named it with &lt;code&gt;--name&lt;/code&gt; or &lt;code&gt;/rename&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Making plan mode the default
&lt;/h2&gt;

&lt;p&gt;For a project, set it in &lt;code&gt;.claude/settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"defaultMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"plan"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two interface-specific exceptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VS Code extension&lt;/strong&gt;: in sessions the extension starts, a settings-file &lt;code&gt;defaultMode&lt;/code&gt; doesn't set the starting mode. Set &lt;code&gt;claudeCode.initialPermissionMode&lt;/code&gt; to &lt;code&gt;plan&lt;/code&gt; in your VS Code user settings instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Desktop app&lt;/strong&gt;: a mode you pick in the mode selector is remembered per folder and takes precedence over &lt;code&gt;defaultMode&lt;/code&gt; — except Plan, which applies to the current session only. You can't "sticky" plan mode from the selector; use the settings file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The two gotchas that actually bite
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Bypass-available sessions don't enforce plan mode's blocks.&lt;/strong&gt; In sessions where bypass permissions are available (started with &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt; or equivalent), plan mode becomes an instruction rather than an enforcement: Claude is still told to plan without editing, but a file edit or shell command it attempts during planning runs without prompting. If your workflow is "bypass permissions in a container, plan first", know that the plan phase is advisory there — the walls are down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Protected paths behave differently per availability.&lt;/strong&gt; Writes to protected paths (&lt;code&gt;.git&lt;/code&gt;, &lt;code&gt;.claude&lt;/code&gt;, &lt;code&gt;.vscode&lt;/code&gt;, shell rc files, &lt;code&gt;.mcp.json&lt;/code&gt;, and friends) are never auto-approved in plan mode's normal operation — they prompt. But with auto mode available during planning they're &lt;em&gt;routed to the classifier&lt;/em&gt;, and in bypass-available planning sessions they're &lt;em&gt;allowed outright&lt;/em&gt;. Same mode name, three different behaviors depending on what else is enabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to reach for it
&lt;/h2&gt;

&lt;p&gt;My own rule of thumb after using it daily:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unfamiliar repo or cross-cutting change&lt;/strong&gt; → plan mode. The forced pause before edits is worth more than the keystrokes it costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research question, no edits expected&lt;/strong&gt; → plan mode via &lt;code&gt;/plan&lt;/code&gt; prefix, or an Explore subagent. Both keep your session from mutating anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Well-understood task in a repo you trust&lt;/strong&gt; → skip planning, go straight to &lt;code&gt;acceptEdits&lt;/code&gt; or auto mode. Plan mode's value is proportional to your uncertainty, and it's close to zero when you already know the diff you want.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And from August 14: if you're on Pro, Max, or Team and accept the new default, your sessions will start in auto mode — which means entering plan mode becomes the &lt;em&gt;deliberate&lt;/em&gt; act of slowing down, with classifier-reviewed exploration while you're there. Worth having the exact semantics loaded in your head before that switch flips.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I maintain &lt;a href="https://rulestack.gumroad.com?ref=devto" rel="noopener noreferrer"&gt;Rulestack&lt;/a&gt; — practical packs of Claude Code skills, hooks, and rules files, kept current against how these tools actually behave. Daily AI-coding-workflow notes on Bluesky: &lt;a href="https://bsky.app/profile/ai-shop.bsky.social" rel="noopener noreferrer"&gt;@ai-shop.bsky.social&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>devtools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Your CLAUDE.md loads into every subagent — the context multiplier nobody budgets for</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Wed, 12 Aug 2026 16:53:06 +0000</pubDate>
      <link>https://dev.to/rulestack/your-claudemd-loads-into-every-subagent-the-context-multiplier-nobody-budgets-for-440g</link>
      <guid>https://dev.to/rulestack/your-claudemd-loads-into-every-subagent-the-context-multiplier-nobody-budgets-for-440g</guid>
      <description>&lt;p&gt;You split your workflow into subagents to save context. Each worker gets a fresh window, does its job, returns a summary. Clean.&lt;/p&gt;

&lt;p&gt;Here's the part that doesn't show up in the mental model: &lt;strong&gt;every one of those workers re-loads your entire CLAUDE.md hierarchy at startup.&lt;/strong&gt; Ten subagents means your CLAUDE.md is paid for ten times — before any of them does a single unit of work.&lt;/p&gt;

&lt;p&gt;I measured subagent fixed overhead at roughly ~436k tokens per agent in &lt;a href="https://dev.to/rulestack/what-a-claude-code-subagent-actually-costs-measuring-the-436k-token-fixed-overhead-46g6"&gt;a previous experiment&lt;/a&gt;. A commenter (&lt;a class="mentioned-user" href="https://dev.to/skillselion"&gt;@skillselion&lt;/a&gt;) pointed out a variable I'd held constant without pricing it: CLAUDE.md rides along with every custom subagent, so its size gets multiplied by headcount. They're right, and the docs are explicit about it. This post is the follow-up: what exactly loads into a subagent, which agents are exempt, and how to shrink the multiplicand.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually loads into a subagent at startup
&lt;/h2&gt;

&lt;p&gt;Per the &lt;a href="https://code.claude.com/docs/en/sub-agents" rel="noopener noreferrer"&gt;official subagents doc&lt;/a&gt;, a non-fork subagent's initial context contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System prompt&lt;/strong&gt; — the agent's own prompt plus environment details. Notably &lt;em&gt;not&lt;/em&gt; the full Claude Code system prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task message&lt;/strong&gt; — the delegation prompt the main conversation writes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLAUDE.md files&lt;/strong&gt; — quoting the doc: "every level of the CLAUDE.md hierarchy the main conversation loads, including &lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;, project rules, &lt;code&gt;CLAUDE.local.md&lt;/code&gt;, and managed policy files."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git status&lt;/strong&gt; — a snapshot from the parent session's start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preloaded skills&lt;/strong&gt; — full content of anything in the agent's &lt;code&gt;skills&lt;/code&gt; frontmatter field.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sibling roster&lt;/strong&gt; — a small system reminder, only when relevant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The line that matters for budgeting: your user-level CLAUDE.md, your project CLAUDE.md, your &lt;code&gt;.claude/rules/&lt;/code&gt; files without &lt;code&gt;paths&lt;/code&gt; frontmatter, and your &lt;code&gt;CLAUDE.local.md&lt;/code&gt; all board every subagent you spawn.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two exemptions — and why you can't add more
&lt;/h2&gt;

&lt;p&gt;The built-in &lt;strong&gt;Explore&lt;/strong&gt; and &lt;strong&gt;Plan&lt;/strong&gt; agents skip CLAUDE.md and git status. The doc's phrasing: "Explore and Plan are the only subagents that omit CLAUDE.md and git status. There is no frontmatter field or per-agent setting to change which agents skip them."&lt;/p&gt;

&lt;p&gt;Two consequences:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;You can't opt a custom subagent out.&lt;/strong&gt; If your review pipeline spawns 12 custom agents, all 12 carry your full instruction hierarchy. There's no &lt;code&gt;skipMemory: true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-only research is cheap by design.&lt;/strong&gt; When the task is "find where X is defined," delegating to Explore genuinely avoids the multiplier. Reaching for a custom agent for pure lookup work forfeits that discount.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The arithmetic
&lt;/h2&gt;

&lt;p&gt;Say your CLAUDE.md hierarchy totals 6,000 tokens — a 400-line project file plus a user-level file plus a couple of unscoped rules. That's unremarkable; project instruction files grow monotonically because nobody ever deletes a rule.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single conversation: 6,000 tokens, paid once.&lt;/li&gt;
&lt;li&gt;A 10-agent fan-out (parallel review, migration sweep): &lt;strong&gt;60,000 tokens of CLAUDE.md&lt;/strong&gt; before any file is read.&lt;/li&gt;
&lt;li&gt;Run that pipeline 5 times a day: 300k tokens/day of pure instruction re-delivery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my earlier measurement, per-agent fixed overhead (~436k tokens) dwarfed the content I explicitly embedded (~46k). The CLAUDE.md hierarchy is part of that fixed slice — and unlike the harness portion, it's the part &lt;em&gt;you control&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure yours in 30 seconds
&lt;/h2&gt;

&lt;p&gt;Run &lt;code&gt;/context&lt;/code&gt; in a session. The &lt;strong&gt;Memory files&lt;/strong&gt; list shows every CLAUDE.md-family file that loaded and what it weighs. That number, times your typical concurrent agent count, is your real instruction overhead per fan-out.&lt;/p&gt;

&lt;p&gt;If the number surprises you, the doc's own size guidance is the fix-list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shrinking the multiplicand
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://code.claude.com/docs/en/memory" rel="noopener noreferrer"&gt;memory doc&lt;/a&gt; targets &lt;strong&gt;under 200 lines per CLAUDE.md file&lt;/strong&gt;, and warns that longer files both consume context and &lt;em&gt;reduce adherence&lt;/em&gt;. Three moves actually reduce the multiplied cost, one popular move doesn't:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Path-scoped rules load on demand.&lt;/strong&gt; Rules in &lt;code&gt;.claude/rules/&lt;/code&gt; with a &lt;code&gt;paths&lt;/code&gt; frontmatter field only enter context when Claude works with matching files. A rule about your Terraform layout doesn't need to ride into a subagent that's reviewing TypeScript. This is the highest-leverage move: it subtracts from &lt;em&gt;every&lt;/em&gt; agent's startup load.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Skills load when invoked.&lt;/strong&gt; A multi-step procedure ("how we cut a release") doesn't belong in CLAUDE.md at all — the memory doc says to move procedures to skills or path-scoped rules. A skill's cost is its description line until something actually invokes it. One caveat in reverse: a subagent's &lt;code&gt;skills&lt;/code&gt; frontmatter field injects the &lt;em&gt;full&lt;/em&gt; skill content at startup — preloading is the opposite of lazy-loading, use it only when the agent always needs that knowledge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Delete rules that no longer earn their tokens.&lt;/strong&gt; I wrote a &lt;a href="https://dev.to/rulestack/your-rules-file-only-grows-heres-how-to-find-the-rules-that-do-nothing-4n2m"&gt;separate method for finding dead rules&lt;/a&gt;. With the multiplier in view, a dead rule isn't one wasted line — it's one wasted line × every agent × every run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What doesn't help: &lt;code&gt;@path&lt;/code&gt; imports.&lt;/strong&gt; The doc is blunt: imports help organization "but doesn't reduce context, since imported files load at launch." Splitting a 600-line CLAUDE.md into six imported files ships the same 600 lines to every agent, just in prettier luggage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;/context&lt;/code&gt;, note total Memory files weight.&lt;/li&gt;
&lt;li&gt;Multiply by your typical fan-out size. That's the real number.&lt;/li&gt;
&lt;li&gt;Anything procedural → skill. Anything area-specific → path-scoped rule. Anything dead → deleted.&lt;/li&gt;
&lt;li&gt;Use Explore/Plan for pure research tasks; they're the only free riders.&lt;/li&gt;
&lt;li&gt;Don't confuse imports with savings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The subagent isn't expensive because it's an agent. It's expensive because it's a &lt;em&gt;full re-reader of everything you never trimmed&lt;/em&gt;, times however many of them you launch.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I maintain &lt;a href="https://rulestack.gumroad.com" rel="noopener noreferrer"&gt;Rulestack&lt;/a&gt; — practical packs of Claude Code skills, hooks, and rules files, kept current against how these tools actually load things. Daily AI-coding-workflow notes on Bluesky: &lt;a href="https://bsky.app/profile/ai-shop.bsky.social" rel="noopener noreferrer"&gt;@ai-shop.bsky.social&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>devtools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>AGENTS.md vs CLAUDE.md vs .cursorrules: what Cursor actually reads now (and in what order)</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Tue, 11 Aug 2026 10:45:02 +0000</pubDate>
      <link>https://dev.to/rulestack/agentsmd-vs-claudemd-vs-cursorrules-what-cursor-actually-reads-now-and-in-what-order-17bo</link>
      <guid>https://dev.to/rulestack/agentsmd-vs-claudemd-vs-cursorrules-what-cursor-actually-reads-now-and-in-what-order-17bo</guid>
      <description>&lt;p&gt;Cursor has accumulated four different places to put project rules, and most explanations you'll find online describe a version of the app that no longer exists. I re-checked Cursor's official docs today (2026-08-11) before writing this — every claim below has a quote or a directly verifiable statement behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four file kinds, in one table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;When it applies&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current, primary mechanism&lt;/td&gt;
&lt;td&gt;Depends on rule type (always / intelligent / glob / manual)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current, plain-markdown alternative — now with nested subdirectory support&lt;/td&gt;
&lt;td&gt;When working in that directory tree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read by Cursor for Claude Code compatibility&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Always&lt;/strong&gt;, every conversation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.cursorrules&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Legacy, will be deprecated&lt;/td&gt;
&lt;td&gt;Always (old behavior) — migrate off it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now the details, because the details are where the surprises are.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt; — the primary mechanism
&lt;/h2&gt;

&lt;p&gt;Project rules live in &lt;code&gt;.cursor/rules/&lt;/code&gt; as &lt;code&gt;.mdc&lt;/code&gt; files with frontmatter that controls activation. The two failure modes the official FAQ calls out are worth repeating, because they cover most "my rule never fires" reports:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check the rule type. For Apply Intelligently, ensure a description is defined. For Apply to Specific Files, ensure the file pattern matches referenced files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words: an "intelligent" rule with no &lt;code&gt;description&lt;/code&gt; has nothing for the agent to decide with, and a glob rule only enters context when a matching file does.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;code&gt;AGENTS.md&lt;/code&gt; — now nested
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; is the cross-tool plain-markdown format, and Cursor's docs recently added something genuinely useful — nested support:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nested AGENTS.md support in subdirectories is now available. You can place AGENTS.md files in any subdirectory of your project, and they will be automatically applied when working with files in that directory or its children.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the merge semantics:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Instructions from nested AGENTS.md files are combined with parent directories, with more specific instructions taking precedence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So &lt;code&gt;frontend/AGENTS.md&lt;/code&gt; wins over the root &lt;code&gt;AGENTS.md&lt;/code&gt; where they conflict, while both still apply. If you've been simulating per-directory rules with glob-scoped &lt;code&gt;.mdc&lt;/code&gt; files just to scope instructions, a nested &lt;code&gt;AGENTS.md&lt;/code&gt; is now the simpler way to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;code&gt;CLAUDE.md&lt;/code&gt; — Cursor reads it, and reads it unconditionally
&lt;/h2&gt;

&lt;p&gt;This is the one most people don't know. From the official help page:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cursor reads &lt;code&gt;CLAUDE.md&lt;/code&gt; files the same way it reads &lt;code&gt;AGENTS.md&lt;/code&gt;. Place a &lt;code&gt;CLAUDE.md&lt;/code&gt; file in your project root and Cursor picks it up automatically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the part that matters for anyone running both tools:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; files are always applied to every conversation, regardless of any &lt;code&gt;alwaysApply&lt;/code&gt; frontmatter setting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read that second quote carefully. It means &lt;strong&gt;Claude-specific instructions leak into your Cursor sessions&lt;/strong&gt;. If your &lt;code&gt;CLAUDE.md&lt;/code&gt; says "always run pnpm test before committing" because that's your Claude Code workflow, Cursor's agent will absorb that instruction too — always, with no way to scope it. The docs are explicit that conditional behavior is not available for this file: if you need conditional rules, that's what &lt;code&gt;.cursor/rules/&lt;/code&gt; is for.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;code&gt;.cursorrules&lt;/code&gt; — formally legacy
&lt;/h2&gt;

&lt;p&gt;No ambiguity left here:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;.cursorrules&lt;/code&gt; file in your project root is legacy and will be deprecated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The documented migration is four steps: create a new rule via the command palette ("New Cursor Rule"), copy your &lt;code&gt;.cursorrules&lt;/code&gt; content in, set the rule type to &lt;strong&gt;Always Apply&lt;/strong&gt; (that matches the old behavior), and delete the &lt;code&gt;.cursorrules&lt;/code&gt; file. Five minutes, and you stop depending on a file with a removal date.&lt;/p&gt;

&lt;h2&gt;
  
  
  Precedence: who wins when rules conflict
&lt;/h2&gt;

&lt;p&gt;For teams, the docs give an explicit order:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Precedence: Rules are applied in this order: Team Rules → Project Rules → User Rules. All applicable rules are merged; earlier sources take precedence when guidance conflicts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So organization-level Team Rules beat your repo's rules, which beat your personal User Rules. Everything is merged — precedence only matters where guidance actually conflicts. Within &lt;code&gt;AGENTS.md&lt;/code&gt; files, the separate rule applies: more specific (deeper) files win over parents.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd actually do with this
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Single-tool repo (Cursor only):&lt;/strong&gt; use &lt;code&gt;.cursor/rules/&lt;/code&gt; for anything conditional, plus one root &lt;code&gt;AGENTS.md&lt;/code&gt; for the always-true project facts. Skip &lt;code&gt;CLAUDE.md&lt;/code&gt; entirely — it buys you nothing and its unconditional loading is a blunt instrument.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cursor + Claude Code repo:&lt;/strong&gt; you have two workable shapes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;One file:&lt;/em&gt; a single root &lt;code&gt;CLAUDE.md&lt;/code&gt;. Claude Code reads it natively, Cursor picks it up automatically. Fine for small projects where both tools should behave identically.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Split:&lt;/em&gt; &lt;code&gt;AGENTS.md&lt;/code&gt; as the shared base (project facts, conventions), &lt;code&gt;CLAUDE.md&lt;/code&gt; for genuinely Claude-specific workflow — but remember the leak: Cursor will read the Claude file too. Keep anything that would misdirect Cursor's agent out of &lt;code&gt;CLAUDE.md&lt;/code&gt;, or accept that both agents follow it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Anything still on &lt;code&gt;.cursorrules&lt;/code&gt;:&lt;/strong&gt; migrate now, on the official four-step path, before deprecation makes it urgent.&lt;/p&gt;

&lt;p&gt;The meta-lesson from re-checking the docs: the answer to "what file does Cursor read" keeps changing under people's feet — &lt;code&gt;.cursorrules&lt;/code&gt; went from standard to legacy, and &lt;code&gt;CLAUDE.md&lt;/code&gt; support arrived without much fanfare. When behavior seems wrong, read the current docs before adding another rules file — the fix is usually that the mechanism changed under you, not that your rule text is bad.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I maintain &lt;a href="https://rulestack.gumroad.com/?ref=devto" rel="noopener noreferrer"&gt;Rulestack&lt;/a&gt; — tested rule packs for Cursor, Claude Code, and Codex, kept in sync with changes like the ones above.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I post daily notes on AI coding agent configuration on Bluesky: &lt;a href="https://bsky.app/profile/ai-shop.bsky.social" rel="noopener noreferrer"&gt;@ai-shop.bsky.social&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What a Claude Code subagent actually costs: measuring the ~436k-token fixed overhead</title>
      <dc:creator>Rulestack</dc:creator>
      <pubDate>Mon, 10 Aug 2026 15:55:22 +0000</pubDate>
      <link>https://dev.to/rulestack/what-a-claude-code-subagent-actually-costs-measuring-the-436k-token-fixed-overhead-46g6</link>
      <guid>https://dev.to/rulestack/what-a-claude-code-subagent-actually-costs-measuring-the-436k-token-fixed-overhead-46g6</guid>
      <description>&lt;p&gt;Spawning a subagent in Claude Code feels free. It isn't. We measured it across a real review pipeline, and the number that matters is one almost nobody talks about: &lt;strong&gt;each subagent costs roughly 436,000 tokens in fixed overhead before it does any useful work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This post explains where that number comes from, how to reproduce the measurement on your own setup, and what it changes about how you should split work between agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The experiment
&lt;/h2&gt;

&lt;p&gt;We run a weekly review pipeline over a catalog of digital products (Markdown-heavy repos: rules files, skills, templates). The pipeline embeds each product's full content into a reviewer prompt and asks for structured findings.&lt;/p&gt;

&lt;p&gt;We ran the same product, same full content, two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Arm A: three subagents&lt;/strong&gt;, one per review perspective (buyer value, niche accuracy, compliance). Total prompt size: ~314k characters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arm B: one subagent&lt;/strong&gt; covering all three perspectives in sequence. Total prompt size: ~105k characters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Billed token totals, from the session transcript:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Arm A (3 agents)&lt;/th&gt;
&lt;th&gt;Arm B (1 agent)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total tokens&lt;/td&gt;
&lt;td&gt;2,150,310&lt;/td&gt;
&lt;td&gt;809,070&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distinct defect classes found&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary-source fetches performed&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Arm B cost 37.6% of Arm A. The naive expectation — "three agents read the same content, so about 3x" — roughly holds, but the &lt;em&gt;reason&lt;/em&gt; is not the content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the tokens actually go
&lt;/h2&gt;

&lt;p&gt;Breaking the transcript down per turn, each agent carried about &lt;strong&gt;436k tokens of overhead that had nothing to do with the review itself&lt;/strong&gt;: the initial context load at spin-up plus the cache write on its final turn. The embedded product content — the thing we assumed dominated cost — was only about &lt;strong&gt;46k tokens&lt;/strong&gt; per agent.&lt;/p&gt;

&lt;p&gt;That's a 9.5:1 ratio of fixed cost to payload.&lt;/p&gt;

&lt;p&gt;Two consequences fall out immediately:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Embedding full content is cheap.&lt;/strong&gt; We had been truncating embedded files to save tokens, which quietly excluded the files that carried the product's actual value from review. Full-content embedding turned out to cost almost nothing relative to what we were already paying per agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headcount is expensive.&lt;/strong&gt; The cost lever is the number of agents, not the size of what you hand them. Three agents reading 46k each cost far more than one agent reading 138k.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to measure this yourself
&lt;/h2&gt;

&lt;p&gt;You don't need any special tooling. Claude Code writes full transcripts as JSONL under &lt;code&gt;~/.claude/projects/&amp;lt;project-dir&amp;gt;/&lt;/code&gt;, and each assistant message records its token usage.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run your multi-agent task once.&lt;/li&gt;
&lt;li&gt;Find the transcript files for the session (one per agent for spawned agents).&lt;/li&gt;
&lt;li&gt;Sum &lt;code&gt;usage&lt;/code&gt; fields per agent: input tokens, output tokens, cache creation, cache reads.&lt;/li&gt;
&lt;li&gt;Separate the first turn (spin-up) and last turn (final cache write) from the middle turns. The first and last are your fixed overhead; the middle is your actual work.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The exact overhead number will vary with your system prompt, MCP servers, and loaded skills — every always-on tool schema is part of the spin-up payload. Ours landed at ~436k. Yours may be smaller or much larger; the point is that it is &lt;em&gt;per agent&lt;/em&gt; and &lt;em&gt;independent of the task&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this changes in practice
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Merge reviewers whose perspectives overlap.&lt;/strong&gt; In Arm A, two of our three perspectives (buyer value and compliance) produced overlapping findings — 4 of 7 findings duplicated across them. We were paying the fixed cost twice to hear the same defect twice. We now run those as one agent with explicit perspective switching, and keep only genuinely orthogonal perspectives (spec verification against primary sources) separate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spawn for independence, not for tidiness.&lt;/strong&gt; A subagent is worth its 436k when you need something a single context can't give you: an opinion formed without seeing your reasoning, a parallel read of material you don't want polluting your main context, or true wall-clock parallelism. "This feels like a separate concern" is not, by itself, worth 436k tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't starve the agents you do spawn.&lt;/strong&gt; Since payload is the cheap part, hand each agent everything it needs — full files, full context, explicit instructions to fetch primary sources. The quality difference in our experiment came from exactly that: the one agent that fetched two official docs pages found the most serious defect (a fabricated quote presented as official documentation) that all three narrow agents missed.&lt;/p&gt;

&lt;p&gt;One honest caveat: this is n=1, one pipeline, one week, measured on our workload. The 436k figure is ours, not a constant of the platform. But the &lt;em&gt;structure&lt;/em&gt; of the result — fixed cost per agent dwarfing content cost — held on every agent we inspected, and it inverted how we design review fleets.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;We publish AI-coding field notes like this daily, and maintain &lt;a href="https://rulestack.gumroad.com?ref=devto" rel="noopener noreferrer"&gt;Rulestack&lt;/a&gt; — rules files, skills, and templates for Claude Code, Cursor, and Codex that hold up in real projects.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow us on Bluesky for the daily short-form version: &lt;a href="https://bsky.app/profile/ai-shop.bsky.social" rel="noopener noreferrer"&gt;@ai-shop.bsky.social&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Correction (2026-08-17)
&lt;/h2&gt;

&lt;p&gt;A reader pointed out something this post gets wrong by omission, and they are right.&lt;/p&gt;

&lt;p&gt;The ~436K figure sums the token fields from the transcript at face value. Those fields do not all bill at the same rate. Cache reads bill at 0.10x base input, and 5-minute cache writes at 1.25x — and spin-up overhead is overwhelmingly cached system prompt and tool schemas, which is exactly the part that gets read from cache. So the 9.5:1 fixed-to-payload ratio is a ratio of &lt;em&gt;tokens&lt;/em&gt;, and the corresponding ratio in &lt;em&gt;dollars&lt;/em&gt; is considerably smaller.&lt;/p&gt;

&lt;p&gt;I should have said that in the post. "Costs 436K tokens" reads as a cost claim, and I did not separate token accounting from dollar accounting anywhere.&lt;/p&gt;

&lt;p&gt;What the correction does not change: the fixed part still barely moves with how much work you hand the child, so headcount — not payload size — is the lever. Two reviewers whose findings overlap still pay the fixed cost twice to find the same defect twice. Spawn for independence, not for tidiness.&lt;/p&gt;

&lt;p&gt;The measurement I still owe you is the cache-read versus cache-creation split from the same transcript fields, which would turn the dollar ratio from an argument into a number. I have not run it yet.&lt;/p&gt;

&lt;p&gt;Primary sources for this correction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.claude.com/en/docs/build-with-claude/prompt-caching" rel="noopener noreferrer"&gt;https://docs.claude.com/en/docs/build-with-claude/prompt-caching&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>llm</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
