How to Isolate State Across Multiple Parallel AI Coding Agent Sessions
Problem
I was running two coding agents in parallel — one on a feature branch, one on a hotfix — and they kept stepping on each other. The hotfix agent “fixed” a file the feature agent had just edited. The feature agent’s context file got overwritten mid-task. Two sessions both replied to the same inbox event. I tried to fix it with file locks, then a shared SQLite, then a queue. Every shared thing became a new failure mode.
The clearest description of the same bug, from someone who had lived through it:
The UI was never the hard part for me, state was. Once I had my own harness I wanted a few sessions going at once, and keeping their context/auth/inbox from stepping on each other got messy fast. What fixed it: a separate state dir per session (own token, own inbox, own log) so nothing shares a poll consumer or a context file. — u/Evening_Classic_9207
Environment
- A custom harness (or a wrapper around Claude Code, Pi, OpenClaw)
- Two or more concurrent sessions — different branches, different clients, different cron jobs, or different providers
- Python 3.11+ for the dispatcher sketch
What happened?
When a single developer runs one session, “state” is just a file path. As soon as a second session starts, every part of that state becomes a contention hazard:
- Auth tokens shared between sessions mean revoking one revokes all.
- Context files shared between sessions mean the second session overwrites the first’s working memory mid-task.
- Inboxes / poll consumers shared between sessions mean two sessions race to reply to the same event.
- Working copies shared between sessions mean two agents try to edit the same file with conflicting diffs.
Multiple commenters in the thread independently described hitting each of these. The visual difference between a single-context subagent and a properly isolated runtime makes the cost obvious — the subagent pattern floods the main context with every intermediate result, while an isolated runtime keeps the main context small and only returns the final summary.

How to solve it?
The pattern that recurs in the thread is a per-session state directory plus a thin dispatcher. Four rules, applied together:
- One state directory per session. Layout, roughly:
Per-session state layout ~/.harness/sessions/<session_id>/context.json # working memorytoken # auth for this session onlyinbox/ # incoming eventsoutbox/ # outgoing eventslog/ # per-session logsworktree/ # optional, when the session owns a branch - One process per session. No two sessions share a poll consumer, an event loop, or a context file. The dispatcher is the only thing that talks to all of them.
- Filesystem as the boundary. When the dispatcher needs to send a message to a session, it writes to that session’s
inbox/. When the session needs to report, it writes to itsoutbox/. No shared queues, no shared databases for in-flight state. - Per-session worktree. When the session owns a branch, it works in its own
worktree/(or a shallow clone). The dispatcher merges or rebases later.
The five primitives a dispatcher usually grows into are agent, parallel, pipeline, phase, and log. agent runs a single subagent. parallel fans work out to several with a barrier. pipeline runs multi-stage work without a barrier. phase groups progress for the UI. log writes status messages.

Here is a sketch of the dispatcher. The point is the directory layout and the lack of shared paths — the exact API does not matter.
import os, subprocess, uuidfrom pathlib import Path
STATE_ROOT = Path.home() / ".harness" / "sessions"
def spawn_session(task: str, branch: str) -> str: sid = uuid.uuid4().hex[:8] sdir = STATE_ROOT / sid (sdir / "inbox").mkdir(parents=True) (sdir / "outbox").mkdir() (sdir / "log").mkdir() (sdir / "token").write_text(issue_token_for(sid)) (sdir / "context.json").write_text({"goal": task, "plan": []})
# Each session is its own process with its own cwd, env, and auth. subprocess.Popen( ["harness-agent", "--session", sid, "--branch", branch], cwd=sdir, env={"HARNESS_STATE": str(sdir)}, # token stays on disk, not env ) return sid
def send(sid: str, msg: str) -> None: (STATE_ROOT / sid / "inbox" / f"{uuid.uuid4().hex}.msg").write_text(msg)The reason
I think the key reason shared state breaks is that every shared path is a coupling point, and the more sessions you run, the more coupling points there are. One state dir per session makes the coupling explicit and pushes the only shared thing — the dispatcher — into a single, small, testable process.
A few related things that came out of the same thread:
- u/BP041 was running an 18-job OpenClaw stack from a custom dashboard — the same per-session layout, just at a larger scale.
- The Speedwave team took the same idea further for security: each service keeps its own creds in a separate worker, so even if a session breaks out of its sandbox there is nothing to take.
- Per-session isolation is what makes it safe to mix providers per session — cheap local models for some jobs, frontier models for others, with no risk of shared tokens.
Summary
In this post, I showed how to isolate state across multiple parallel AI coding agent sessions. The key point is to give every concurrent session its own state directory, its own process, and its own inbox, and to keep the dispatcher as the only thing that talks to all of them. No shared context files, no shared auth, no shared poll consumers.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments