My Claude Code Workflow for Running Multiple Projects
One worktree per task, one CLAUDE.md per repo, and a review step you never skip. The full multi-project setup, the real commands, and the seven failure modes that cost me the most time.
Table of contents
Run each project in its own git worktree, give each repo its own CLAUDE.md, and review every diff by name before it merges. That is the workflow. The command that makes it work is claude --worktree <name>, which creates an isolated checkout on its own branch so two sessions in the same repository never touch each other's files. Time blocks, session notes and skill files are scaffolding around that one idea.
This post is the map. Four other posts on this site go deep on individual pieces, and I link down to each one where it belongs.
The one-clone setup I started with
For the first few months I kept three clones on disk, three terminal tabs, one Claude Code session each. It worked until two sessions were in the same repository at once — one adding a field to the blog schema, one fixing a CSV export — and both edited lib/supabase.ts within a minute of each other. The second write silently clobbered the first. I did not notice until the build failed, which is the good outcome. The bad outcome is when it does not fail.
I also spent that period pairing Claude Code with Google Antigravity, Google's agentic development platform, which splits into an editor view and an agent manager and runs across editor, terminal and browser (Google Developers Blog). Antigravity is genuinely good at holding a task across a long asynchronous run. But it was solving a problem I had misdiagnosed. My problem was not "I lose the conversation." It was "two agents are editing the same file." That is a filesystem problem, and worktrees solve it at the filesystem.
Worktrees: one checkout per task, not one per project
A git worktree is a separate working directory with its own files and its own branch, sharing one repository history. Claude Code has this built in.
The four commands
# Start a session in an isolated worktree
claude --worktree feature-auth
# Second terminal, same repo, no collisions
claude --worktree fix-csv-export
# See what exists
git worktree list
# Remove one you are done with (--force if it has uncommitted work)
git worktree remove .claude/worktrees/feature-auth
By default the worktree lands at .claude/worktrees/<name>/ on a branch named worktree-<name> (Claude Code docs). Omit the name and it generates one like bright-running-fox. Add .claude/worktrees/ to your .gitignore first, or your main checkout fills with untracked files.
The env-file trap
A worktree is a fresh checkout, so gitignored files are not in it. .env.local is gitignored. Which means the first thing every new worktree did was fail to connect to Supabase, and I would sit there re-diagnosing a solved problem.
The fix is a .worktreeinclude file at the repo root, in gitignore syntax. Only files that match and are gitignored get copied:
.env
.env.local
Branch from your work, not from main
By default a new worktree branches from the remote default branch. If you are three unpushed commits into a feature and you spin up a worktree to fix something adjacent, those three commits are not there. Set this in .claude/settings.json:
{
"worktree": {
"baseRef": "head"
}
}
"head" branches from your current local HEAD. "fresh" (the default) branches from the remote. I use "fresh" on the marketing site, where most tasks are independent, and "head" on anything where I am mid-feature. This all came out of the sprint I wrote up in how I shipped four full-stack platforms in 25 days — the isolation problem is what made that pace survivable.
What actually goes wrong
Every row below cost me at least an hour before I understood it.
| Symptom | Cause | Fix | Covered in |
|---|---|---|---|
| Two sessions overwrite the same file | Both running in the main checkout | One --worktree per concurrent task | This post |
| Build fails in a new worktree, works in main | .env.local is gitignored, so it was not copied | .worktreeinclude listing your env files | This post |
| Worktree is missing your unpushed commits | Default base is the remote default branch | worktree.baseRef set to "head" | This post |
| Main checkout full of untracked files | .claude/worktrees/ not gitignored | Add it to .gitignore | This post |
| Agent applies Project B's conventions to Project A | One global config, no per-repo file | Per-repo CLAUDE.md plus project skill files | Skill files |
| Commit includes files you never read | git add . | Name every file in the prompt | Commit, push, deploy |
| A schema change reaches production untested | No approval step between agent and live table | Agent writes proposals to a queue | Approval queue |
Note the shape of the last three: they are not tooling failures. They are review failures. Isolation stops sessions colliding with each other. It does nothing to stop a session colliding with production.
Put the context in files, not in your head
Claude Code starts every session with an empty context window. Anything you want carried across sessions has to exist on disk.
One CLAUDE.md per repo, and one per surface
Claude Code loads CLAUDE.md from your working directory and every directory above it, concatenated root-first, so the file closest to where you launched is read last (memory docs). The docs suggest keeping each file under 200 lines, because long files consume context and reduce adherence.
That load order is why I run two files, not one. The repo root CLAUDE.md says what the repository is, which directory is actually live, and which directories are legacy. The one inside dashboard/ covers the app itself — the Supabase data layer, the admin auth wrapper, the theming override block. Working on the app loads both; working at the root loads only the first.
The rule I use for what goes in: if I have typed the same correction into chat twice, it belongs in the file.
Session notes
Before closing a project I write one paragraph — what I was doing, what the next concrete step is, what is still open. Three minutes. The next session reads it before touching code. This is the part of my old system that survived everything else, and it is the core of running three projects at once.
Skill files for the conventions
CLAUDE.md covers facts that apply every session. Procedures — how to write a Supabase seed file, how to run the deploy sequence — belong in skills, which load only when invoked. The per-project skill file is what stops the agent applying the news site's component conventions to the marketing site. Full format and the skills I actually run are in skill files explained.
The review discipline
Three rules, and I have broken all three at some point.
Name the files. Never git add .. The prompt says which files to stage. That is my decision, not the model's — the full pipeline is in commit, push, deploy.
Schema and config changes get a manual diff. Anything touching a database schema, an API contract or a hostname skips the fast path entirely. I learned this the expensive way: a canonical hostname bug cost me three months of indexing, and it would have been caught by two minutes of reading the diff.
Agents propose; humans approve. The autonomous agents on this site do not write to live tables. They write rows into a pending_actions table that I approve or reject from the admin panel. The reasoning is in why my AI agents get an approval queue, not write access.
Does running three at once actually pay?
Parallelism helps only when agent time and your attention are separable. Here is that arithmetic as a runnable script. Every number in it is an assumption I made up to show the shape of the model — not a measurement.
type Task = { name: string; agentMinutes: number; reviewMinutes: number };
// Assumed durations, not measured ones.
const tasks: Task[] = [
{ name: 'blog schema migration', agentMinutes: 12, reviewMinutes: 8 },
{ name: 'admin CSV export fix', agentMinutes: 6, reviewMinutes: 5 },
{ name: 'sitemap route', agentMinutes: 9, reviewMinutes: 6 },
];
// Assumption: 4 minutes to re-orient each time you move to another worktree.
const SWITCH_MINUTES = 4;
const sum = (ns: number[]) => ns.reduce((a, b) => a + b, 0);
const longestBuild = Math.max(...tasks.map(t => t.agentMinutes));
const totalReview = sum(tasks.map(t => t.reviewMinutes));
// Serial: you watch each build, then review it.
const serial = sum(tasks.map(t => t.agentMinutes + t.reviewMinutes));
// Parallel: builds overlap; review and switching do not.
const parallel = longestBuild + totalReview + SWITCH_MINUTES * (tasks.length - 1);
// Switch cost at which parallel stops paying.
const breakEven = (serial - longestBuild - totalReview) / (tasks.length - 1);
console.log({ serial, parallel, saved: serial - parallel, breakEven });
// { serial: 46, parallel: 39, saved: 7, breakEven: 7.5 }
Run it with npx tsx model.ts. Under these assumptions three parallel worktrees save seven minutes, and the whole advantage disappears once re-orientation costs more than 7.5 minutes per switch. The lever is not the number of sessions. It is how cheap switching is — which is exactly what session notes and per-repo config files buy you.
The honest version: parallelism does not multiply output. It stops the machine idling while you read.
What the research actually says
The number everyone quotes is that it takes 23 minutes and 15 seconds to recover from an interruption, usually attributed to Gloria Mark's 2008 work. I went looking for the primary source and could not find one. The figure does not appear in the CHI 2008 paper at all — it shows up in interviews with the author. One detailed search for its origin reached the same conclusion.
What the paper — The Cost of Interrupted Work: More Speed and Stress, Mark, Gudith and Klocke, CHI 2008 (PDF) — does report is close to the opposite of the folk version: interrupted participants completed their work in less time than uninterrupted ones, with no quality difference, but reported higher stress, frustration, time pressure and effort.
So I am not going to tell you context switching costs 23 minutes. I do not have a controlled study for the developer case, and I could not find one. What I have is my own arithmetic above, clearly labelled as assumption, and one observation: the cost I actually feel is not lost minutes, it is the stress the paper measured.
FAQ
Can I run multiple Claude Code sessions at the same time?
Yes. Run each in its own worktree — claude --worktree <name> in separate terminals. Without worktrees, two sessions in the same repository will overwrite each other's edits, and neither will tell you.
How do git worktrees work with Claude Code?
--worktree <name> creates a checkout at .claude/worktrees/<name>/ on a branch named worktree-<name>, and the session is confined to it — edits targeting the main checkout are blocked. On exit, a clean worktree is removed automatically; one with work in it prompts you to keep or delete.
Do I need a separate CLAUDE.md for every project? Yes, and sometimes more than one per project. Files load from your working directory upward and are concatenated, so a repo-root file plus one inside the app directory gives you general and specific context without duplicating either.
How many projects can one person actually run at once? Three is my ceiling, and the limit is review capacity, not tooling. Agent time overlaps; the time you spend reading diffs does not. Add a fourth and something ships unread.
Running several projects and losing track of what shipped? The fix is usually a per-repo config file and a review step, not a bigger tool. See my services or get in touch.
Get the AI Automation Playbook
The real architecture behind a 6-agent AI content team — what it saves, what it gets wrong, and the propose-then-approve pattern that makes it safe to trust.
Browse all free guides →Want to implement this with guidance?
Santosh helps founders turn insights like this into real systems.
External Resources