TL;DR

I run 3-5 Claude Code sessions in parallel at staggered cadences. They coordinate through a shared #mat-claude-sessions Mattermost channel plus a small coordination board file. Each session announces what it’s about to touch, claims it, and announces when it’s done. Conflicts are rare; throughput is dramatically higher than running one session at a time and waiting.

Why parallel

A single Claude Code session running a long task — refactor across a few repos, work through a debugging session, draft a blog post — is mostly me waiting. The model is fast but tasks are bounded by my decisions, my reviews, and my edits. If I’m waiting on Session A to finish a build, Session B can be drafting something unrelated. Session C can be running a slow eval. The bottleneck stops being the model and becomes my own attention rotation.

The cost is coordination. Two sessions both deciding to edit the same file is annoying. Two sessions both running terraform apply against the same state is bad. I needed a coordination layer that didn’t require either session to be omniscient.

The setup

Three pieces:

  1. A shared Mattermost channel (#mat-claude-sessions) that every session can post to and read from.
  2. A coordination board file at ~/.claude/hooks/state/coordination.md that’s the canonical “who’s working on what” state.
  3. Hooks that auto-announce session start, claims, and completion.

The Mattermost channel is the human-readable feed. The coordination board is the machine-readable source of truth. Sessions read both.

Coordination protocol

Every session, on startup, reads the coordination board and announces itself in Mattermost:

[session-a] starting — repo: home_k3s_cluster — task: longhorn debug

Before touching a non-trivial resource (a repo, a file with high contention, a piece of infra), it claims:

[session-a] CLAIM home_k3s_cluster/kubernetes/apps/longhorn/* — eta 30min

The coordination board appends an entry. Other sessions reading the board see the claim and avoid the area, or queue work behind it.

When done:

[session-a] RELEASE home_k3s_cluster/kubernetes/apps/longhorn/* — done

The board entry gets marked complete. Mattermost gets a closing post.

If a session needs something another session is holding, it doesn’t fight — it asks, in Mattermost, with @me so I see it on my phone:

[session-b] @me session-a is holding home_k3s_cluster/kubernetes/apps/longhorn —
            need it for unrelated work in the same dir, OK to interrupt?

The default is to wait. Interruption requires me to weigh in.

The coordination board format

# Claude Session Coordination Board

Last updated: 2026-04-27T19:42:00-04:00

## Active claims

- session-a (started 19:15) — home_k3s_cluster/kubernetes/apps/longhorn/* — eta 19:45
- session-c (started 19:30) — zolty-blog/hugo/content/posts/* — eta 20:00

## Recent releases

- 2026-04-27 18:55 — session-b — openclaw-v2/workers/dream-worker
- 2026-04-27 18:30 — session-a — home_k3s_cluster/terraform/environments/homelab-prod

## Pinned notes

- Don't run `terraform apply` against homelab-prod before 2026-04-28 — pending change review

Plain markdown. No database, no API. Sessions edit it via Claude’s normal file tools. Race conditions are theoretically possible but in practice I’ve never hit one — claims are seconds-long writes against an ~200-line file.

Why Mattermost (and not Slack)

I used to coordinate through Slack. The free tier capped my message history at a level the bots blew through in days, and once messages started getting auto-deleted, the Mattermost-equivalent of “the audit trail” disappeared with them. I migrated all bot output to Mattermost — self-hosted, infinite history, same message API ergonomics, and integrated with my Authentik SSO.

The migration was also a forcing function for renaming slack_*-prefixed code to chat_* — naming should match destination, not the destination it had three platforms ago.

What this enables

  • Staggered cadence. Sessions tick at 2-3 min, 10 min, and 30 min intervals depending on how interactive they are. The fast one is what I’m actively working with; the slow ones are running long jobs or batched analysis.
  • Returning after a gap. When I come back from a meeting or a school pickup, the Mattermost channel is a complete log of what happened in my absence. No “let me scroll back” detective work.
  • Proactive context dumps. When I switch back to a session that’s been idle, it greets me with a recap of where we left off — pulled from the channel, not from its own ephemeral memory.

Anti-patterns I avoid

  • Don’t broadcast every tool call. Mattermost is a coordination channel, not a debug log. Sessions post starts, claims, releases, and questions. The play-by-play stays in my terminal.
  • Don’t let sessions argue. If session A and session B disagree about how to do something, they don’t debate. They both ping @me and I decide. The model is consistent within itself; cross-session “disagreement” is just compounding hallucinations.
  • Don’t merge contexts. Each session has its own working memory. There is no “shared brain”. The coordination board is task state only — not opinions, not decisions, not analysis.

What’s next

The coordination board would benefit from auto-expiring claims — a session that crashes leaves a lock that needs manual cleanup. A 60-minute TTL on claims with auto-renewal from a heartbeat would close that. The current manual cleanup happens once or twice a week and isn’t bad enough to prioritize, but it’s the obvious next iteration.