Module 08
Delegate with Isolation
On this page
In plain words
Like a college fest, work goes faster only when each volunteer owns a different area. Two agents editing the same file is not speed, it is a clash. So before splitting work between agents, write down what each one will do, which files it alone may touch, and what proof it must return. Then one integrator checks the proof and merges.
How it flows
- 1Check independence
- 2Write unit contracts
- 3Assign file ownership
- 4Run in waves
- 5Collect proof
- 6Integrate and verify
A tiny example
units = [
{"id": "api", "paths": ["backend/pay.py"], "deps": []},
{"id": "ui", "paths": ["frontend/pay.js"], "deps": []},
{"id": "review", "paths": [], "deps": ["api", "ui"]},
]
if paths_overlap(units):
stop("redesign the split")
for wave in compute_waves(units):
run_agents(wave)
check_proof_and_merge(units)Notice the split is blocked before any agent starts, and review only runs after the units it depends on.
What you will learn
- How to check if a task is really splittable, or only looks splittable.
- How to write a work unit that one agent can own end to end.
- How to keep two agents from stepping on the same files.
- What the integrator must check before merging their work.
The problem, simply
Think of a college fest. You are the coordinator. You have five volunteers and one week.
If you give one volunteer the stage setup, another the food stalls, another the posters, work finishes fast. Nobody is waiting for anybody. Each one owns their own area.
Now give two volunteers the same poster job. Both design, both print, both paste. You end up with two colour themes on one wall and money wasted. That is not speed, that is a headache.
Coding agents behave the same way. Running four at once feels productive. But if all four edit the same file, or wait on one decision nobody has taken, you have turned one clear task into a mess that fails in four places.
The idea
First, the parallelism test
Before you split anything, ask: is there real independence here?
Delegate in parallel only when at least one of these is true:
- Two investigations answer two different unknowns, and neither needs the other's answer.
- Two implementations touch completely different files, and the interface between them is already decided.
- A reviewer can read a finished piece of work without changing it.
- A slow check, like a long test run, can run while local work continues.
Keep the work serial when agents need the same files, the same undecided decision, or the same shared environment.
TipTip: If you cannot say in one line which files each agent owns, the split is not ready.
Second, a work unit is a contract
"Handle the backend" is not a work unit. It is a wish. A real work unit has six fields:
- Goal — one result you can actually see.
- Owner — one agent, accountable.
- Paths — the files this agent alone may write.
- Dependencies — units that must finish first.
- Proof — the exact command output that shows it worked.
- Handoff — files changed, decisions taken, risk left behind.
So instead of "handle the backend", write: "Add the duplicate-email check in app/accounts.py, prove it with the account test file."
Third, isolation has three layers
- Filesystem isolation — each agent works in its own copy of the repo (in Git, this copy is called a worktree). Stops accidental shared edits.
- Ownership isolation — the contract says which paths belong to whom. Stops deliberate shared edits.
- State isolation — separate log files and output files, so one agent does not overwrite another's evidence.
Now here is the trick people miss. Filesystem isolation does not give you ownership isolation. Two agents in two clean copies can still build contradicting designs. Decide the shared interface before the work starts, not during the merge.
Remember: separate folders stop collisions, not disagreements.
A worked example
Suppose Priya is building the payment page for a college portal. She wants to split the work.
Unit A: Rahul's agent owns backend/payments.py. Goal, create the UPI order and return an order id. Proof, the payments test passes.
Unit B: Sneha's agent owns frontend/checkout.js. Goal, show the ₹499 amount and open the payment popup. Proof, the page prints the order id.
Unit C: Arjun's agent is the reviewer. It only reads. It starts after A and B finish.
A and B do not share a single file. But they do share one thing: the shape of the order id. So Priya decides that first, writes it in both contracts, and only then starts the agents. If she skips this, both units pass their own tests and still refuse to talk to each other.
- 1Check independence
- 2Write unit contracts
- 3Assign paths
- 4Run in waves
- 5Collect proof
- 6Integrate
Fourth, the integrator does not redo the work
One agent (or you) plays integrator at the end. The integrator's job is narrow:
- Check each handoff stayed inside its assigned scope.
- Read the actual proof output, not the agent's cheerful summary.
- Combine changes in dependency order.
- Run the full test gate across everything, not just the per-unit tests.
- Reject any extra work nobody asked for.
- Record every conflict as a new decision, written down, not as a silent edit.
If the integrator has to rewrite most of an agent's output, the split was wrong in the first place. Fix the split, do not patch the merge.
Fifth, calibrated autonomy
Delegation does not delete your judgement. You still own anything that changes public behaviour, touches money or permissions, or cannot be undone. Agents own bounded work: investigate, implement, verify, review.
Give freedom where evidence is strong and rollback is easy. Put a checkpoint where the consequence is high. That balance is calibrated autonomy.
- 1Low risk, easy undo
- 2Let agent run
- 3High risk, hard undo
- 4Ask the human
Build it
This program takes a few work units, checks nobody owns overlapping paths, checks the dependencies exist, and then computes which units can run together in each wave.
# delegation_planner.py -- plan safe parallel agent work
UNITS = [
{"id": "api", "paths": ["backend/payments.py"], "deps": [],
"proof": "python3 -m pytest backend/test_payments.py"},
{"id": "ui", "paths": ["frontend/checkout.js"], "deps": [],
"proof": "npm run test:checkout"},
{"id": "docs", "paths": ["docs/payments.md"], "deps": [],
"proof": "markdown lint passes"},
{"id": "review", "paths": [], "deps": ["api", "ui"],
"proof": "integrator reads both handoffs"},
]
def overlaps(a, b):
# "backend" also owns "backend/payments.py", so compare prefixes too
return a == b or a.startswith(b + "/") or b.startswith(a + "/")
def find_conflicts(units):
problems = []
for i, u in enumerate(units):
for v in units[i + 1:]:
for p in u["paths"]:
for q in v["paths"]:
if overlaps(p, q):
problems.append(f"{u['id']} and {v['id']} both write {p} / {q}")
known = {u["id"] for u in units}
for u in units:
for d in u["deps"]:
if d not in known:
problems.append(f"{u['id']} depends on unknown unit {d}")
return problems
def waves(units):
# a unit can run once all its dependencies are already done
done, remaining, out = set(), list(units), []
while remaining:
ready = [u for u in remaining if all(d in done for d in u["deps"])]
if not ready: # nothing can start -> cycle
out.append(["DEADLOCK: " + u["id"] for u in remaining])
break
out.append([u["id"] for u in ready])
done.update(u["id"] for u in ready)
remaining = [u for u in remaining if u not in ready]
return out
problems = find_conflicts(UNITS)
if problems:
print("BLOCKED, fix the split first:")
for p in problems:
print(" -", p)
else:
print("Split looks safe. Execution waves:")
for n, w in enumerate(waves(UNITS), start=1):
print(f" Wave {n}: {', '.join(w)}")
print("\nProof each unit must return:")
for u in UNITS:
print(f" {u['id']:7s} -> {u['proof']}")Look at the output. Wave 1 has three units that can run at the same time, and review waits in wave 2 because it depends on two others. Now change the docs unit's path to backend/ and run it again — the planner blocks, because backend/ is a parent of the file the api unit owns. That is the check most people forget.
Where you will see this
- Claude Code and similar coding agents spawning sub-agents for a search task while the main agent keeps writing code.
- Cursor running a background agent on one branch while you edit another.
- CI pipelines splitting a test suite across parallel runners, then one job collecting all reports.
- A support bot handing a refund case to a specialist flow and waiting for its verdict before replying.
- Any team using Git worktrees or separate branches so two people can build two features without touching each other's files.
Common mistakes
- Splitting by person, not by files. If two units can touch the same file, you get merge conflicts that no test catches. Split by ownership of paths.
- Forgetting parent folders overlap.
app/andapp/models.pyare not separate. One agent owning the parent silently owns the child too. - Trusting the agent's summary instead of its proof. An agent saying "all tests pass" is not evidence. The test output is.
- Splitting when a decision is still open. If both units need the same undecided interface, they will each guess, and both guesses will be wrong at merge time.
- Skipping the full gate after merge. Each unit passing alone proves nothing about the combination. Run the whole suite at the end.
If they ask in an interview
Q: When would you run agents in parallel instead of one after another?
A: Only when the work is genuinely independent — different files, different unknowns, no shared undecided design. If two units need the same file or the same pending decision, parallel execution just multiplies the failure points without saving time.
Q: Two agents work in separate Git worktrees. Is that enough isolation?
A: No. Separate worktrees give filesystem isolation, so they cannot overwrite each other's files. But they can still build two designs that contradict each other. You also need ownership isolation, which means writing down which paths belong to whom, and settling any shared interface before work starts.
Q: What is the integrator's job in a multi-agent setup?
A: To verify, not to rebuild. The integrator checks each unit stayed in scope, reads the actual proof output, merges in dependency order, and runs the full cross-unit test gate. If it has to rewrite most of an agent's output, the original split was wrong.
Try these
- Take a real change from a project you have (say, add a search box) and write it as two work units plus one integrator, filling all six contract fields.
- Extend the planner code so a unit can also declare read-only paths, and print a warning when one unit reads a path another unit writes.
- Add a cycle to the dependencies (make
apidepend onreview) and confirm the planner reports a deadlock instead of hanging. - Find a split that only looks independent — two units in different files that still share one decision. Write that shared decision in one sentence.
Words, simply
| Word | Meaning in simple words |
|---|---|
| Work unit | One small job with one owner, clear files, and clear proof |
| Ownership | Which agent is allowed to write which files |
| Worktree | A separate copy of the same repo, so two people can work without clashing |
| Proof | The actual command output that shows the job really worked |
| Handoff | The note an agent gives back: what changed, what was decided, what is risky |
| Integrator | The one who checks and combines everyone's work at the end |
| Wave | A group of units that can safely run at the same time |
| Calibrated autonomy | Freedom for cheap, reversible work; a human checkpoint for costly work |
Quick recap
- Split work only when the parts are truly independent — different files, no shared open decision.
- Separate folders stop accidental collisions, but only written ownership stops conflicting designs.
- The integrator verifies proof and runs the full gate; it should never have to redo the work.