Skip to content

Multi-Agent Workflows

Overview

Multiple Claude (or other AI) agents can work in the same repository simultaneously without conflict — if the right isolation, coordination, and communication patterns are in place. This document describes the architecture used in this project.

When to use multi-agent approaches:

  • Independent tasks with clearly non-overlapping file scopes
  • Long-running research or validation that should run in parallel with active development
  • Specialized workers (e.g., one agent for Terraform, one for Ansible, one for docs)
  • Compute-bound tasks where sequential execution is unnecessarily slow

The Two Agent Models

Model 1 — Orchestrator + Subagents (Task tool)

Status: Production-ready. Used in this project today.

One orchestrator agent spawns specialized workers via the Task tool. Communication is strictly hierarchical — subagents report only to the orchestrator; they cannot talk to each other directly.

Orchestrator
  ├─ Task(Ansible worker)  → result ─┐
  ├─ Task(Terraform worker) → result ─┤→ Orchestrator synthesizes
  └─ Task(Docs worker)     → result ─┘

Key constraints:

  • Subagents run with an isolated context window (they do not see the main conversation)
  • No direct peer-to-peer communication between subagents
  • To pass data from subagent A to subagent B, the orchestrator receives A's result, then includes it in B's prompt
  • Subagents can be run foreground (blocking, default) or background (concurrent)
  • Background subagents pre-approve all tool permissions upfront; any tool call not pre-approved is auto-denied (no interactive prompts)

Model 2 — Agent Teams

Status: Experimental (Claude Code CLI only, not in Agent SDK).

Each "teammate" runs as an independent session. They share a task list and a mailbox system — teammates can message each other directly, not just the lead.

Lead (orchestrator)
  ├──msg──► Teammate A ──msg──► Teammate B
  └──msg──► Teammate B ──msg──► Lead

Agent Teams are the right model when agents need to negotiate with each other. Watch this feature — when it stabilizes, it replaces custom file-based coordination for complex workflows.


Isolation: Worktrees

Worktrees are the primary isolation mechanism for parallel agent work.

A git worktree gives each agent its own working directory and branch while sharing the repository's full history and remotes. Multiple agents can have different files checked out simultaneously with no checkout conflicts.

Mechanism Filesystem isolation Git isolation Complexity
isolation: "worktree" (Task tool) ✅ Automatic ✅ Own branch Zero — automatic
/start-issue (interactive sessions) ✅ Per session ✅ Own branch Low — EnterWorktree handles setup
Branches only (shared directory) ❌ Same dir ✅ Own branch Medium — collapses on concurrent writes
Separate repo clones ✅ Total ✅ Total High

Automatic Worktrees (Subagents)

Add isolation: "worktree" to any Task tool call for a code-writing subagent:

Task(
  subagent_type: "general-purpose",
  isolation: "worktree",
  prompt: "..."
)

Claude Code creates a worktree at .claude/worktrees/<auto-name> with a dedicated branch. On completion:

  • No changes made → worktree is automatically cleaned up
  • Changes committed → worktree path and branch are returned; user prompted to keep or remove

Issue-Driven Worktrees (Interactive Sessions)

For a human-coordinated parallel Claude Code window, always start from an issue:

/start-issue <number>

This runs EnterWorktree under the hood, which:

  1. Creates a worktree at .claude/worktrees/<branch> with branch worktree-<type>/<slug>
  2. Switches the session's CWD into the worktree
  3. Moves the issue to "In Progress" on the project board

When work is complete, run /ready (creates the PR) then /restart (tears down the worktree). The branch is merged through GitHub, not via a local --no-ff merge.


Communication Mechanisms

Within a Single Orchestrator Session

The orchestrator is the message bus. Pass data between subagents via the orchestrator:

Channel Direction Use case
Task prompt / result Orchestrator ↔ subagent Primary — all structured output
Files in repo Any agent → any agent Large results, structured data
/tmp/agent-*.json temp files Subagent → next Task prompt Pass intermediate results between sequential subagents

Between Independent Sessions

Channel Direction Latency Notes
Git commits on branches Sequential Pull-based Code handoffs between sessions
GitHub issues / comments Any → any Async Work claiming, status, verification
GitHub Project board Any → any Async Visual: who owns what
Shared state files Any → any File-based Atomic mv-based writes for coordination
MCP-connected database Any → any Query-based Persistent structured state (e.g., postgres-dumbo)

No native real-time bus

There is no native real-time message bus between independent Claude Code sessions. The most practical options are a shared file with atomic writes, or a lightweight database accessible via MCP.


Coordination Protocols

GitHub Issues as Work-Claiming

Each agent works on a specific issue. The issue serves as:

  • Work claim — prevents two agents from picking up the same task
  • Status signal — board column shows where the work is
  • Communication thread — comments contain verification steps and handoff notes

Workflow:

