Your CLAUDE.md is a briefing, not a boundary

Many hands reaching in from every side toward one small sheet of paper in the center of a desk, several of them holding pens, in flat editorial shapes.
Illustration generated with AI (Flux 1.1 Pro).

Nobody told me to do this. I did not read it in a blog post or copy anyone’s setup. It occurred to me one evening and I wanted to see whether it would work.

I am one person with about ten projects, and I stopped working on them one at a time. On a Mac Studio I keep iTerm tabs open, one per repository, each running its own Claude Code session. Two or three do real work at once while I move between them.

Then I added a second layer, because if one is good, more must be better. In the Claude desktop app I made one Project per repository, each told it is the supervisor for a specific path on my Mac Studio, that a Claude Code session is the implementer down there, and that it reviews and advises but never writes. They have MCP access, so they argue with the actual code instead of with my summary of it. When I want an outside opinion rather than another pair of hands, I ask there and paste the useful parts back into the terminal. Clumsy, and it works. Copy and paste turns out to be a decent air gap between something that decides and something that acts.

On top sits one more repo, master-supervisor, whose only job is to watch the others and tell me where my attention should go. Every project keeps a status file with a one-line summary; a script walks the repos, reads those plus recent commits, and builds an index; three times a day another script turns that index into a ranked list with a couple of sentences of reasoning per project.

So it is not one agent with ten tools. It is ten agents, a couple of chats, some scheduled scripts and a human, all touching the same filesystem.

I was quite pleased with myself. Then it started eating itself and I spent two days untangling the mess instead of building anything. This is not a method I am recommending. It is a list of the holes I stepped in.

Three ways they collided

The first one cost me production time. A config file on a server that several projects share, a small JSON map of spending limits. It belongs to no repository, so it gets edited from whichever project I happen to be sitting in. Over about a day, three sessions from two different repos each read it, changed their part, and wrote the whole thing back.

Classic lost update, the thing every database course warns you about. One session was cleaning up something unrelated, saw a project sitting at a suspiciously huge limit, concluded it was leftover test junk, and removed it. It was not junk. It was a deliberate exception added the day before, so that project would stop being locked out of its own AI features. The lockout came back and a customer-facing feature sat dark until I noticed.

Nobody did anything unreasonable. Every single edit was defensible given what that session could see. The file had no owner and no lock, so the last writer won and the reason behind the earlier write was invisible to everyone.

The second one was worse, because no human was in it at all. A LaunchAgent ran a weekly dependency scan, and a small script took the findings and helpfully appended a TODO entry to each affected project’s own list so nothing would get lost. It had written into nine repositories that were not its own. Then you open one of those projects, and its session finds edits nobody in that session made, in a file that session owns. Merges that make no sense. A state that does not match the history.

Before: every actor writes anywhere Three Claude Code sessions and a cron job all write directly into a shared config file and into each other's repositories, with no owner and no lock. session · repo A iTerm tab 1 session · repo B iTerm tab 2 session · repo C iTerm tab 3 cron: vuln scanner reads no rules shared config file no owner, no lock the cron job writes TODOs into repos it does not own last writer wins · the reason for the earlier write is invisible
Before. Every actor could write everywhere, including into repositories it did not own. Each edit was locally correct.

The third was the same mistake wearing a friendlier face. I have a Claude Code skill for triaging my inbox, and when it hit a mail that concerned another project, its instructions told it to hand the task over by writing into that project’s docs/TODO.md. Reasonable behavior for a human assistant. Bad behavior in a system where that file has exactly one legitimate writer.

Why writing it down did not fix it

My first instinct was the obvious one: document the rule. Add a line to CLAUDE.md saying sessions must not write outside their own repository.

It took me a couple of rounds to accept why that does not work. A session that reads a file, reasons about it correctly and writes back an improved version has done nothing wrong from where it stands. It cannot see that another session, in another repo, set that value ninety minutes earlier for a reason living in a conversation it has no access to. Being careful inside a session does not solve a coordination problem between sessions. Neither does a rule the same session is free to interpret, and rules do get interpreted, particularly when following them is inconvenient.

