<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ahsan Ali</title>
    <description>The latest articles on DEV Community by Ahsan Ali (@ahsanalidotme).</description>
    <link>https://dev.to/ahsanalidotme</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4160688%2F7fed2be1-207b-4a94-b95e-7444172feffa.jpg</url>
      <title>DEV Community: Ahsan Ali</title>
      <link>https://dev.to/ahsanalidotme</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ahsanalidotme"/>
    <language>en</language>
    <item>
      <title>Running a Multi-Repo Product with Parallel AI Agents (Claude Code)</title>
      <dc:creator>Ahsan Ali</dc:creator>
      <pubDate>Mon, 05 Oct 2026 08:00:00 +0000</pubDate>
      <link>https://dev.to/ahsanalidotme/running-a-multi-repo-product-with-parallel-ai-agents-claude-code-2ab4</link>
      <guid>https://dev.to/ahsanalidotme/running-a-multi-repo-product-with-parallel-ai-agents-claude-code-2ab4</guid>
      <description>&lt;h1&gt;
  
  
  Running a Multi-Repo Product with Parallel AI Agents (Claude Code)
&lt;/h1&gt;

&lt;p&gt;How I set up one workspace where a single Claude Code session hands work to one agent per repository, keeps them out of each other's code, and runs several tasks at the same time without them stepping on each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The setup:&lt;/strong&gt; 7 independent git repos (API backend, customer dashboard, admin portal, marketing site, iOS SDK, Android SDK, an internal Python tool) sitting side by side in one folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The idea in one picture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workspace/                      ← its own small git repo (agents, rules, contracts)
├── CLAUDE.md                   ← map of all repos + shared rules
├── contracts/                  ← the ONLY place cross-repo changes are agreed
├── .claude/
│   ├── agents/&amp;lt;repo&amp;gt;.md        ← one agent per repo + a read-only test auditor
│   ├── hooks/                  ← guards that enforce the rules
│   └── settings.json
├── clone-all.sh                ← one command to get every repo
├── backend/                    ← separate repo (ignored by the workspace repo)
├── client-dashboard/           ← separate repo
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You talk to &lt;strong&gt;one main session&lt;/strong&gt; started at the workspace root. It plans, starts one agent copy per task per repo (each in its own git worktree), and collects the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Step by step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Make the workspace root its own repo
&lt;/h3&gt;

&lt;p&gt;Don't use submodules. Keep each product repo as a normal clone, and make the root a tiny repo that tracks only workspace files. Use an &lt;strong&gt;allowlist&lt;/strong&gt; &lt;code&gt;.gitignore&lt;/code&gt; so a product repo can never be committed by accident:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*
!/.gitignore
!/README.md
!/CLAUDE.md
!/clone-all.sh
!/contracts/
!/.claude/
/.claude/settings.local.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a &lt;code&gt;clone-all.sh&lt;/code&gt; that clones every repo into its folder, so a new machine or teammate gets the same layout with one command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: A CLAUDE.md in every repo
&lt;/h3&gt;

&lt;p&gt;Keep each one short (under 300 lines): purpose, stack, key folders, entry points, run/test/build commands, conventions. Generate it by having an agent read &lt;code&gt;package.json&lt;/code&gt;, build files and the existing docs, then &lt;strong&gt;check the commands against the real scripts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;End each one with a pointer to the shared rules instead of copying them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Agent rules
Shared rules live in the workspace root ../CLAUDE.md.
In short: edit only this repo, put cross-repo needs in ../contracts/.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude Code automatically loads &lt;code&gt;CLAUDE.md&lt;/code&gt; files from parent folders, so the root rules apply inside every repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: The root CLAUDE.md
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;One line per repo: name, stack, role.&lt;/li&gt;
&lt;li&gt;How the repos connect (who calls which API, which auth).&lt;/li&gt;
&lt;li&gt;The core rule: &lt;strong&gt;route work to the matching agent; cross-repo changes go through /contracts.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A git workflow (branch from &lt;code&gt;develop&lt;/code&gt;, never skip hooks, one PR per repo).&lt;/li&gt;
&lt;li&gt;Parallel-work rules (see section 4).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: One agent type per repo, one copy per task
&lt;/h3&gt;