Issue created (backlog)
  → /start-issue <number>    ← agent claims work, creates branch, moves to In Progress
  → commits on feature branch
  → /ready                   ← agent creates PR, moves to Review, posts verification comment
  → user reviews + merges PR
  → issue closed (user action)

See GitHub Projects for board structure.

Scope Boundary Rule

The best conflict prevention is design: give each agent a clearly non-overlapping file scope. Agents working on different top-level directories are always safe.

Agent A scope Agent B scope Safe?
cloud/ansible/roles/ cloud/terraform/ ✅ Yes
cloud/ansible/roles/ homeassistant/ ✅ Yes
cloud/ansible/roles/ docs/operations/ ✅ Yes
cloud/ansible/scripts/run-bogart.sh cloud/ansible/scripts/run-pluto.sh ✅ Yes
cloud/ansible/scripts/lib/run-script-common.sh cloud/ansible/scripts/lib/run-script-common.sh ❌ No

Protected Shared Files

Certain files must never be modified by worktree subagents (they affect all sessions):

  • CLAUDE.md — project instructions (all sessions read this)
  • AGENTS.md — cross-agent project context (all AI agents read this)
  • CLAUDE.md.proposed — transitional file (destined to replace CLAUDE.md post-merge)
  • .claude/settings.json — hooks and MCP config
  • .mcp.json — MCP server configuration
  • memory/MEMORY.md — orchestrator-only cross-session state

Enforcement: Claude Code PreToolUse hooks block write/edit attempts to these files from worktrees. See scripts/hooks/protect-shared-files.sh.


Claude Code Hooks for Coordination

Hook events useful for multi-agent coordination:

Hook Fires when Coordination use
SubagentStop Subagent finishes Write results to shared log, notify orchestrator
Stop Orchestrator finishes Validate all expected results arrived (exit 2 to block)
PreToolUse (Bash) Before any shell command Enforce branch naming, check work isn't claimed
WorktreeCreate Worktree being created Custom VCS setup, register agent scope
WorktreeRemove Worktree being removed Custom cleanup, unregister scope

Hook types: command hooks (shell scripts), prompt hooks (yes/no via model), agent hooks (spawn a subagent for complex verification, 60s timeout).

Exit code semantics: 0 = allow, 2 = block/deny.


Practical Patterns for This Repo

Pattern A: Orchestrator-Spawned Parallelism

One Claude session coordinates multiple workers. Use when you have several independent tasks in the same session.

Orchestrator session:
  Task(isolation: "worktree", "Update all Ansible roles for X")
  Task(isolation: "worktree", "Update all Terraform modules for X")
  Task(isolation: "worktree", "Update docs for X")
  ← receive all results, synthesize, commit merge

Rules:

  • Each subagent gets isolation: "worktree" if it writes files
  • Define non-overlapping scopes in each subagent's prompt
  • Orchestrator reviews and merges results; never auto-merges without review

Pattern B: Independent Parallel Sessions (Human-Coordinated)

Multiple Claude Code windows, each on their own worktree/branch. User coordinates.

# Window 1: working on Ansible role changes
scripts/wt-new.sh feat/ansible-zerotier-hardening

# Window 2: working on Terraform changes
scripts/wt-new.sh feat/terraform-vpc-cleanup

# Both sessions work independently; merge via PRs when done

Rules:

  • Use /start-issue <number> at session start (claims work on board)
  • Use /ready when done (creates PR, moves to Review)
  • Merge PRs sequentially if they touch shared files

Pattern C: Agent-to-Agent via Files

For passing structured data between subagents through the orchestrator:

# Subagent A writes result
echo '{"key": "value"}' > /tmp/agent-result-a.json

# Orchestrator reads and passes to Subagent B
# (include file contents in next Task prompt)

For persistent cross-session coordination, write to a shared file in the repo or query postgres-dumbo via MCP.


Upcoming: GitHub Flow Skills

Two Claude Code skills are planned to automate the issue lifecycle:

/start-issue <number>

Invoke before writing any code on an issue:

  1. Fetches issue title + labels via GitHub API
  2. Derives a branch name (e.g., feat/61-op-inject-migration)
  3. Creates and checks out the branch
  4. Moves issue to "In Progress" on the project board
  5. Optionally posts a "Starting work" comment

/ready

Invoke after pushing commits and validating:

  1. Detects current branch + linked issue number
  2. Creates a GitHub PR (title from issue, body with summary + test plan)
  3. Moves issue to "Review" on the project board
  4. Posts a verification comment on the issue with confirmation steps

  • GitHub Projects — board structure and issue lifecycle
  • Git Hooks — existing enforcement hooks
  • Pre-Commit Hooks — commit-time validation
  • scripts/hooks/protect-shared-files.sh — shared file protection
  • .claude/settings.json — Claude Code hooks configuration

Last updated: 2026-02-28 Status: Reference — patterns in use; skills planned