A step-by-step setup for running several coding agents against one repository: git worktrees for isolation, a shared plan file for context, per-agent branches, a merge order decided before anything starts, and a way to see what the whole thing is spending. Real commands, and the mistakes each step is there to prevent.
the short version
To set up a multi-agent coding workspace, split the work along file boundaries you can name out loud, commit that split to the repository as a plan file, then give every agent its own git worktree and its own branch before it starts. After that the work is bookkeeping: an environment inside each worktree, handoffs written as committed files, an eye on the combined spend, and a merge in dependency order with the tests run between each merge.
Nothing below needs a framework or a product. It is git, a shell, and the agent CLIs you already have. The order matters more than the commands do, because almost every way this goes wrong is a step done after the agents started instead of before. If you want the concepts behind the procedure, read what AI agent orchestration means first.
1 to 2. decide the split, then write it down
1. Decide the decomposition before anything starts
Before a single agent launches, you have to be able to say what each one owns. The test is a sentence with no comma in it: "this agent touches only ___." A package, a directory, a layer, a route group and a named file set are all things that finish that sentence. "The refactor" and "the backend, plus a bit of the shared types" are not.
Some splits are clean by construction. A monorepo splits per package. A layered app splits into schema, server and client. A test-writing pass splits per module, because each test file is new and nothing else touches it. Ports of a component library split file by file, which is why that job is the one everybody tries first.
Some splits are not clean and no amount of prompting fixes them. A cross-cutting rename touches everything. A change to a shared type that four call sites depend on is one piece of work wearing four hats. Anything where the second agent needs to read what the first one wrote before it can start is a pipeline, not a fan-out, and pipelines run in sequence.
While you are here, write down the order the pieces will land in. If the API depends on the schema, schema merges first. You are deciding this now because deciding it at merge time means rebasing branches that have already diverged.
The failure this prevents: two agents assigned overlapping file sets. Agent A reads a file, thinks for a minute, and writes back a version that quietly discards agent B's edit. That is not a merge conflict, which git would tell you about. It is a lost update, and you find it later as a bug with no obvious author.
2. Write the plan into the repository, not into a prompt
Put the split in a file on the base branch and commit it before you create anything else. Plain markdown, short, one heading per agent.
cat > PLAN.md <<'EOF'
# Payments provider
## feat/schema (db/, migrations/)
Add customers, subscriptions, invoices. Write the migration.
Do not touch server/ or app/.
## feat/api (server/api/, server/lib/)
Provider client and webhook handler. Import types from db/types.ts.
Do not edit db/ or migrations/.
## feat/checkout (app/checkout/, app/components/checkout/)
Checkout form and status page. Call API routes by name, do not edit them.
## nobody edits
package-lock.json, *.generated.ts, migrations already applied
## merge order
schema -> api -> checkout
EOF
git add PLAN.md && git commit -m "plan: payments provider split"Every agent's first instruction can now be one line: read PLAN.md, do your section, stay inside your directories. That is a much smaller prompt than describing the same thing three times, and it stays correct when you change your mind, because you edit one file and tell the agents to reread it.
The failure this prevents: context that exists in exactly one session. No agent can see another agent's context window. If you explained the boundaries in agent A's prompt, agents B and C do not know about them, and neither do you in an hour when you have forgotten which half of the refactor you gave to whom.
3 to 5. one checkout, one branch, one scope each
3. Create one git worktree per agent
A worktree is a second checkout of the same repository, on its own branch, in its own directory. Unlike a clone it shares one object database, so several checkouts of a large repository cost a fraction of what several clones would, and everything an agent commits is already in the repository you are sitting in. No remote, no fetch, no pushing between copies.
cd ~/code/myapp
git worktree add ../wt-schema -b feat/schema
git worktree add ../wt-api -b feat/api
git worktree add ../wt-checkout -b feat/checkout
git worktree listThen start each agent inside its own worktree. The working directory is the isolation boundary, so an agent launched in the wrong directory has no isolation at all, whatever the branch says.
cd ../wt-schema && claude # terminal 1
cd ../wt-api && codex # terminal 2
cd ../wt-checkout && gemini # terminal 3Leave your original checkout on the base branch and run no agent in it. It is where you read diffs, run the full suite and merge. One directory that is always clean is what lets you tell whether the repository is actually broken or just mid-edit somewhere.
When a branch has landed, take the worktree down. The pair is remove then, if you deleted a directory by hand, prune.
git worktree remove ../wt-schema
git worktree prune # forget directories deleted outside git
git branch -d feat/schemaIf remove refuses, it is because that worktree has uncommitted changes. That refusal is the feature. Go and look at what is in there before you reach for a force flag.
The failure this prevents: three processes writing the same inodes with no lock between them. Also the slower version of the same problem, where one agent runs git checkout or git stash mid-session and the other two watch their files change underneath them for reasons they cannot see and will confidently misdiagnose.
4. Name each branch after the work, not after the agent
feat/schema, not claude-1. The branch outlives the session that created it. In a week the interesting question about a commit is what it was trying to do, not which vendor's CLI typed it, and a branch list full of model names tells a reviewer nothing they can act on.
It also lets you swap agents without renaming anything. If Codex stalls on the schema slice, you can kill it, start a different CLI in the same worktree on the same branch, and the plan file is still accurate. Branch names that encode the tool make that a rename operation for no reason.
The failure this prevents: a branch list nobody can read at merge time, and the small, real cost of git history that answers "which tool" when the question you will actually ask is "which change".
5. Write scope rules, and a list nobody may touch
Two lists per workspace. The first is per agent: the directories it may write to, already in PLAN.md. The second is global and matters more, because it is the one that causes conflicts in files nobody was assigned.
Say plainly what this is: convention. Git enforces none of it. There is no per-branch path permission, a CODEOWNERS file does nothing on a local branch, and a pre-commit hook is not installed in a fresh worktree unless you install it there. The enforcement is you, reading the file list before you read any code:
git diff --name-only main...feat/checkoutThat output is the cheapest check you can run. A checkout branch that touched migrations/ is a boundary violation, and you want to know that in one line rather than three hundred.
The failure this prevents: the conflict class that has no owner. Nobody was assigned the lockfile, so all three agents edited it, and now the merge that should have been clean needs an hour of manual reconstruction in a file no human wrote.
6 to 8. environment, handoffs, spend
6. Set up the environment inside every worktree
A worktree carries tracked files and nothing else. Gitignored things do not come with it, which catches everyone exactly once. Each new worktree needs its own environment file, its own installed dependencies, and its own port.
cd ../wt-api
cp ../myapp/.env.local . # gitignored, so it did not come across
npm install # node_modules is per worktree
# and a port that is not the one the other two are using
PORT=3001 npm run devAssign ports at the same time you assign directories, and write them in the plan file next to each slice. If something is already listening, find out what before you go hunting in the application:
lsof -i :3000If bootstrapping a worktree takes more than two commands in your stack, make it one script committed to the repository. You will run it every time you fan out, and an agent can run it too.
The failure this prevents: the twenty minutes you spend on two dev servers fighting over port 3000. It is a boring failure and it is the single most common one, because the symptom is a page that loads from the wrong branch rather than an error. An agent debugging code that is not the code being served will chase that for a long time.
7. Hand context between agents as committed files
When the schema agent finishes, the API agent knows nothing about what it built. There are two ways to fix that. Copying the useful part of one transcript into the other agent's prompt works, and it is what everyone does at first, and it is lossy, unversioned and unrepeatable. Do it once. Do not build a workflow on it.
The version that scales is to make the handoff an artefact. Ask the finishing agent to leave a note behind, commit it with the work, and let git carry it:
# in ../wt-schema, when the agent says it is done
git add -A
git commit -m "schema: customers, subscriptions, invoices"
# in ../wt-api, pick it up
cd ../wt-api
git merge feat/schemaNow the API agent reads db/types.ts and HANDOFF-schema.md directly. It is not being told about the schema, it is looking at it, which costs fewer tokens and cannot go stale. Ask for the note in four explicit headings rather than asking for a summary: what exists now, what the names are, what was deliberately left undone, and what the next agent must not change. An agent asked to summarise writes prose. An agent given those four headings writes something the next agent can use.
The failure this prevents: the second agent reinventing a decision the first one already made, under a different name. Two implementations of the same thing that both work is worse than one, because now the third agent has to pick.
8. Watch what the whole thing is spending
One agent gives you no running total. Three give you three times no running total, and the burn is genuinely additive: three agents working for an hour is roughly three agent-hours of tokens. This is the one variable a person watching three terminals cannot estimate, and it goes wrong exactly when nobody is looking, which is the situation parallelism is for.
After the fact you can read what Claude Code spent from its own transcripts, which are JSONL files on your disk:
npx claude-code-spend # spend by day, by model, by projectThat is an open-source CLI we wrote, and it reads local files only. If you are counting yourself, do not sum raw token counts against one rate. Cache reads are a small fraction of a fresh input token and they dominate a long coding session, so the naive sum is wrong by a multiple rather than by a rounding error. The arithmetic is in what Claude Code actually costs.
Two habits that keep the number down. Scope each prompt to its boundary, because an agent told to work in db/ reads far less of the repository than one told to "add payments", and reading the repository is where the tokens go. And do not leave a finished agent sitting in a loop: an agent that has completed its task but has not been told to stop is the most expensive process on your machine.
The failure this prevents: a bill, or a burned-through usage window, that arrives as a surprise. On a subscription the surprise is worse, because there is no invoice to watch: the first signal is a rate limit in the middle of the one task you needed finished.
9. rebase, then merge in dependency order
9. One at a time, tests between each
You now have three branches and a plan file that says which order they land in. Read the file lists first, all of them, before any code:
cd ~/code/myapp
git diff --stat main...feat/schema
git diff --stat main...feat/api
git diff --stat main...feat/checkoutThen take the first branch in the order, rebase it onto the base, and merge it. Rebase before merging, not after: once a merge commit exists, rebasing rewrites shared history and you have turned a tidy-up into a problem.
git -C ../wt-schema rebase main
git merge --no-ff feat/schema
npm testOnly when that passes do you go to the next one. The second branch rebases onto a base that now contains the first, which is the point of the order: the API branch gets the schema it depends on before anyone asks it to compile against it.
git -C ../wt-api rebase main
git merge --no-ff feat/api
npm test
git -C ../wt-checkout rebase main
git merge --no-ff feat/checkout
npm test--no-ff keeps each slice as one identifiable merge commit. When something breaks next week, "which piece of work introduced this" is a question you will actually ask, and a linear fast-forward history makes it harder to answer.
Boundaries leak anyway, and when they do, resolve the conflict yourself in the main checkout. Handing it back to one of the two agents that caused it is tempting and usually wrong: it has no context on the other branch, so it will resolve in its own favour, at length, and sound certain about it.
The failure this prevents: merging everything at once and finding out that the suite is red without knowing which of three branches did it. Merging one at a time turns an afternoon of bisecting into a single obvious culprit, every time.
when not to do this
All nine steps are overhead. They pay for themselves when the work genuinely splits and they are pure cost when it does not, so it is worth naming the cases where the right number of agents is one.
If you cannot finish the "this agent touches only ___" sentence for each slice, do not fan out. You are not going to discover the boundary once the agents are running, you are going to discover the collision. One agent working through the same list in sequence is slower on paper and faster in practice, because it carries context from one piece to the next for free.
If the task is small, one agent. The setup here costs a few minutes of your attention, and a few minutes is a large fraction of a small task. If the task is exploratory and you do not yet know what the change looks like, one agent, because a decomposition you invent before you understand the problem is a decomposition you will throw away.
The real ceiling is review. Agents parallelise. Reading diffs does not. Every agent you add produces another diff one person has to read carefully, another set of questions one person has to answer, and another moment where something stops and waits for you. Past a small number of concurrent agents the limiting resource is your attention, and the failure mode is not a crash: it is approving diffs by pattern-matching because there are four of them and you are tired. That produces worse code than one careful session would have, while feeling like more output.
So let the number of agents be set by how many diffs you can genuinely read, not by how many cores you have. One agent at a time, working well, is a better workspace than four you cannot review.
one implementation, and what it does not do
Skribbl is our attempt at folding this bookkeeping into the workspace. It is a macOS app, Apple Silicon, that puts real terminals on an infinite canvas, so every command in this post is still the command. Spawning a fleet of agents creates the worktree first, because the worktree path is the working directory the agents launch into and it cannot be created afterwards. Sessions are held in tmux and reattached rather than recreated, so closing the app does not kill a run. A line drawn from one agent to another is what grants the first authority to instruct the second, and cutting the line revokes it, which keeps the picture of who commands whom from drifting away from the reality.
What it does not do. It includes no model and no subscription, and it never holds a provider key: each agent talks to its own provider with your credentials. It does not decide your decomposition, which is step one and still yours. The spend meter can only price Claude today, because Claude is the only agent that reports token usage back, and Grok is registered but unconfirmed, so its node runs while its status light stays idle. Windows and Linux are not available.