Side project · May 2026

A 5-persona Claude Code dev squad.

DevSwarm is a FastMCP server that orchestrates five persistent Claude Code persona sessions (Architect, Researcher, Frontend, Backend, Reviewer-Deployer) to turn one prompt into one pull request. Fire-and-forget: kick it off from a single MCP call, close the laptop, wake up to a PR. No Docker, no daemon, no port; just files on disk and stdio MCP. Built end-to-end on a Fedora server in a single afternoon.

5 personas 6 MCP tools 4m 42s capstone build Opus 4.7 across all personas ~3 h of build time, one afternoon
The premise

One idea at 11pm. A pull request in the morning.

A single Claude Code session is already a capable engineer. But it works one thing at a time (read, plan, code, review) in the same context window, with the same priorities, on the same clock as the human typing at it. DevSwarm is a small experiment in splitting that work across persistent specialised agents that hand off via files on disk, run in their own sessions, and don't need anyone watching.

The end-state I wanted: you have an idea late at night, you say it once, and you wake up to a pull request you can read, accept or send back. No babysitting. No "the swarm got stuck three steps in." The orchestrator detaches from the caller; status lives in a JSON file on disk; you can close the terminal and come back the next morning.

Architecture

Pure files on disk, plus stdio MCP.

The MCP server is one Python file. Each persona is a real claude session pinned to a UUID and resumed via subprocess. The runner is a detached process (start_new_session=True) that walks five stages, serialising state to .swarm_state.json after each. Frontend and Backend run in parallel threads. No daemon, no port, no container.

Architect PLAN.md Researcher RESEARCH.md Frontend frontend/ Backend backend/ Reviewer- Deployer → PR
You (terminal, glasses, anywhere with Claude Code)
      ↓ mcp call: devswarm.start_project(idea, repo_url)
┌────────────────────────────────────┐
│  devswarm_mcp.py  (FastMCP, stdio) │   ← spawned on demand
└─────────────┬──────────────────────┘
              ↓ subprocess.Popen(start_new_session=True)
┌────────────────────────────────────┐
│  runner.py   (detached, PPID=1)    │   ← survives the MCP dying
└─────────────┬──────────────────────┘
              ↓ for each stage: claude --resume <uuid> -p ...
      ┌──────┴──────┬──────────┬──────────┬──────────────┐
      ↓             ↓          ↓          ↓              ↓
  Architect    Researcher   Frontend   Backend      Reviewer-
  → PLAN.md    → RESEARCH   → code     → code       Deployer
                  .md         in fe/     in be/     → REVIEW.md
                                                    → gh pr create
              ↓
       state lives in workspace/<id>/.swarm_state.json
       check anytime via get_project_status(id)
The five personas

Five sessions, five tool sets.

Each persona is a separate Claude Code session living in its own working directory with its own CLAUDE.md system prompt and settings.json tool allow-list. Tool isolation is hard-enforced at the settings layer, not just promised in the prompt.

Architect
Writes PLAN.md · read-only
Reads the user's idea, picks the stack, defines file layout, API contracts, db schema, task breakdown, acceptance criteria. No code. No bash.
Researcher
Writes RESEARCH.md · read + web
Looks up libraries, patterns, similar OSS projects, known gotchas, security pitfalls for whatever the Architect specified. Heavy on citations.
Frontend
Builds frontend/ · bash + edit
Implements the UI side of PLAN.md. Has bash and write access, but is hard-blocked from git push and any GitHub-write MCP.
Backend
Builds backend/ · bash + edit
Implements API, server, DB migrations. Returns NOT_REQUIRED on static projects so the pipeline skips it cleanly. Same git-push block as Frontend.
The capstone test

4 minutes 42 seconds, one prompt to one PR.

First end-to-end live test, no human edits: a single-page CV with dark-mode toggle, fade-in-on-scroll, and prefers-reduced-motion respect. Every stage succeeded on first attempt.

ArchitectPLAN.md: stack, file layout, API contracts, acceptance criteria28 s
ResearcherRESEARCH.md: libraries, patterns, gotchas, security1 m 31 s
Frontendindex.html: flash-free theme init, CSS variables, IntersectionObserver59 s
BackendNOT_REQUIRED · static page, no API9 s
ReviewerREVIEW.md: 14 acceptance checks · clone · commit · push · open PR1 m 44 s

Below: the exact index.html the swarm wrote, rendered live. Try the theme toggle in the top-right of the frame, scroll to see the fade-in animation fire on each section.

/projects/cv-onepager-artifact.html, verbatim from the swarm's workspace output. View the PR ↗

Stack & status

Honest about what's in the box.

Language
Python 3.14, hand-written FastMCP server in ~260 lines (devswarm_mcp.py) plus runner (runner.py) plus shared state helpers (swarm_state.py).
Agent model
claude-opus-4-7 across all five personas. Sessions persisted as JSONL under ~/.claude/projects/<encoded>/<uuid>.jsonl, resumed via claude --resume <uuid> -p ....
Tool isolation
Per-persona .claude/settings.json with explicit allow/deny patterns. Frontend/Backend hard-blocked from git push, gh pr, all mcp__github__* writes. Notion writes denied for every persona.
Concurrency
Python threading for the parallel Frontend + Backend stage. State writes use unique tmp filenames + a process-wide lock so the read-modify-write inside update_stage doesn't drop updates.
Deploy
Reviewer-Deployer uses gh CLI with the user's existing auth. PR URL is written to .pr_url in the workspace; the runner picks it up and surfaces it in get_project_status.
Source
Not on a public repo yet; kept local while the API stabilises. Likely to land at github.com/tomscholtes93-collab/devswarm once a couple more capstones land cleanly.