This guide is the version of a multi-agent setup that I actually run, not the version that demos well. The site you are reading is built by a fleet of Claude Code agents working off a GitHub issue queue, with a CEO agent doing the triage. It is boring in the way real engineering processes are boring, which is the point. This is how you wire it up.
For the framing of why this role exists at all, see the companion post: What an agentic developer actually does.
The mental model
Forget "swarms of autonomous agents collaborating." That phrase sells conference tickets and breaks production. The model that actually works looks like a small contracting agency:
- A CEO decides what should be worked on next and writes the briefs.
- Each role agent owns a specific surface (code, content, design, ops, QA, security, etc.) and only picks up tickets labelled for it.
- The queue is GitHub Issues. The artifacts are GitHub PRs.
- A human reviews the queue and the merged PRs on a cadence and steers the CEO when it drifts.
Two things make this tractable. First, every agent gets a written contract (a prompt file) that says what it owns, what it does not touch, and how it knows it is done. Second, every agent runs in its own git worktree, so parallel agents do not fight over the working tree.
Layout
The repo has a .claude/ directory that holds everything the agents need:
.claude/
agents/ # one .md file per role: ceo, dev, qa, design, content, ...
skills/ # user-invocable slash commands (e.g. /ceo to dispatch)
memory/ # shared Obsidian-style notes the agents read at startThat is the entire control plane. No bespoke orchestration service. No vector DB. Claude Code reads the .claude/agents/*.md files as subagent definitions and the .claude/skills/*/SKILL.md files as user-invocable commands. The rest is GitHub.
Step 1: Write the CEO contract
Create .claude/agents/ceo.md. The frontmatter tells Claude Code this is a subagent:
---
name: ceo
description: Strategy and triage agent. Reviews repo state, opens/refines
GitHub issues, and routes work to role agents. Does NOT write code or merge PRs.
---
# CEO
You are the orchestrator for a multi-agent team. Your job is to keep the
issue queue healthy and pointed at outcomes that move the project forward.
Other agents (`agent:dev`, `agent:content`, `agent:design`, `agent:qa`,
`agent:security`, ...) do the actual work.The body of the file is the prompt. Keep it concrete. The CEO needs to know:
- What inputs to read at start. Recent commits, open issues, open PRs, production health if you have a deployed site, plus your shared
CLAUDE.mdfor stack and conventions. - What outputs to produce. A new issue, a refinement comment on an existing issue, a "this is stale, closing" comment, or a no-op with a one-line rationale.
- The issue body template. Goal, Acceptance criteria, Constraints / links. One
agent:*label and an optionalpriority:high|med|low. Every issue gets exactly one role label; if work spans roles, split it. - Guardrails. Do not write code. Do not merge PRs. Never assign more than one
agent:*label per issue.
That last bullet matters more than it looks. The single-label rule is what lets the dispatch step be a clean fan-out. If an issue has two labels, two role agents pick it up, branch from the same commit, and you end up with two PRs racing each other.
Step 2: Write a role agent contract
The dev agent is the template the others follow. .claude/agents/dev.md:
---
name: dev
description: Builds features, fixes bugs, refactors, and improves perf.
Picks up `agent:dev` issues and self-merges after CI passes.
---
# Developer
You implement code for the project. Pick up issues labeled `agent:dev`,
branch, build, open a PR, and self-merge once CI passes.The prompt body covers:
- Scope. What this agent owns (features, bugs, perf) and explicitly what it does not (substantive MDX content, visual identity decisions, new dependencies without justification).
- Picking work. If invoked without an issue number, pick the highest-priority open issue labelled
agent:devthat has no existing branch or open PR. - The workflow. Branch as
agent/dev/<issue-number>-<slug>. Implement against the acceptance criteria. Runpnpm typecheck && pnpm lint && pnpm buildlocally. Open the PR, pollmcp__github__get_pull_request_status, and self-merge on green. - Conventions. Whatever your project uses. TypeScript strict, kebab-case filenames, no
console.login shipped code, no commented-out stubs, etc.
Repeat for each role you want. QA gets Lighthouse and broken-link audits. Content gets the MDX surface. Design owns the visual identity. The shape of every file is the same: frontmatter, scope, picking-work rule, workflow, guardrails, done criteria.
Step 3: The dispatch skill
The CEO contract above does not actually launch the role agents. That is the job of a Claude Code skill: a slash command the user (or a cron) invokes that runs the CEO and then fans out role agents in parallel.
Create .claude/skills/ceo/SKILL.md:
---
name: ceo
description: Run the CEO orchestration loop, then dispatch role agents in
parallel against the open agent:* issue queue.
---
# Dispatch queue
1. CEO subagent reviews strategy and opens/refines issues
2. Collect all open `agent:*` labeled issues
3. De-duplicate against in-flight work (open PRs, recent activity)
4. Dispatch one role subagent per remaining issue, in parallel, in worktrees
5. Summarize what was launched and what was skippedThe body walks through each step with the exact MCP calls. Two details matter more than they look.
De-duplication. Before dispatching, classify each open issue into three buckets: skip (active in-flight, healthy), re-dispatch (stuck PR with CI red for a day, or merge-conflicted), and fresh (no branch, no PR). Without this, every cron tick spawns a fresh agent on issues that are already being worked, and you get duplicate PRs.
Worktrees. Every dispatched agent gets its own git worktree. Two agents in the same working tree will trash each other's index. Claude Code supports isolation: "worktree" in subagent launches; use it.
Step 4: Wire it to cron
The skill is the on-demand version. The cron version is the same logic on a schedule. I run mine three times a day: morning, midday, evening. That cadence gives the CEO enough new state to react to (overnight failures, merged PRs, deploy health) without spamming the queue.
The cron job is a one-liner that invokes Claude Code in headless mode against the /ceo skill. If you do not want true cron, GitHub Actions on a schedule: trigger works the same.
Step 5: Memory
The fleet shares a small Obsidian-style vault under .claude/memory/. Two files matter:
MEMORY.md, the index. Agents read this at session start and skim entries whose hooks touch their role's surface.- Individual notes covering environment quirks, validated counter-intuitive choices, and postmortems a future agent would otherwise re-discover.
The bar for adding a note is high. Routine task output belongs in the PR or issue, not the vault. The point of the vault is to make the fleet collectively smarter over time without each agent re-learning the same lesson.
A concrete example: the first time the dev agent learned that a certain MCP tool 401s when called without an explicit auth header, that went into a note. The next dev agent reads the index, sees the entry, and skips the trap.
Failure modes to expect
A few things that will happen the first week you run this.
Speculative tickets. The CEO will, with great confidence, file issues for work that does not need doing. The fix is to tighten the CEO prompt: require it to surface unresolved decisions to the user rather than silently queue speculative work.
Duplicate PRs. If you skip the de-duplication step, you get them. If your agent:* labelling drifts and two labels land on one issue, you get them. Both are easy to fix once you see them.
Confident-but-wrong PRs. Agents will ship things that pass CI but do not actually solve the problem. There is no clever fix for this. Humans review the merged work on a cadence, file follow-up tickets for the misses, and the loop tightens over time.
Prompt drift. Six weeks in, an agent prompt that worked great will be subtly wrong because the project has evolved around it. Treat agent prompts like code. Read the diffs in PRs that touched the surface that role owns, and refactor the prompt when it stops matching reality.
What you get
A small fleet of role-specific agents pointed at a tagged issue queue, with a CEO doing triage and a human steering on a cadence, is the most boring and most productive multi-agent setup I have run. It is slow to set up (a day or two to write the contracts properly, another day to debug the dispatch logic) and fast to operate (a few minutes a day to review what shipped).
The thing it gives you that no single-agent workflow can is concurrency with role boundaries. The dev agent does not need to know how to write a blog post. The content agent does not need to know how to wire a build. Each one is a smaller, sharper prompt that fails in narrower ways. That is the whole trick.