# RELAY — Multi-Agent Kickoff & Coordination How to spin up parallel Claude Code sessions that coordinate over a shared MCP relay. One PM, two or more Devs, each in their own terminal, each on their own branch / worktree, exchanging structured messages. ## TL;DR — three commands ```bash # 1. Generate kickoff prompts (interactive — answers the design questions) # In any Claude Code session in this repo: /multi-agent-kickoff # 2. Start the relay + open the windows bash tools/relay/start.sh --kitty # or --tmux, or --manual # 3. In each new Claude window, paste the prompt below the `---` line # from the file the launcher prints (e.g. coordination/-pm-prompt.md) ``` That's the whole workflow. Everything below is the why and the troubleshooting. ## What this is The "PM/Senior-Dev paradigm" — one Claude session acts as project manager, two or more Claude sessions act as senior developers, each running its own subagents on a feature branch in its own worktree. They coordinate by sending each other typed messages (status / question / directive / free) through a tiny MCP server running locally. When to use it: - You have **2+ implementation plans** that share a release target and want to execute them in parallel under one coordinator. - You want each stream isolated (separate worktree, separate branch) so subagents can't accidentally commit to main or step on each other's files. - You want one human (the user) to be a relay-of-last-resort but not a router — the PM does the routing. When NOT to use it: one-off tasks, single-stream plans, anything where the overhead of "spin up four windows" exceeds the work itself. For those, just work in the foreground. ## The pieces ``` ┌──────────────────┐ HTTP/SSE ┌──────────────────┐ │ Relay (MCP) │◀───────────│ PM session │ │ tools/relay/ │ │ (Claude Code) │ │ port 7331 │ └──────────────────┘ │ │ ┌──────────────────┐ │ Per-role inbox │◀───────────│ Dev-A session │ │ in-memory │ │ (Claude Code) │ │ consume-once │ └──────────────────┘ └──────────────────┘ ┌──────────────────┐ ◀────────│ Dev-B session │ │ (Claude Code) │ └──────────────────┘ ┌──────── (optional) ───────┐ ◀────────│ Dev-C session │ └───────────────────┘ ``` - **Relay MCP server** — `tools/relay/server.ts`. HTTP/SSE on `localhost:7331`. Exposes three MCP tools: `post_message`, `read_messages`, `list_pending`. Per-connection MCP server instance prevents routing collisions across concurrent SSE clients. - **In-memory queue** — `tools/relay/queue.ts`. Per-role inbox (`pm`, `dev-a`, `dev-b`, `dev-c`). `read` is consume-once (FIFO drain). No TTL, no persistence, no cap — relay is dev-only ephemeral; restart the server to wipe state. - **Launcher** — `tools/relay/start.sh`. Three modes (manual / tmux / kitty) that all start the relay and either open the role windows or print the commands for you to open them by hand. - **Fallback shim** — `tools/relay/call.py` (Python) and `tools/relay/call.ts` (TS). Direct CLI access to the same MCP tools, for when the in-Claude MCP client isn't loading or you want to script a status check from a regular shell. Both are tracked in the repo and load-bearing for the multi-agent flow — do not delete. ## Invocation ### Step 1 — Generate the kickoff prompts In any Claude Code session inside this repo, run: ``` /multi-agent-kickoff ``` The skill walks you through a short Q&A (release target, branch names per dev, plan-file paths, coordination cadence) and writes four prompt files to `docs/superpowers/coordination/`: ``` -pm-prompt.md -dev-a-prompt.md -dev-b-prompt.md -dev-c-prompt.md (only if 3 devs) ``` Each prompt is self-contained: it tells the receiving session its role, its branch / worktree, the plan it owns, and the coordination protocol (block format, when to send status, who to escalate to). The launcher script discovers the latest `*-pm-prompt.md` / `*-dev-a-prompt.md` / etc. by `mtime`, so the most recently generated set wins automatically. ### Step 2 — Start the relay Pick a launcher mode that matches your terminal setup. **`--kitty` (recommended on kitty)** ```bash bash tools/relay/start.sh --kitty ``` Opens 4 (or 5 with dev-c) tabs in the current kitty window: one for the relay log, one per role. Each role tab launches `claude` in the repo root. Paste the corresponding prompt into each role tab to start the session. **`--tmux` (recommended on non-kitty)** ```bash bash tools/relay/start.sh --tmux ``` Creates a tmux session `relay-lift` with windows `relay`, `pm`, `dev-a`, `dev-b` (and `dev-c` if a fourth prompt is found). Attaches automatically. `Ctrl-b N` to navigate windows. Detach with `Ctrl-b d`. **`--manual` (for any terminal)** ```bash bash tools/relay/start.sh --manual ``` Starts the relay in the current terminal and prints `cat ` commands for each role. Open new terminals yourself and paste the printed commands; this is the most flexible mode for unusual setups (split panes, remote sessions, terminal multiplexers other than tmux). The launcher uses port **7331**. If it's already in use the script aborts with the kill command — `kill $(lsof -ti:7331)` clears it. ### Step 3 — Drive the coordination The PM session is the entry point. Talk to PM about goals; PM decides who's working on what and posts directives to the dev sessions via the relay. Each dev reads its inbox, executes, and posts back status / questions. The user (you) is mostly a watcher — PM should self-route. Common rhythm: - **PM at start:** posts a directive to each dev describing the first slice. - **Dev on completion:** posts status with branch / commit / what shipped. - **Dev when blocked:** posts a question; PM unblocks (decision) or escalates to user. - **PM end-of-cycle:** asks each dev for a status, summarizes, decides next slice. Message kinds (`MessageKind` in `queue.ts`): | Kind | Use when | |------|----------| | `status` | "I shipped X, branch is at Y, ready for next slice" | | `question` | "Should I do A or B? Blocking until I hear back" | | `directive` | PM-to-dev: "Next, do X. Constraints are Y. Acceptance is Z." | | `free` | Anything that doesn't fit the above (FYI, side-channel chatter) | Block format inside `body` is freeform markdown. The kickoff prompts include the project's preferred block templates. ## Fallback — when the MCP client misbehaves If a Claude session can't reach the relay's MCP tools (transient SSE hiccup, MCP server failed to register, sandboxed network), use the shim: ```bash # From any shell, with the relay running on 7331: python3 tools/relay/call.py read_messages '{"for":"pm"}' python3 tools/relay/call.py post_message '{"from":"dev-a","to":"pm","kind":"status","body":"shipped X"}' python3 tools/relay/call.py list_pending '{"for":"dev-b"}' ``` `call.ts` is the same surface in TypeScript (`bun run tools/relay/call.ts ...`) for when you want to script from a TS context. Both shims speak raw MCP over the SSE transport; output is the JSON-RPC response. The kickoff prompts reference `call.py` by path — if the in-Claude MCP client breaks mid-session, the dev can fall back to `Bash python3 tools/relay/call.py ...` and keep coordinating without restarting. ## Where things live ``` docs/superpowers/coordination/ ├── RELAY.md ← you are here ├── -pm-prompt.md generated by /multi-agent-kickoff ├── -dev-a-prompt.md ├── -dev-b-prompt.md ├── -dev-c-prompt.md (optional, 4-role mode) └── archive/ older kickoff sets tools/relay/ ├── start.sh launcher (manual / tmux / kitty) ├── server.ts MCP server (HTTP/SSE on :7331) ├── queue.ts in-memory per-role FIFO ├── queue.test.ts node:test — run with `bun test` ├── call.py Python MCP-client shim (fallback) ├── call.ts TypeScript MCP-client shim (fallback) ├── package.json └── tsconfig.json ``` The launcher's prompt-discovery is `ls -t "$COORD_DIR"/*--prompt.md | head -1` — newest wins. To switch back to a previous kickoff set, either delete the newer files or move them under `archive/`. ## Conventions - **Roles are fixed strings:** `pm`, `dev-a`, `dev-b`, `dev-c`. Adding a new role means editing `Role` in `queue.ts`, `KNOWN_ROLES`, the `enum` in `server.ts`'s tool schema, and the launcher. - **Worktree per dev:** each dev session works in its own git worktree on its own branch. Subagents must `cd` into the worktree first — the multi-agent-kickoff skill bakes this rule into the dev prompts (subagents have been known to commit to `main` if the worktree cwd is only set in a header). - **Branch naming follows the release train:** `feature/--`. PM owns the merge order; devs do not merge each other's branches. - **No squashing:** the project preserves git history as audit log (per `CLAUDE.md`). Devs commit small and often; PM coordinates rebases at integration time, not before. - **The user is not the router.** PM should issue directives directly to devs via the relay. The user steps in only for cross-stream design decisions or when PM explicitly escalates. ## Troubleshooting - **"port 7331 is already in use"** — another relay is running. `kill $(lsof -ti:7331)`, then re-run `start.sh`. - **Launcher can't find a prompt** (`(none found)` in the printed paths) — `/multi-agent-kickoff` hasn't been run yet, or all generated prompts are under `archive/`. Re-run the skill. - **Dev session committing to `main` instead of its worktree** — its subagent prompts are missing the force-`cd` header. Regenerate the dev prompt via `/multi-agent-kickoff` (the skill bakes in the cd rule) or hand-edit the prompt to start with `cd `. - **MCP tools don't show up in the Claude session** — restart the session. If it persists, fall back to `call.py`. If `call.py` also fails, check the relay log window for stack traces; the SSE transport sometimes wedges if a client disconnects ungracefully. - **`bun test` failing in `tools/relay/`** — relay tests use `node:test` via bun. Run from `tools/relay/`, not the repo root: `cd tools/relay && bun test`. Extension tests use vitest and live elsewhere; don't conflate. - **One dev session is silent** — check `python3 tools/relay/call.py list_pending '{"for":""}'` from any shell. If the dev's inbox has unread messages, they may have crashed or detached. Open the role's window and resume. ## Caveats - **In-memory queue is dev-only.** Restart the relay = lose all queued messages. There is no persistence by design — coordination is meant to flow forward, not be replayable. - **No auth.** The relay binds to `localhost:7331` with no token. Don't expose the port; don't run on a shared machine. - **The relay is not a chat history.** `read_messages` drains the inbox. If you need to refer back to what was said, copy-paste into a session note or the PR description; don't expect the relay to remember. - **Context costs scale with session count.** Four parallel Claude sessions burn four context windows. Use this paradigm when the parallel speedup justifies the cost — for sequential work, one session is cheaper. ## See also - `tools/relay/server.ts` — MCP tool definitions (`post_message`, `read_messages`, `list_pending`) and their schemas. - `tools/relay/queue.ts` — `Role` / `MessageKind` types; the canonical per-role-inbox semantics. - `docs/superpowers/coordination/-pm-prompt.md` — the latest PM kickoff (the actual operational instructions PM runs by). - The `multi-agent-kickoff` skill — generates the kickoff prompt set.