# Worktrees and Parallel Loops

Course: [Loop Engineering](https://www.devtutorials.ai/learn/loop-engineering) · Section: Loops at Scale
Source: https://www.devtutorials.ai/learn/loop-engineering/loops-at-scale/worktrees-and-parallel-loops

> Two agents edit the same file at the same time and one fix is lost. Give each agent its own worktree, land the lanes through a merge queue, and see why your review time is the real limit.

## Two Agents, One Folder

The first chapter built one loop with one agent. This chapter makes loops bigger. The first way to go bigger is to run several agents **at the same time**.

That is faster. It is also how fixes get lost.

## Watch Two Agents Collide First

Two agents work on eight tickets at once. They share one folder. Some pairs of tickets touch the same file.

Press **Run it in the Loop Lab**. Look at the **Lane** column and the red list under the table:

1. In the same tick, lane 1 and lane 2 both write the same file.
2. The last one to write wins. The other fix is gone.
3. But the lane that lost its fix still says "done". Its checker passed before the file was overwritten.

**In simple words:** two people typed in the same document at the same moment, and one of them lost their work without knowing.

## TL;DR

- Running several agents at once is called **parallel** work. Each agent works in its own **lane**.
- Agents in **one folder** can overwrite each other's files. That is a **collision**.
- A **git worktree** gives each agent its own copy of the repo, on its own branch.
- A **merge queue** brings the lanes back to `main` one at a time, and re-runs the tests when two lanes touched the same file.
- More agents means more fixes to **review**. Your review time, not the tool, is the real limit.

## A Real-World Example

Think about two students writing one group report.

If they both type into the same file at the same time, chaos follows. One saves, then the other saves over it. A whole page vanishes.

So they work smarter. Each one writes their part in their **own copy**. When a part is done, they hand it to the editor. The editor adds the parts to the final report **one at a time**. If two parts changed the same paragraph, the editor reads them together before adding the second one.

Now map this to a loop:

1. Each student is an **agent**, in its own **lane**.
2. Their own copy is a **worktree**.
3. The editor adding parts one by one is the **merge queue**.
4. Reading two changes to one paragraph together is a **rebase**, followed by running the tests again.

## What You Will Learn

In this lesson, you will learn:

1. What parallel lanes are.
2. Why two agents in one folder collide.
3. What a git worktree is.
4. What a merge queue does.
5. Why review time limits how many agents you should run.

In the lab, the eight tickets come in pairs that touch the same file, such as #3 and #4 in `status.ts`. Each version of the work shuffles the pairs, but keeps each pair together, so you can always see what a collision does.

## Lanes: Several Agents at Once

A loop with two agents deals the tickets out like cards: ticket 1 to lane 1, ticket 2 to lane 2, ticket 3 to lane 1, and so on. Both lanes work at the same time.

The next box deals eight tickets into two lanes, and counts the **ticks**. A tick is one turn for every lane at once.

```js runnable title="Two lanes finish in half the ticks"
const tickets = ["#3", "#4", "#1", "#19", "#5", "#7", "#8", "#15"];
const agents = 2;
const lanes = Array.from({ length: agents }, () => []);
tickets.forEach((t, i) => lanes[i % agents].push(t));

lanes.forEach((lane, i) => console.log("lane " + (i + 1) + ": " + lane.join(", ")));
console.log("ticks with 1 agent:  " + tickets.length);
console.log("ticks with 2 agents: " + Math.max(...lanes.map((l) => l.length)));
```

Expected output:

```text
lane 1: #3, #1, #5, #8
lane 2: #4, #19, #7, #15
ticks with 1 agent:  8
ticks with 2 agents: 4
```

Half the time. But look at the pairs in each tick: #3 and #4, #1 and #19. In this project, each pair touches the same file.

## Why One Folder Collides

When two agents share one folder, they share every file. If both change `status.ts` in the same tick, the second save replaces the first.

```js runnable title="Same file, same tick: the last write wins"
const files = { "status.ts": "original" };
const tick = [
  { lane: 1, ticket: "#3", file: "status.ts", change: "fix for #3" },
  { lane: 2, ticket: "#4", file: "status.ts", change: "fix for #4" },
];
for (const edit of tick) files[edit.file] = edit.change; // both write the same file

const lost = tick.filter((e) => files[e.file] !== e.change).map((e) => e.ticket);
console.log("status.ts now holds: " + files["status.ts"]);
console.log("lost fixes: " + lost.join(", "));
```

Expected output:

```text
status.ts now holds: fix for #4
lost fixes: #3
```

The worst part: lane 1 already told its state file that #3 was done. The loop's memory now says something that is not true.

## Worktrees: A Copy for Each Agent

A **git worktree** is a second (or third, or fourth) working folder for the same repo. Each worktree has its own branch. Edits in one worktree do not touch the files in another.

So each agent gets its own worktree. They can all edit `status.ts` at once, because each one edits its own copy.

| Tool | How each agent gets its own copy |
|---|---|
| Claude Code | `claude --worktree <name>`, or `isolation: worktree` in a subagent's front matter |
| Codex | A scheduled task can run in a new worktree instead of your local project |
| Cursor | Automations and cloud agents run in the background, away from your local files |

In the lab's loop file, it is one setting:

```yaml
parallel:
  agents: 2
  worktrees: true
```

```js runnable title="Worktrees: same file, separate copies"
const worktrees = { lane1: { "status.ts": "original" }, lane2: { "status.ts": "original" } };
worktrees.lane1["status.ts"] = "fix for #3";
worktrees.lane2["status.ts"] = "fix for #4";
console.log("lane 1 copy: " + worktrees.lane1["status.ts"]);
console.log("lane 2 copy: " + worktrees.lane2["status.ts"]);
console.log("lost fixes: none");
```

Expected output:

```text
lane 1 copy: fix for #3
lane 2 copy: fix for #4
lost fixes: none
```

Each lane changed its own copy of `status.ts`. Both fixes are safe, waiting to be merged.

## The Merge Queue

Separate copies solve the collision. But the work still has to come back to `main`. A **merge queue** does that one change at a time:

1. Take the next finished fix.
2. If an earlier fix already changed the same file, **rebase**: put this fix on top of the newest `main`.
3. Run the tests again.
4. Merge. Then take the next one.

```js runnable title="A merge queue lands fixes one at a time"
const finished = [
  { ticket: "#3", file: "status.ts" },
  { ticket: "#4", file: "status.ts" },
  { ticket: "#1", file: "format.ts" },
];
const touched = new Set();
for (const fix of finished) {
  const rebase = touched.has(fix.file);
  console.log("merge " + fix.ticket + (rebase ? " (rebased, tests re-run)" : ""));
  touched.add(fix.file);
}
```

Expected output:

```text
merge #3
merge #4 (rebased, tests re-run)
merge #1
```

Every fix lands. The second change to `status.ts` costs one extra test run. That is cheap compared with a lost fix.

In the Timeline, look for the **Merge queue** line under the table. A fix marked "rebased" shared a file with an earlier fix. Its tests ran again before it landed.

## Review Is the Real Limit

Four agents finish work twice as fast as two. But every fix still needs a person to read it. That is the **outer loop** you will meet in a later lesson.

Four agents finish 8 fixes in 3 ticks. One reviewer can check 4 fixes per run. The red gauge shows the rest waiting. The agents are not the limit any more. Your review time is.

```js runnable title="Fixes made vs fixes a person can review"
const fixesPerAgent = 2;
const reviewCapacity = 4; // one reviewer, per run
for (const agents of [1, 2, 4, 8]) {
  const made = agents * fixesPerAgent;
  const waiting = Math.max(0, made - reviewCapacity);
  console.log(String(agents).padStart(2) + " agents: " + made + " fixes, " + waiting + " waiting for review");
}
```

Expected output:

```text
 1 agents: 2 fixes, 0 waiting for review
 2 agents: 4 fixes, 0 waiting for review
 4 agents: 8 fixes, 4 waiting for review
 8 agents: 16 fixes, 12 waiting for review
```

A good rule: run only as many agents as you can review the work of. Unread fixes are **comprehension debt** that keeps growing.

If the review queue keeps growing, do not add agents. Add reviewers, make the tickets smaller, or slow the loop down. A fix nobody reads is a fix nobody understands.

## Lab Exercise: Give Each Agent a Worktree

Open the loop file. Under `parallel:`, keep `agents: 2` and change `worktrees: false` to `worktrees: true`.

Press **Check my loop**. On all 3 versions, there must be **0 collisions**, and the merge queue must land every fix.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Shared folder | "They work on different tickets, so it is fine." | Different tickets can touch the same file. Use worktrees. | One fix is overwritten and lost. |
| No merge queue | "Each lane can push to main when it is done." | Land lanes one at a time, with tests after each. | Two good fixes combine into a broken main. |
| Skipping the rebase test | "Each fix passed on its own." | Re-run the tests when two fixes share a file. | The pair breaks something neither broke alone. |
| Too many agents | "More agents, more done." | Match agents to review capacity. | Unreviewed fixes pile up. |

## Exercises

Each exercise ends with a value on the last line. Press **Check** to compare it with the expected answer.

## What to Remember

- Parallel agents work in lanes, at the same time.
- In one folder, two agents can overwrite the same file. One fix is lost.
- A git worktree gives each agent its own copy and branch.
- A merge queue lands lanes one at a time, and re-runs the tests after a rebase.
- Review time is the real limit. Run only as many agents as you can review.

## Further Reading

- [Loop Engineering, by Addy Osmani](https://addyosmani.com/blog/loop-engineering): worktrees as a building block for parallel agents.
- [Loop engineering: designing loops you can walk away from, by CodeRabbit](https://coderabbit.ai/blog/loop-engineering): worktrees for parallel agents.
- [git worktree, in the Git documentation](https://git-scm.com/docs/git-worktree): the command itself.

Next: **Connectors**, where the loop says "here is the fix" but nothing changes, and you connect it to a tracker, CI and chat.
