Skip to content

How to onboard the inter-session protocol onto a new project

Goal: take a repository that has no parallel-session protocol and leave it able to run 2–3 coordinating Claude Code sessions safely.

Time: ~15–20 minutes. Prerequisites: the target repo is a git repo with an origin remote and a main branch; you can run bash and (optionally) install lefthook. You are copying from a project that already has the protocol (po-platform is the reference donor).

Throughout, replace <project> with your repo's directory basename (e.g. acme-app). If you are an AI agent doing this autonomously, use the CSF snapshot instead — it is the same procedure in agent-consumable form.

Two levels of adoption. The minimum viable install (Steps 1–6) gives you the working mechanics: worktree isolation, the scoreboard, queues, the ack chain, presence. The full install (Step 7) also copies the normative ADR and the layered doctrine so future sessions have the complete playbook. Do the minimum first; add the doctrine once the mechanics prove out.


Step 1 — copy the three scripts

Copy these from the donor repo into infrastructure/scripts/ (or wherever your repo keeps scripts — adjust paths consistently afterward):

mkdir -p infrastructure/scripts
cp <donor>/infrastructure/scripts/start-session.sh        infrastructure/scripts/
cp <donor>/infrastructure/scripts/session-heartbeat.sh    infrastructure/scripts/
cp <donor>/infrastructure/scripts/check-worktree-prefix.sh infrastructure/scripts/
chmod +x infrastructure/scripts/*.sh
  • check-worktree-prefix.sh is portable verbatim — it derives the -sX suffix generically and contains no project name.
  • start-session.sh and session-heartbeat.sh hardcode the project name and must be adapted in Step 2.

Step 2 — adapt the project name in the two scripts

Both scripts assume the worktree is named <project>-sX. Replace the donor's name with yours:

sed -i 's/po-platform/<project>/g' \
  infrastructure/scripts/start-session.sh \
  infrastructure/scripts/session-heartbeat.sh

That fixes the directory-name checks (<project>-s[A-Z]), the worktree path (../<project>-sX), and the prefix-stripping in the heartbeat. Verify nothing was missed:

grep -n 'po-platform' infrastructure/scripts/start-session.sh infrastructure/scripts/session-heartbeat.sh
# → should print nothing

Tip (optional): if you'd rather not hardcode at all, replace the literal in the case "$BASENAME" blocks with a dynamic base derived from the repo root. The hardcoded form is simpler and what the donor ships; either works.


Step 3 — define your name pool

Open infrastructure/scripts/start-session.sh and edit the NAME_POOL array to a theme that fits your project. The rules are firm (validated by the script): plain ASCII letters [A-Za-z] only, ≤ 12 chars, no spaces, no accents/diacritics, unique within the pool. The theme is yours.

# Examples of valid pools (pick one theme, ~10–15 names):
NAME_POOL=(Orion Lyra Vega Rigel Altair Sirius Polaris Antares Capella Deneb)   # constellations
# NAME_POOL=(Basil Thyme Sage Dill Mint Fennel Tarragon Chervil Lovage Sorrel)  # herbs
# NAME_POOL=(Ada Linus Grace Dennis Edsger Barbara Alan Donald Ken Margaret)    # computing pioneers

Provide at least as many names as the max sessions you expect (5 letters sAsE is the protocol ceiling, so ~5+ names is plenty; more gives variety).


Step 4 — create the claim scoreboard

Create docs/ai/sessions/active.md. The structure matters: start-session.sh inserts rows under a ## Active heading, and session-heartbeat.sh only bumps rows above the first <!-- sX self-closed --> history marker.

mkdir -p docs/ai/sessions/queue
cat > docs/ai/sessions/active.md <<'EOF'
---
audience: developers
category: reference
last_updated: 2026-01-01
related_docs:
  - ../parallel-sessions.md
---

# Active session claims

Live scoreboard of orchestrator sessions currently working on this repo. Read this
file at session start before claiming work. Append your row when you claim. Remove
it (or mark it paused) when you stop.

**Reap rule:** rows older than 6 hours with no commits on the named branch (or no
`[sX]` commits on main in that window) can be removed by any session. Log the reap
in the "Reaped" section below.

## Active

| Session · Name | Riff / Task | Branch / Commit prefix | Claimed (UTC) | Presence (last_seen · caps) | Scope |
|---|---|---|---|---|---|

## Reaped / closed (rolling 7 days)
EOF

The empty table (header + separator, no data rows) is correct — sessions add their own rows via start-session.sh.


Step 5 — set up the message queue directory

Queue files (to-<sX>.md) are created on demand, but seed the directory with a short README so it survives and is discoverable:

cat > docs/ai/sessions/queue/README.md <<'EOF'
# Cross-session message queue

One file per recipient: `to-sA.md`, `to-sB.md`, … (and `to-all.md` for broadcast).
Each message is a markdown block:

· from: · to: · type: · ref: · status:sent

→ status:seen · acked · resolved
The recipient advances the `→ status:` line in their own commit. See
`docs/developers/inter-session-comms/reference.md` for the full grammar.
EOF
--- ## Step 6 — wire the lefthook hooks If your repo doesn't already use [lefthook](https://lefthook.dev/), add it:
npm install --save-dev lefthook
Add (or merge) these two blocks into `lefthook.yml` at the repo root:
commit-msg:
  commands:
    worktree-prefix-match:
      run: bash infrastructure/scripts/check-worktree-prefix.sh "{1}"

post-commit:
  commands:
    session-heartbeat:
      run: bash infrastructure/scripts/session-heartbeat.sh
Then install the hooks into `.git/hooks/` (once per clone):
npx lefthook install
> **No Node toolchain?** The hooks are just bash. You can instead create > `.git/hooks/commit-msg` and `.git/hooks/post-commit` that call the two scripts > directly. lefthook is the convenient path, not a hard requirement. > > **Hooks fire in worktrees — but lefthook must be resolvable there.** `.git/hooks` > is shared across all worktrees, so installing once registers the shims > everywhere. **However**, the lefthook shim resolves the `lefthook` binary > relative to the worktree root (`git rev-parse --show-toplevel`), and worktrees > carry no `node_modules` — so by default the hooks *silently no-op* in every > session worktree (the exact bug recorded as Riff #228 on po-platform). > `start-session.sh` fixes this automatically by symlinking the main checkout's > lefthook into each worktree's (gitignored) `node_modules`. If you wire hooks by > hand instead of via lefthook, point them at an absolute script path so this > doesn't bite you. --- ## ✅ Checkpoint — verify the minimum viable install
bash infrastructure/scripts/start-session.sh
You should see a worktree created at `../-sA`, a `sA · ` claim row committed, and a `cd` path printed. Then:
cd ../<project>-sA
grep '^| sA' docs/ai/sessions/active.md          # your row, with a name from YOUR pool
git commit --allow-empty -m "[sB] test: wrong prefix"   # MUST be rejected by the hook
git commit --allow-empty -m "[sA] test: correct prefix" # MUST pass
grep '^| sA' docs/ai/sessions/active.md          # Presence timestamp bumped by post-commit hook
If the wrong-prefix commit is rejected and the presence timestamp moved, the mechanics are live. Clean up the test:
git reset --hard HEAD~1            # drop the empty test commit
cd .. && git worktree remove <project>-sA && git branch -D session-sA
# and remove the sA claim row from active.md on main if it was pushed
You now have the minimum viable protocol. Run the [tutorial](tutorial.md) end-to-end to feel it in motion. --- ## Step 7 — (full install) add the doctrine and the ADR The mechanics work without these, but sessions coordinate far better when the *playbook* is in the repo. Copy and adapt: 1. **This documentation package** — copy the whole `docs/developers/inter-session-comms/` directory. Then search-and-replace the po-platform-specific bits (the name pool, the worked-example paths) with yours. 2. **The operational doctrine** — copy `docs/ai/parallel-sessions.md`. Adapt: - the name pool (Layer 6 / §11), - the shared-doc ownership table (Layer 5 — list *your* hot files: changelog, backlog, ADRs, memory index), - the stateful-op list (Layer 6/§6 — *your* destructive ops: your auth system, your DB, your payment provider), - the `make dev`-style "only one session owns the dev stack" note if you have shared host ports. 3. **The normative ADR** — copy `docs/developers/adr/ADR-010-inter-session-comms.md` (renumber to your ADR sequence). Adapt the identity pool and any project-specific store names. This is optional but recommended — it records *why* the alternatives (RabbitMQ, A2A, Postgres bus) were rejected, which saves the next person from re-litigating them. --- ## Step 8 — point sessions at the protocol (`CLAUDE.md`) Add a short section to your project's `CLAUDE.md` (or equivalent agent-instruction file) so every session adopts the protocol at start:
## Parallel orchestrator sessions

When 2–3 Claude Code sessions run concurrently against this repo, follow
`docs/developers/inter-session-comms/` (start with `explanation.md`).

**At session start (mandatory if session > 30 min):**
`bash infrastructure/scripts/start-session.sh` — auto-claims an unused `sX`,
creates the per-session worktree at `../<project>-sX`, commits your claim row.
Then `cd` into the printed worktree and work there. Every commit/PR/Riff carries
`[sX]`. At end: remove your row (or mark `paused — resuming <ETA>`).
--- ## Common adaptations & gotchas - **Different default branch.** The scripts assume `origin/main`. If yours is `master` or `trunk`, update the `git worktree add … origin/main` and `git fetch origin main` lines in `start-session.sh`. - **Monorepo / nested package roots.** `start-session.sh` keys off the repo root basename. In a monorepo where the git root isn't the project you mean, set the worktree path and basename checks explicitly. - **No `python3`.** `session-heartbeat.sh` needs `python3` for the in-place timestamp edit; without it the hook no-ops cleanly and liveness falls back to `git log` (the protocol is designed for exactly this graceful degradation). - **`active.md` row regex.** Rows must start with `| sX ` (a space after the id) for `detect_unused` and the heartbeat to match. Keep the `| Session · Name |` leading-cell shape. - **Shared host ports** (`make dev`, dev servers). Only one session can bind the ports; designate a "primary" session that owns the dev stack and have the others hit the same running services. Document this in your doctrine copy. --- ## What "done" looks like - `start-session.sh` claims a worktree + a themed name + a scoreboard row. - A mislabelled `[sX]` commit is rejected from the wrong worktree. - Committing bumps the presence timestamp. - A session can send a `queue/to-.md` message and the recipient can advance the ack chain — all over `git pull` / `git push`, nothing else online. When all four hold, the protocol is onboarded. See [`reference.md`](reference.md) for ongoing lookup.