&lt;p&gt;A file in &lt;code&gt;.claude/agents/&lt;/code&gt; defines an agent &lt;strong&gt;type&lt;/strong&gt;, not a single worker. The main session can start many copies of the &lt;code&gt;backend&lt;/code&gt; agent at once, one per task. Each copy works in &lt;strong&gt;its own git worktree&lt;/strong&gt;, never in your main checkout.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.claude/agents/backend.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;any&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;work&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/backend:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Node/Express&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;API,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;database,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;webhooks,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;endpoints&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;used&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;by&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;dashboards&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SDKs."&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Read, Edit, Write, Grep, Glob, Bash&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="s"&gt;You own the backend repo. Many copies of you can run at once, one per task,&lt;/span&gt;
&lt;span class="s"&gt;so you never work in the main checkout.&lt;/span&gt;

&lt;span class="s"&gt;1. Read backend/CLAUDE.md first, and follow it.&lt;/span&gt;
&lt;span class="s"&gt;2. Work in your own worktree, one per task&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
   &lt;span class="s"&gt;git -C backend worktree add ../_wt/backend-&amp;lt;task&amp;gt; -b &amp;lt;type&amp;gt;/&amp;lt;task&amp;gt; origin/develop&lt;/span&gt;
   &lt;span class="s"&gt;Install dependencies there for real; never symlink node_modules.&lt;/span&gt;
&lt;span class="s"&gt;3. Edit only inside _wt/backend-&amp;lt;task&amp;gt;/. Never edit backend/ itself (it's the user's&lt;/span&gt;
   &lt;span class="s"&gt;checkout) and never edit another repo.&lt;/span&gt;
&lt;span class="na"&gt;4. Cross-repo needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write contracts/&amp;lt;task&amp;gt;.md (owner, consumers, status), then stop.&lt;/span&gt;
&lt;span class="s"&gt;5. Write tests for what you changed, including edge cases, and run them. Then reply&lt;/span&gt;
   &lt;span class="s"&gt;with your worktree path so the main session can run test-auditor. Fix any gaps first.&lt;/span&gt;
&lt;span class="s"&gt;6. Commit, push (hooks on, never --no-verify), open one PR to develop.&lt;/span&gt;
&lt;span class="na"&gt;7. Append a 2-line summary to backend/.agent-notes.md. Final reply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worktree, branch,&lt;/span&gt;
   &lt;span class="s"&gt;PR link, tests run, open contract items.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The description is what routing uses&lt;/strong&gt;, so make it specific ("Use for any work in /backend: …").&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: A read-only test auditor that judges quality, not just presence
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-auditor&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use after a code change, before pushing, to audit one task's worktree. Read-only.&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Read, Grep, Glob, Bash&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="s"&gt;1. List what changed in the worktree vs origin/develop. Read the task.&lt;/span&gt;
&lt;span class="na"&gt;2. Coverage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;every new or changed behaviour has a test; no untested branch,&lt;/span&gt;
   &lt;span class="s"&gt;error path or validation rule.&lt;/span&gt;
&lt;span class="na"&gt;3. Missing edge cases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;name the specific ones that should exist but don't&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
   &lt;span class="s"&gt;empty/null, boundaries (0, 1, max), invalid input, auth/permission failures,&lt;/span&gt;
   &lt;span class="s"&gt;cross-tenant access, duplicates/idempotency, retries/timeouts, dates/time zones,&lt;/span&gt;
   &lt;span class="s"&gt;pagination, UI loading/empty/error states.&lt;/span&gt;
&lt;span class="na"&gt;4. Value: flag tests that would still pass if the code were broken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no real&lt;/span&gt;
   &lt;span class="s"&gt;assertion, asserting a mock's own return value, testing implementation details,&lt;/span&gt;
   &lt;span class="s"&gt;snapshot-only logic, copy-paste cases, over-mocking the thing under test.&lt;/span&gt;
&lt;span class="s"&gt;5. Run the tests. Output PASS, or GAPS&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;one specific bullet per gap, most important first.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has no Edit or Write tools, so it reports problems instead of quietly "fixing" them. Each gap is specific enough for the repo agent to write the missing test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: /contracts for cross-repo work
&lt;/h3&gt;

