SOPs
Multi-Session Orchestration — Sessions Talking to Sessions
What This Is
Section titled “What This Is”I run many Claude Code panels in parallel. Each one is its own conversation, its own context, its own tool history. Until recently the only way to know what was happening in another panel was to switch to it and read.
That is not true anymore. Every Claude Code session writes a complete JSONL transcript to disk in real time. Any other session can read that file. Combined with the inbox routing already in place, this gives one master session the ability to watch, query, and eventually clean up all the others.
This is the unlock. The page below is the mechanism plus where I think it goes.
The Mechanism
Section titled “The Mechanism”Where sessions live on disk
Section titled “Where sessions live on disk”Every session writes its transcript to:
~/.claude/projects/<encoded-cwd>/<session-uuid>.jsonlThe <encoded-cwd> is the working directory with slashes replaced by dashes. For my main workspace it is -Users-ojhurst-apps. So the transcript files for everything I do under ~/apps/ live in ~/.claude/projects/-Users-ojhurst-apps/.
Each .jsonl is append-only. One JSON object per line. Every user prompt, every tool call, every tool result, every assistant message. Hooks output is in there too. The whole session, end to end.
Mapping a UUID to a human-readable name
Section titled “Mapping a UUID to a human-readable name”The session UUIDs are not memorable. The session-namer skill (Haiku) writes a human-readable title into the transcript itself as a special line:
{"type":"ai-title","sessionId":"5743cd7b-...","aiTitle":"Add word count tracking to FreeFlow app"}So the lookup goes: title → grep across *.jsonl for "aiTitle":"<title>" → session UUID → full transcript.
Which sessions are live right now
Section titled “Which sessions are live right now”~/.claude/sessions/<pid>.json has one file per running Claude Code process, keyed by PID. Each file is small JSON with pid, sessionId, cwd, startedAt, version, entrypoint. Reading this directory tells me which sessions are actively running. Files for closed sessions are cleaned up; transcripts in projects/ are kept indefinitely.
So:
- All sessions ever →
~/.claude/projects/<workspace>/*.jsonl - Live sessions right now →
~/.claude/sessions/*.json - This session’s UUID →
$CLAUDE_SESSION_IDin the env, or the most recently modified*.jsonlfor this cwd.
How To Peek
Section titled “How To Peek”From inside a Claude session
Section titled “From inside a Claude session”The Read and Bash tools work on these files like any other. Concrete patterns:
Find a session by name:
grep -l '"aiTitle":"Review smarter review requests"' \ ~/.claude/projects/-Users-ojhurst-apps/*.jsonlSee what another session is currently working on (last user prompt and assistant response):
tail -50 ~/.claude/projects/-Users-ojhurst-apps/<uuid>.jsonl | \ python3 -c "import json, sysfor line in sys.stdin: try: d = json.loads(line) if d.get('type') in ('user', 'assistant'): content = d.get('message', {}).get('content', '') if isinstance(content, list): content = ' '.join(c.get('text','') for c in content if isinstance(c, dict)) print(d['type'].upper(), str(content)[:300]) except Exception: pass"List every live session with its title:
for f in ~/.claude/sessions/*.json; do sid=$(python3 -c "import json; print(json.load(open('$f'))['sessionId'])" 2>/dev/null) [ -z "$sid" ] && continue cwd=$(python3 -c "import json; print(json.load(open('$f'))['cwd'])" 2>/dev/null) enc=$(echo "$cwd" | sed 's|/|-|g') title=$(grep -h '"aiTitle"' ~/.claude/projects/${enc}/${sid}.jsonl 2>/dev/null | \ tail -1 | python3 -c "import json,sys; print(json.loads(sys.stdin.readline()).get('aiTitle','(untitled)'))" 2>/dev/null) echo "$sid $title"doneThat last block is the seed of a “what is everybody working on right now” sweep.
From the user side
Section titled “From the user side”When I want one session to look into another, I just say it: “Check the session called ‘Review smarter review requests’ — what is it working on?” and the session in front of me grep-finds the UUID, reads the transcript tail, and tells me. No special wiring. The unlock is realizing the data is already there to be read.
The Existing Inbox Half
Section titled “The Existing Inbox Half”Reading transcripts is one direction. The other direction is sending — one session pushes a message to another. That layer was built 2026-04-29 and lives under ~/apps/cc/:
cc/inbox/PRIMARY_SESSION_ID— single line, UUID of the current primary session.cc/inbox/inbox.jsonl— pending messages from secondaries to primary.cc/inbox/replies/<secondary-uuid>.jsonl— primary’s replies to a specific secondary.cc/bin/claim-primary-session.sh— claim caller as primary.cc/bin/report-to-primary-claude.sh— secondary → primary sender (--from NAME --type update|question|done|error MESSAGE).cc/bin/reply-to-secondary-claude.sh— primary → secondary (--to <uuid>or--to-recent).cc/hooks/inbox-inject.sh— UserPromptSubmit hook. Drains the primary inbox if this session is primary, drainsreplies/<this-session-id>.jsonlif a reply is waiting./be-primaryslash command — claims the current session as primary.
Known limit: hooks fire only on UserPromptSubmit, so a secondary does not see a reply until I prompt it. Zero-touch wake-up is not built yet.
The Two Halves Together
Section titled “The Two Halves Together”| Need | Tool |
|---|---|
| See what another session is doing | Read its .jsonl directly |
| Send a message to another session | report-to-primary-claude.sh / reply-to-secondary-claude.sh |
| Find a session by human name | grep '"aiTitle"' projects/*/*.jsonl |
| List live sessions | ls ~/.claude/sessions/*.json |
| Coordinate “everybody report in” | Master session loops over live sessions, reads each tail, summarizes |
Reading is passive — no impact on the other session. Sending is active — surfaces in the other session next time I prompt it. Most of the orchestration value is in reading. The sending half is for handoffs that need a response.
The Vision
Section titled “The Vision”What makes this an unlock and not just a trick: the master session does not need to be told what every other session is doing. It can find out.
A few patterns I want to build:
-
Stand-up sweep. One session, one prompt: “What is every live session working on right now?” → reads each tail → returns one paragraph per session. Replaces panel-switching as the way I keep my head around ten parallel threads.
-
Loose-ends close-out. “Sweep all my other sessions for anything unfinished.” Each transcript gets read for the last user request, the last assistant response, and any
commit-checkStop-hook blocks. The master session returns a punch list: Session X has an uncommitted edit to repo Y. Session Z asked you a question 40 minutes ago. Session W finished its task and is idle. I decide what to do; the master session can prompt any of them via the inbox to act on it. -
Cross-session learning. When one session figures out a tricky thing — a working API call, a config that finally took, a non-obvious workaround — the next session does not need to rediscover it from scratch. The transcript is already on disk. “How did I get the GHL invoice send working last Tuesday? Read that session’s transcript.” This is what
loose-ends.mddoes on a 24-hour cadence with Haiku; the in-session version is faster and more targeted. -
Session reaping. A daemon, or a /closeout extension, that identifies sessions which have been idle for hours with no loose ends and closes them. The chaos of nine open panels is real; auto-cleanup of finished work would compound.
-
One-prompt ops. “Send the same instruction to all four sessions working on FreeFlow.” The inbox can already do this; the listing-by-content piece is what makes the ergonomic version possible.
The deeper thing: the file system already holds a complete record of every conversation I have ever had with Claude Code. Any session, current or new, can read any of it. That is a memory layer that does not depend on the user retelling what happened. The first applications are coordination across panels. The bigger applications are about treating the JSONL archive as a queryable knowledge base in its own right.
Related
Section titled “Related”- Loose Ends Harvester — the nightly version of pattern (3) above, on the VPS.
- Working Over SSH — the routing layer, since some sessions run on Studio while I sit at the MacBook Pro.
~/apps/cc/memory/project_multi_session_routing.md— the inbox project memory.
Updating This Page
Section titled “Updating This Page”When a new pattern proves itself in real use, add it to “The Vision” with a one-line example. When a script gets built that automates one of the patterns, move it from “Vision” to “The Mechanism” with the path. The unlock keeps growing as long as I keep finding new things the JSONL archive lets me do.