Skip to content

Parallelism & worktrees

When you run /c-execute, it doesn’t grind through your plan one task after another. It works out which tasks can safely run at the same time and runs them in parallel (in separate, isolated copies of your repo) then merges the results back together. This page explains how that works without you needing to manage any of it.

Every task in a plan can declare what it depends on, via a Depends: line. Task 2.1 might say it depends on Task 1.1, because it can’t start until that earlier work exists.

/c-execute reads all of those edges and builds a dependency DAG: a map of what must come before what. A task is ready the moment every task it depends on has landed. Everything that’s ready and doesn’t depend on each other can, in principle, run together.

The unit of parallel work is a lane. A lane is one phase file. Here “phase” correctly means a subdivision of the plan, the 01-…md, 02-…md files that break a big plan into chunks. Each ready phase file becomes one lane, run by one implementer agent.

Running things in parallel is only safe if they don’t step on each other. Each task declares a Touches: list: the files it will modify. The scheduler enforces a hard rule:

Two lanes run concurrently only if their Touches: file sets are disjoint (no file in common).

If two ready lanes both want to touch the same file, they don’t run together. The later one waits until the first has landed and merged. This touches-conflict guard is never overridden and never auto-resolved. If the file sets overlap, the lanes are serialized, full stop. (A conflict that slips through means a Touches: list was wrong, and /c-execute stops and tells you rather than guessing.)

Step four: each lane gets its own worktree

Section titled “Step four: each lane gets its own worktree”

A lane doesn’t run in your main checkout. It runs in its own git worktree: a separate working directory on its own branch, created from the current tip of your work. Worktrees live under .cadence/worktrees/ and are created and removed automatically.

This is what makes true parallelism safe: each lane has a private copy of the repo, so concurrent lanes can’t clobber each other’s files mid-flight. The combination of separate worktrees plus the touches-conflict guard is the belt-and-suspenders that lets multiple agents work at once without corrupting the result.

Putting the four steps together: the plan’s tasks become a dependency map, the ready ones pass the touches-conflict guard, the survivors run as parallel lanes in their own worktrees, and each lane merges back to advance the tip:

flowchart TB
    plan["Plan tasks<br/>each with Depends: / Touches:"] --> dag{{"dependency DAG:<br/>which tasks are ready now?"}}
    dag --> guard{"ready lanes'<br/>Touches: sets disjoint?"}
    guard -->|yes| run["run as parallel lanes<br/>≤ execute.max_parallel"]
    guard -->|"no — files overlap"| wait["serialize:<br/>later lane waits its turn"]
    run --> wt["each lane in its own<br/>git worktree (isolated copy)"]
    wt --> review["lane passes two-stage review"]
    review --> merge["merge back per worktree.integrate<br/>(default rebase-ff)"]
    merge --> tip(["tip advances;<br/>next ready lanes dispatch"])
    wait -.->|blocker landed| dag
  • Cap. At most execute.max_parallel lanes run concurrently (default 4). If more work is ready than slots are free, the extra lanes wait their turn.
  • Merge. When a lane finishes and passes review, it’s integrated per worktree.integrate. The default is rebase-ff: the lane’s branch is rebased onto the current tip and fast-forwarded in, keeping a clean linear history with each task’s commits preserved. Repos that forbid history rewriting can set merge-commit instead.

A single phase file can even become two or three lanes over a run: if only part of it is ready now, the ready part dispatches immediately and the rest forms a follow-up lane once its blockers clear.