&lt;p&gt;A folder of small spec files, one per topic. Each says: &lt;strong&gt;owner repo, consumers, status (proposed / agreed / implemented)&lt;/strong&gt;, and the shape (types, endpoints, examples). An agent never edits another repo; it writes the need here, and the owning agent implements it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Enforce the rules with hooks (instructions alone aren't enough)
&lt;/h3&gt;

&lt;p&gt;Rules written in &lt;code&gt;CLAUDE.md&lt;/code&gt; are suggestions. Hooks are code the harness runs every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PreToolUse guard:&lt;/strong&gt; a repo agent may edit only its own worktrees, &lt;code&gt;contracts/&lt;/code&gt;, and its notes file. Not other repos, not other agents' worktrees, and not your main checkout. Hook input includes &lt;code&gt;agent_type&lt;/code&gt; when a subagent makes the call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .claude/hooks/repo-guard.js (trimmed)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// fail open&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tool_input&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nx"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;agent_type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                 &lt;span class="c1"&gt;// set only inside a subagent&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test-auditor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ownWorktree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_wt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// _wt/backend-&amp;lt;task&amp;gt;/&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notesFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rel&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.agent-notes.md&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Allowed: own worktrees, contracts/, own notes file. Not the main checkout.&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ownWorktree&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contracts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;notesFile&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;hookSpecificOutput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;hookEventName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PreToolUse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;permissionDecision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deny&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;permissionDecisionReason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Agent "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;": work in _wt/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&amp;lt;task&amp;gt;/; cross-repo needs go to contracts/.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;PostToolUse formatter:&lt;/strong&gt; each repo gets a hook that formats the file Claude just edited, using &lt;strong&gt;that repo's own pinned prettier&lt;/strong&gt; (&lt;code&gt;node_modules/.bin/prettier&lt;/code&gt;, not &lt;code&gt;npx&lt;/code&gt;), limited to the file types its CI checks.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.claude/settings.json&lt;/code&gt; at the root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Edit|Write|MultiEdit|NotebookEdit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/repo-guard.js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Edit|Write|MultiEdit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/format-dispatch.js&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also add a guard that blocks reading &lt;code&gt;.env&lt;/code&gt; files. It caught me (the AI setting this up) trying to link a &lt;code&gt;.env&lt;/code&gt; into a temp checkout, which is exactly what it's for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Keep local git hooks fast
&lt;/h3&gt;

&lt;p&gt;Pre-push runs only &lt;strong&gt;lint and the formatter check&lt;/strong&gt; (seconds). Typecheck, unit tests, build and E2E run in &lt;strong&gt;CI on the pull request&lt;/strong&gt;, where branch protection gates the merge. Slow local hooks teach people (and agents) to use &lt;code&gt;--no-verify&lt;/code&gt;, which then skips the fast checks too.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. How a task flows
&lt;/h2&gt;

&lt;p&gt;Start Claude Code at the workspace root and describe the task. Example: &lt;em&gt;"Add utm_campaign to link analytics and show it on the dashboard."&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Plan.&lt;/strong&gt; The main session sees that two repos are involved and that it's a cross-repo change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contract first.&lt;/strong&gt; A backend copy writes &lt;code&gt;contracts/utm.md&lt;/code&gt; (owner, consumers, shape, status).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build each side in parallel.&lt;/strong&gt; One backend copy in &lt;code&gt;_wt/backend-utm/&lt;/code&gt;, one dashboard copy in &lt;code&gt;_wt/client-dashboard-utm/&lt;/code&gt;, each on its own branch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit.&lt;/strong&gt; Each copy runs its tests and returns its worktree path. The main session runs &lt;code&gt;test-auditor&lt;/code&gt; on it, and gaps go back to that same copy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ship.&lt;/strong&gt; Each copy pushes (lint + format hooks) and opens its PR. CI runs the heavy checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Report.&lt;/strong&gt; The main session summarizes with PR links and removes the worktrees.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Subagents can't start other subagents, so every handoff goes through the main session. You only talk to that one.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Running many tasks at the same time
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Every task gets its own agent copy and its own worktree&lt;/strong&gt;, so tasks never share files or branches, even when ten of them touch the same repo.&lt;/p&gt;

&lt;p&gt;Example: 10 tasks, 8 touching backend + dashboard and 2 backend-only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8 full-stack tasks → 8 backend copies + 8 dashboard copies (same task name, joined by a contract)
2 backend-only     → 2 backend copies
                     = 18 agent copies, each in its own _wt/&amp;lt;repo&amp;gt;-&amp;lt;task&amp;gt;/ worktree
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Limits and what to do about them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Machine load (installs, builds):&lt;/strong&gt; run in waves of ~4 copies; keep local hooks to lint + format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests that share one local database:&lt;/strong&gt; run that repo's test suite one copy at a time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token cost and rate limits:&lt;/strong&gt; waves; the main session only keeps each copy's short summary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task B needs task A's API:&lt;/strong&gt; contract first; list dependencies in the prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A prompt that works:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here are 10 tasks. Run one agent copy per task per repo, each in its own worktree. Cross-repo tasks go contract-first, then both sides in parallel. Max 4 copies at a time. Run test-auditor on every worktree before pushing, open one PR per repo per task, then give me one summary with the PR links.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  5. Lessons learned (the painful ones)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hooks can be silently off.&lt;/strong&gt; Git runs nothing if &lt;code&gt;core.hooksPath&lt;/code&gt; points to a folder that doesn't exist (for example, husky never installed in a stale checkout). CI failed on a formatting issue the pre-push hook would have caught. Check that &lt;code&gt;.husky/_&lt;/code&gt; exists, or run the hook once by hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A PostToolUse hook only sees Claude's file-edit tools.&lt;/strong&gt; Files changed through shell scripts, or written before the hook existed, are not formatted. CI is still the backstop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check that a PR is still open before pushing to its branch.&lt;/strong&gt; I pushed follow-up commits to PRs that had already been merged; they never reached &lt;code&gt;develop&lt;/code&gt; and had to be moved to new PRs. This is now a written rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never --no-verify to get past a hook,&lt;/strong&gt; not even for a doc-only commit. If a hook can't run in a temp checkout, fix the checkout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't symlink node_modules into a worktree.&lt;/strong&gt; Next.js/Turbopack refuses a symlink that points outside the project. Do a real install (&lt;code&gt;pnpm install --prefer-offline&lt;/code&gt; is fast thanks to the shared store).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Settings load only from the folder you start in.&lt;/strong&gt; Root hooks apply when you start at the root; a repo's own &lt;code&gt;.claude/settings.json&lt;/code&gt; applies when you start inside it. Ancestor &lt;code&gt;CLAUDE.md&lt;/code&gt; files load either way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relative paths only.&lt;/strong&gt; Absolute machine paths in shared docs break for every teammate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep settings.local.json out of git.&lt;/strong&gt; It holds personal permissions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Checklist to copy
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Workspace root repo with an allowlist &lt;code&gt;.gitignore&lt;/code&gt; + &lt;code&gt;clone-all.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Short &lt;code&gt;CLAUDE.md&lt;/code&gt; per repo, with commands checked against real scripts&lt;/li&gt;
&lt;li&gt;Root &lt;code&gt;CLAUDE.md&lt;/code&gt;: repo map, connections, routing rule, git workflow, parallel rules&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.claude/agents/&amp;lt;repo&amp;gt;.md&lt;/code&gt; (one type per repo, one copy + worktree per task) + a read-only &lt;code&gt;test-auditor&lt;/code&gt; that checks edge cases and test value&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;contracts/&lt;/code&gt; with owner / consumers / status per spec&lt;/li&gt;
&lt;li&gt;PreToolUse repo guard + &lt;code&gt;.env&lt;/code&gt; guard; PostToolUse formatter using each repo's pinned tools&lt;/li&gt;
&lt;li&gt;Pre-push = lint + format only; everything heavy in CI on the PR&lt;/li&gt;
&lt;li&gt;Guard blocks agents from the main checkout; waves of ~4 copies&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
