●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Three State-Passing Patterns for Claude Code Subagents — Lessons from 4 Months of Automated Blog Pipelines
JSON files, environment variables, and context bundles — three ways to hand state between Claude Code subagents, compared with four months of production data and clear guidance on when to pick each.
When you start running Claude Code subagents in earnest, almost everyone hits the same wall: how do you hand state from a parent agent to a child agent? The Agent tool is stateless. The child only sees the prompt you give it. If the parent's hard-won context never crosses that boundary, the child re-discovers everything from scratch.
I'm an indie developer — I've been shipping iPhone and Android apps since 2014 (cumulative installs are now north of 50 million), and over the past few years I've been running multiple Claude-powered Lab sites in parallel. After four months of operating an automated blog generation pipeline across four sites, the question of state passing came back to bite me more times than I'd like to admit. This article distills the three patterns I kept, plus when to choose each.
Why subagent state is awkward
Claude Code's subagent model resembles a Unix subshell more than a true fork. The parent doesn't share memory with the child; it composes a prompt string and spawns a fresh session. The child has its own context window and only returns a final message when it's done.
That isolation is wonderful for safety and context hygiene. The cost is a hard ceiling on how much the parent can hand down. In my pipeline, a single-site article generation run needs to pass:
Slugs from the last 14 days so the child doesn't duplicate a topic
Recent GSC data (clicks, impressions, CTR) per article
Per-category article counts to keep the mix balanced
Ongoing real-world threads — AdMob fill rate, Crashlytics top crashes, Xcode build issues — the child can pull from
Cross-site exclusion lists so four sites don't all suddenly publish on the same topic
If you inline all of that into the prompt, the child wakes up with most of its context window already eaten. I measured a stretch where 12,000 of the available 30,000 tokens were spent on state alone, leaving little room for the child to write a substantive premium article.
Pattern 1: JSON file handoff (best for ~100 runs/day)
The first pattern I landed on writes state to a JSON file. The parent persists it, the child reads it back with the Read tool.
Implementation
import { writeFileSync, readFileSync, renameSync } from 'fs';import { join } from 'path';type PipelineState = { pipelineId: string; timestamp: number; recentSlugs: string[]; gscData: Record<string, { clicks: number; impressions: number }>; excludedSlugs: string[]; categoryBalance: Record<string, number>;};const STATE_DIR = '/tmp/claude-pipeline-state';function writeState(state: PipelineState): string { const path = join(STATE_DIR, `${state.pipelineId}.json`); // Atomic write: stage to a tmp file, then rename const tmpPath = `${path}.tmp.${process.pid}`; writeFileSync(tmpPath, JSON.stringify(state, null, 2), 'utf-8'); renameSync(tmpPath, path); return path;}function readState(pipelineId: string): PipelineState { return JSON.parse(readFileSync(join(STATE_DIR, `${pipelineId}.json`), 'utf-8'));}// Hand the child only the path, not the payloadconst childPrompt = `Before you start, Read the state from this path to avoid duplicate topics: ${writeState(currentState)}Then choose a topic, generate the article, and write your result back to the same file.`;
What four months told me
The end-to-end overhead per handoff was 12ms in our Linux sandbox. Token consumption for the state itself dropped from roughly 12k tokens to 4k. At Sonnet 4.6 input prices, that's about $30 in savings per month per pipeline.
Two months in I hit a sharper edge. Cowork scheduled tasks for multiple sites overlapped at one timing, and concurrent writes to the same JSON file caused one update to silently lose to the other — three times in a single week.
The fix is the rename-based atomic write shown above. On the same filesystem, Linux's rename(2) is atomic, which is enough to dissolve the race.
When to choose this
This is what I use today for the four sites generating four articles per day each. For state below 100KB and parallelism up to about four, the JSON file pattern is the lowest-friction default and what I recommend trying first.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Three patterns compared with working code (overhead: 12ms / 0.3ms / 380ms) so you can pick by latency budget
✦Real numbers from running 4 sites and 630 generated articles per month — which pattern scales to which volume
✦Three production failures (32KB env var limit, JSON write race, context bundle token blowup) and the fixes that stuck
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Pattern 2: Environment variables (best for small, fast references)
The next pattern keeps only a short identifier in an environment variable; the actual state lives in a KV store. This combines well with Cloudflare Workers KV.
Implementation
# Parent: put the state in KV; pass only the ID via envPIPELINE_ID="run-$(date +%s)-${RANDOM}"wrangler kv key put --binding=PIPELINE_STATE \ "${PIPELINE_ID}" "$(cat current_state.json)" \ --remote# Child reads the ID from the env, then fetches from KVexport CLAUDE_PIPELINE_ID="${PIPELINE_ID}"claude-code -p "$(cat <<EOFRead CLAUDE_PIPELINE_ID from the environment and pull state from: https://internal.example.com/state/\${CLAUDE_PIPELINE_ID}EOF)"
The 32KB trap
My first instinct was the lazy version: serialize the entire state into an env var and skip file I/O altogether. Overhead clocked in at 0.3ms — practically free.
In production, however, the moment state hit roughly 32KB, child processes started crashing on launch. Linux's ARG_MAX puts a practical ceiling on the combined size of argv and the environment (around 128KB depending on the distribution); cross it and the child dies with E2BIG. If state might grow, keep only the ID in the env and fetch the body from a real store.
When to choose this
This shines for small, fast lookups — a user ID, a run ID, a cache-version counter. I use it to pass a GSC-data cache generation number so the child can quickly decide whether to refresh.
Pattern 3: Context Bundle (best for long-running pipelines over 1,000 runs)
The third pattern formats state as a readable Markdown "Context Bundle" and inlines it into every subagent prompt. It mirrors how Anthropic's own Skills system attaches reference documents wholesale.
Implementation
function buildContextBundle(state: PipelineState): string { return `# Pipeline Context (generated: ${new Date(state.timestamp).toISOString()})## Recent slugs (avoid duplicates)${state.recentSlugs.map(s => `- ${s}`).join('\n')}## Article count by category (rebalance target)${Object.entries(state.categoryBalance) .sort((a, b) => b[1] - a[1]) .map(([cat, count]) => `- ${cat}: ${count}`) .join('\n')}## Top GSC performers (last 10)${Object.entries(state.gscData) .sort((a, b) => b[1].clicks - a[1].clicks) .slice(0, 10) .map(([slug, data]) => `- /${slug}: ${data.clicks} clicks, CTR ${(data.clicks/data.impressions*100).toFixed(1)}%`) .join('\n')}## Exclusion list (cross-site)${state.excludedSlugs.slice(0, 50).map(s => `- ${s}`).join('\n')}${state.excludedSlugs.length > 50 ? `\n(${state.excludedSlugs.length - 50} more omitted)` : ''}`;}const childPrompt = `${buildContextBundle(state)}---Using the context above, pick a topic for a new premium article and draft it.`;
Expensive, but it buys reasoning quality
Measured overhead is 380ms, and token consumption runs about 3x higher than the JSON pattern. The reason to pay that cost is that the child doesn't need to perform any "go fetch the state" step. No Read call, no network I/O — from the very first second of inference, the child can focus on topic selection.
I reserve this for weekly retrospective articles. Those need to look across an entire week's worth of metadata at once. The runs are infrequent enough that I'd rather spend tokens than risk a child agent reasoning over partial information.
Watch for token blowup
One month I forgot to bound the bundle size for a monthly retrospective and a single subagent invocation burned 45,000 tokens. In production I now guard every bundle:
Numbers below are from real production runs, not synthetic benchmarks.
JSON file — Overhead 12ms / Best scale: up to 100 runs per day / Watch out for concurrent writers / Cost: parent-side I/O only
Env vars — Overhead 0.3ms / Best scale: tiny payloads (IDs and flags) / Watch out for the ~32KB ARG_MAX ceiling / Cost: essentially zero
Context Bundle — Overhead 380ms / Best scale: 1,000+ runs over long horizons / Watch out for token blowup / Cost: roughly 3x token usage
How I actually choose
When I design a new pipeline I walk through this decision in order:
If the state is under 1KB and it's basically "an ID plus a few flags," I reach straight for env vars
If the state is 1KB to 100KB and parallelism stays at four or below, I go with JSON files
If the state crosses 100KB, or the child needs to reason over the state from the first token, I use a Context Bundle
When pipelines grow I hybridize. The daily pipeline stays on JSON files; the monthly aggregation switches to Context Bundles. The boundary is the value of the child's first second of thought.
Three lessons that stuck
I'll close with three notes I wrote down in my own logbook.
The first lesson is to never commit to one pattern up front. Whether Cowork's scheduler will overlap your runs, and how quickly state will grow, are things you only learn from running the pipeline. A thin abstraction that lets you swap between the three patterns is cheap insurance against breaking changes later.
The second lesson is to always pair persistence with TTL. Early on I put JSON files in /tmp and forgot about expiration; whenever the sandbox VM restarted the state vanished and I generated duplicate articles before noticing. If the state needs to outlive a VM, use a Dropbox-synced directory or a KV store.
The third lesson is that the best subagent design is the one where the child never needs to read state at all. My main app business has long passed the point where automation is uncontroversial, but I still find that polishing what the child "thinks in the first second" is the highest-leverage place to spend time.
If you're designing something similar, I hope this saves you a few months. Thanks for reading.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.