AI Agents

Subagent Isolation: Why Agents Run in Separate Contexts

Why Claude Code subagents run in isolated contexts, what isolation means for tool access and memory.

Developers building their first multi-agent Claude Code system hit a consistent confusion: "Why does my subagent not know what the parent agent just did?" The answer is isolation - each Claude Code subagent runs in its own context, with its own message history, its own tool call budget, and no access to the parent session's accumulated state. This is by design, not a limitation, and understanding the boundaries of isolation is what separates developers who design multi-agent systems that work from those who spend hours debugging why an agent "forgot" something.

Subagent isolation
Subagent isolation is the architectural property of Claude Code where each spawned subagent receives a fresh context window with no inherited message history from the parent session - the subagent's knowledge is limited to what is explicitly included in its task description and its own tool call results, preventing unintended state leakage between agents while requiring deliberate context passing at handoff boundaries.

What isolation means - the direct answer

When Claude Code invokes a subagent, it starts a new Claude API session with only the subagent's own system prompt (from the .claude/agents/[name].md instruction body) and the task it was given. The subagent does not receive the parent's conversation history, does not know what tools the parent called, and cannot read the parent's in-progress work. Isolation boundaries apply to:

  • Message history - the parent's accumulated messages[] array is not passed to the subagent
  • Tool call state - tool results the parent received are not visible to the subagent
  • In-memory variables - any state held in the parent process's memory is unavailable to the subagent
  • Context window content - the parent's full conversation is not injected into the subagent's context

What is NOT isolated (shared across agents):

  • The filesystem - files written by the parent agent are readable by the subagent, and vice versa
  • Environment variables - the same process environment is inherited
  • External state - a database row written by the parent agent is immediately visible to a subagent that queries the same database
  • CLAUDE.md and project memory - the project's CLAUDE.md is loaded for every agent session; subagents read the same project-level instructions

Why isolation is the correct design

Isolation prevents three failure modes that emerge when agents share context naively:

  1. Context pollution - a parent agent's long intermediate reasoning fills the subagent's context with irrelevant content, causing the subagent to anchor on information that was tentative or already superseded.
  2. Uncontrolled scope expansion - a subagent with access to the parent's full history may attempt to complete parts of the parent's task, creating duplicate or conflicting actions.
  3. Debugging opacity - when agents share implicit state, a subagent's surprising behaviour may be caused by something the parent did, making the bug invisible without tracing the full shared context.

Isolation makes each subagent's behaviour attributable solely to the task it was given and the tools it called - which is the property that makes multi-agent systems debuggable.

Bridging context across isolation boundaries

Because isolation is intentional, bridging it must also be intentional. Three patterns for passing context across agent boundaries:

// Pattern 1 - Explicit context injection in the task description
// The parent agent summarises relevant state and includes it in the subagent's task.
// The subagent receives exactly what it needs, nothing more.

function buildSubagentTask(
  taskGoal: string,
  parentContext: {
    completedWork: string[];
    relevantFindings: string[];
    constraints: string[];
  }
): string {
  const contextBlock = [
    parentContext.completedWork.length > 0
      ? `Already completed:
${parentContext.completedWork.map(w => `- ${w}`).join('
')}`
      : '',
    parentContext.relevantFindings.length > 0
      ? `Relevant context:
${parentContext.relevantFindings.map(f => `- ${f}`).join('
')}`
      : '',
    parentContext.constraints.length > 0
      ? `Constraints:
${parentContext.constraints.map(c => `- ${c}`).join('
')}`
      : '',
  ].filter(Boolean).join('

');

  return contextBlock
    ? `${contextBlock}

Your task: ${taskGoal}`
    : taskGoal;
}

// Pattern 2 - File-based context sharing
// The parent writes a context file; the subagent's instruction body includes a step
// to read it before starting work. Uses the shared filesystem.

async function writeContextFile(taskId: string, context: object): Promise {
  const path = `.claude/context-${taskId}.json`;
  await writeFile(path, JSON.stringify(context, null, 2));
  return path;
}

// In the subagent definition (.claude/agents/my-agent.md), the instruction body includes:
// "1. Read .claude/context-{TASK_ID}.json to understand the current task state."
// "2. Complete your assigned task."
// "3. Write your output to .claude/output-{TASK_ID}.json."

// Pattern 3 - Shared database / external store
// Parent and subagent use the same Supabase/Redis instance.
// The parent writes a task record; the subagent reads it by task ID.
// This is the most robust pattern for complex pipelines.

async function createTaskRecord(db: Database, task: AgentTask): Promise {
  const { data } = await db.from('agent_tasks').insert({
    goal: task.goal,
    context: task.context,
    status: 'pending',
    created_at: new Date().toISOString(),
  }).select('id').single();

  return data!.id;
}

async function getTaskRecord(db: Database, taskId: string): Promise {
  const { data } = await db.from('agent_tasks').select('*').eq('id', taskId).single();
  return data as AgentTask;
}

Tool access and isolation

Subagent tool access is defined by the tools: list in the agent's frontmatter, not inherited from the parent. A parent with access to Write, Bash, WebSearch, and Read spawns a subagent that has only the tools listed in that subagent's definition file. This is the tool-scoping aspect of isolation - a read-only subagent cannot write files even if the parent can, preventing accidental side effects from workers that should only read.

What to include in the task description vs what to put in the agent definition

The agent definition body (the markdown instruction below the frontmatter) contains stable, task-independent instructions: the agent's role, its process, its output format, its constraints. The task description (what the orchestrator passes when invoking the subagent) contains task-specific state: the specific goal, the context from the parent, the constraints that vary per invocation. Mixing these - putting task-specific details in the definition body - means the agent definition must change with every new task, which defeats the purpose of having reusable agent definitions.

For how to write the agent definition body and frontmatter correctly, including which fields are available and how the description field controls routing, see the Claude Code subagents guide. For how to structure the orchestration layer that spawns and manages isolated subagents, see the agent supervisor pattern.