Then there are the scheduled jobs, which never read CLAUDE.md at all. Any rule written in prose is invisible to half the actors in my system.

What actually fixed it

Untangling this took two days. Mostly not building, just working out what had happened, in which order, and which of the changes in front of me were real.

One writer per repository. A session may only write inside its own git repo. Reading anything anywhere stays allowed. Crossing a repo boundary with a write is blocked by a PreToolUse hook that exits non-zero before Write, Edit or a shell redirect can touch the file. Not a warning in a document, a mechanical stop. If I really need to work in another project, I go and open that project.

One owner per shared resource. The handful of files belonging to no repo now go through a wrapper I call mit-lock: mit-lock budgets -- <command>. It takes a lock, runs the command, and logs every attempt with project, PID, host and command line. A second hook refuses direct writes to those paths, so nobody has to remember to use the lock.

One channel for cross-project tasks. Nothing writes a task into another project anymore. It goes into a mailbox, append-only, one writer per file. The overseer reads that and turns entries into orders inside its own repository, and each project picks its own up from there. Two hops instead of one reach-across. Sounds like bureaucracy, and it is the thing that keeps the history readable.

After: one owner per thing Each session writes only its own repository. The shared file is reachable only through a lock with an audit log. Cross-project requests go through a mailbox that the overseer turns into orders. session · repo A session · repo B session · repo C own repo only own repo only own repo only R1 · hook blocks writes across repos lock + audit log one writer at a time shared config file mailbox (append-only) overseer writes orders target project picks them up R3 · no reaching into other repos R2 · shared resources only through the lock
After. Same actors, same work, but every mutable thing has exactly one owner and the two rules that matter are enforced by hooks, not by good intentions.

The delegation script that wrote into nine repos runs as a proposer into the mailbox now, not as a writer. The inbox skill’s handover instruction was rewritten the same way.

What I did not expect to value most is the audit log. When a shared file looks wrong, I can see which project touched it and when, in one place, instead of reconstructing it from three session transcripts.

The pieces, concretely

Vague posts about “agent orchestration” have helped me exactly zero times, so here is what actually runs. None of it is clever.

Ten repos, one Claude Code session each, in iTerm tabs. A CLAUDE.md per repo plus a global one, explaining the why, the conventions, the trigger phrases I use. Skills for the recurring jobs, inbox triage and invoicing and deploys, which inherit the same weakness as everything else made of text: a skill does what its instructions imply, including reaching into a repo it should not touch.

Then the parts that hold. Two PreToolUse hooks, maybe 150 lines of bash together. One resolves the git root of the session’s working directory and rejects any write outside it. The other refuses direct writes to the shared paths and points at the lock. Plus mit-lock itself, and a mailbox directory of plain markdown files.

No queue, no broker, no daemon. Markdown files and a rule about who is allowed to append to them.

If you copy one thing from this list, copy the hooks. They turned a rule I kept re-explaining into one that holds while I am not looking.

What I would take from this

If you run more than one agent at a time, the useful mental model is not “assistants”. It is concurrent writers. Every mutable thing needs an owner or a lock, which has been true of concurrent systems for fifty years. What changed is that the writers are now several sessions of me, all fast, all confident, each working from a partial view, every one of them producing changes that look perfectly reasonable on its own.

Enforcement has to sit below the agent. In a hook, in a lock, in something that returns a non-zero exit code. CLAUDE.md is where you explain the why and it is worth writing, but it is advice to an actor trying hard to be helpful right now, and it is invisible to every cron job in the system.

This is a week old. I rebuilt the coordination layer, restarted everything, and I still do not know whether it survives a normal working week. Nothing here is finished and none of it is a template. It is repair work on something I made up as I went along, written down because two days of my confusion might save someone else an afternoon of theirs.

The interesting question is no longer whether an agent can do the task. It is who owns the file when three of them decide to help at once. If you have a better answer than mine, I would like to hear it.

← All posts