◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude Code
⟐ Claude Code/2026-05-25Advanced

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.

claude-code132subagent4pipelinestatearchitecture10

✦ Premium Article

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 payload
const 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.

or
Unlock all articles with Membership →
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.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⟐ Claude Code2026-08-28
A small script that catches invisible characters in plugin names before you install
Control characters and zero-width characters hiding in plugin or skill names are invisible in a terminal listing. I built six deliberately confusing names, compared what ls actually shows, and wrote a short check you can run before installing anything — plus an honest note on what that check misses.
⟐ Claude Code2026-08-26
I decide the prompt cache TTL by where I come back to, not how long I was away
Claude Code v2.1.242 added promptCacheTtl and subagentPromptCacheTtl. Whether the one-hour cache pays off depends on returning to the same directory and the same git state, not on how long the break was. Here is the order I check things in.
⟐ Claude Code2026-06-12
Untangling Android Back-Button Ad Gates: A Parallel, Priority-Ordered Redesign with Claude Code
Nested back-button ad gates fired at the wrong moments. The parallel, priority-ordered redesign we shipped in v2.1.0, with Claude Code, Kotlin, and tests.