A linear pipeline of Claude Code sessions coordinated through
cross-session messaging:
an interactive orchestrator session (pilot) drives headless workers
(spec → dev → qa) that hand work to each other like runners in a relay race.
The core trick: a message arriving at an idle session starts a new turn.
The pilot spawns a worker, ends its turn, and sits idle; when the worker finishes
and sends [spec] DONE — ..., the message wakes the pilot, which dispatches the
next stage. No polling anywhere.
sequenceDiagram
actor U as you
participant P as pilot (orchestrator)
participant S as spec
participant D as dev
participant Q as qa
U->>P: task
P->>+S: spawn (claude -p)
Note over P: idle — the next message wakes it
S-->>-P: [spec] DONE — summary
P->>+D: spawn (claude -p)
D-->>-P: [dev] DONE — summary
P->>+Q: spawn (claude -p)
Q-->>-P: [qa] DONE — verdict
P->>U: pipeline summary
Solid arrows are process spawns; dashed arrows are cross-session messages. Each worker lives only for its own stage.
The real artifacts (spec, code, QA report) travel through files in work/;
cross-session messages carry text only (a summary plus file paths), which is all
the channel supports.
- macOS or Linux (cross-session messaging is not available on native Windows)
- Claude Code v2.1.224+ with an Anthropic login (not available through Bedrock/Vertex/Foundry)
- None of
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,DISABLE_TELEMETRY,DO_NOT_TRACK,DISABLE_GROWTHBOOKset to a value that disables feature-flag evaluation
Check everything at once:
bash scripts/check-setup.shgit clone https://github.com/mariohercules/session-relay.git
cd session-relayFirst, activate the project configuration (a deliberate manual step — the file grants permissions, so review it before copying):
cp .claude/settings.example.json .claude/settings.jsonThen:
bash scripts/start-pilot.shThis opens the orchestrator session named pilot, already primed with the
playbook in pilot/orchestrator.md. Give it a task, for example:
Task: a Python CLI (work/wordfreq.py) that reads a text file and prints
the 10 most frequent words with their counts.
The pilot spawns the spec worker and waits. Each worker runs as a background
claude -p process, does its part, and reports back. After qa finishes, the
pilot hands you the pipeline summary.
To follow along:
/list-agents(inside thepilotsession) — shows the workers currently alivetail -f logs/<stage>.log— raw output of each workerwork/pipeline-state.json— consolidated state maintained by thepilotCtrl+Oin thepilotsession — expands received messages
.claude/settings.example.json(activated via thecpabove) setscrossSessionInbound: "accept"at project scope: every session opened in this directory accepts messages from your other sessions without an approval dialog. That's what makes the unattended flow possible — switch it toholdif you want to approve message by message.- Workers get
--allowedTools "Read,Write,Edit,Glob,Grep,ListAgents,SendMessage". NoBash: a-psession can't answer permission prompts, so QA does a static review (it never executes the code). To let QA run tests, addBashto the list inscripts/spawn-worker.sh— knowing that means auto-approved shell in a headless process. - Each stage is a prompt template in
pilot/stages/. To change the pipeline (add adocsstage, swapqaforsecurity-review...), create a new template and adjust the sequence inpilot/orchestrator.md.
- Text only between sessions; context/history never travels (hence the files in
work/). - Queue of 50 messages per session, identical repeats are dropped — send milestones, not heartbeats.
- Sessions in different containers can't see each other (discovery works through files on disk).