Blog

Agent Orchestration: Keep Parallel Workers From Colliding

Learn agent orchestration patterns and reproduce a shared-file collision test, with task ownership, partial file leases, handoffs, and human review.

Worker A holds the lease on db/client.json while Worker B is denied that shared path and continues on db/cache.json.

Agent orchestration is the control layer that decides which agent handles a task, what context it receives, when it may act, and how its result gets checked. For a coding team, that includes coordinating access to shared files. Assigning one agent to an API change and another to a cache change does not make their edits independent when both touch the database client.

We tested that overlap against SYNCStaff's local coordination API. A denied worker left the shared file unchanged, continued on its independent file, and acquired the contested path after its owner released it. Seven checkpoints passed. This guide explains the orchestration patterns around that example, the experiment you can reproduce, and the boundaries it does not establish.

What does agent orchestration actually manage?

An orchestrator turns a goal into controlled work. It selects a worker, supplies inputs, tracks progress, handles failures, and decides whether the result satisfies the original request. The worker may be an LLM agent, a deterministic program, or a person. The essential part is the explicit relationship between the task, its permissions, and its completion evidence.

A useful coding task therefore needs more than a role such as “backend specialist.” It needs an expected result, a bounded set of files or interfaces, a starting revision, and an acceptance check. Otherwise two sensible workers can make incompatible changes while each believes it is doing the right thing.

Keep three decisions distinct:

Decision Question Example
Model routing Which model answers this request? Choose a model for a planning or editing step
Agent orchestration Which worker acts next, with which inputs and limits? Delegate a cache implementation and join its result
Repository coordination Which worker may change this shared resource now? Refuse an overlapping lease on the database client

A model router can sit inside an orchestrated workflow. A worker can also use its own model router. Neither arrangement automatically resolves competing edits to a shared repository. Our LLM router audit guide covers recording model selection; this article focuses on task execution and shared changes.

Choose an orchestration pattern that matches the work

Start with the simplest arrangement that can complete the task and produce evidence. Adding agents introduces handoffs, waiting, repeated context, and more places for an error to travel. A strong single-worker baseline helps you discover whether specialization is actually useful.

Anthropic's Building effective agents distinguishes predefined workflows from agents that choose their next steps dynamically. That distinction is useful when deciding which parts of a coding process should stay predictable. Its tooling discussion has evolved since publication, so use it for the architectural distinction rather than current product setup instructions.

Pattern A useful coding application Control to define first
Sequential workflow Update a schema, then its client, then integration tests Required output and acceptance check at each step
Concurrent workers Implement independent components after agreeing on interfaces File ownership and a check for overlapping changes
Supervisor and workers Investigate a defect whose subtasks emerge during the investigation Delegation depth, allowed tools, and escalation conditions
Handoff between specialists Transfer a diagnosed issue from investigation to implementation A structured handoff and an accountable receiving worker

Microsoft's agent orchestration training module provides framework-specific exercises across several of these patterns. The shared-repository problem remains relevant whichever framework schedules the work.

Concurrent execution is a good fit when the outputs can be developed independently. When one task changes the contract another consumes, establish that contract first. You can still parallelize implementation afterwards, but the dependency belongs in the plan rather than being discovered during the merge.

Delegate tasks, coordinate edits

Consider a small change with two workers:

  • Worker A updates the route used by db/client.json.
  • Worker B enables a cache, adding db/cache.json and updating db/client.json.

The task descriptions sound different. The path lists reveal the shared dependency. A coordinator that only hands out prose can miss it; an intent that names the expected paths makes the overlap inspectable before a compliant worker edits.

In SYNCStaff, the shared board records the work, an intent describes the proposed change, and a file lease coordinates ownership of the declared paths. Conflict information gives the blocked worker a concrete next step: reduce its scope, take an available partial grant, or pick independent work. A named person remains responsible for decisions that require human judgment.

That is particularly useful when teammates use different coding tools. A shared coordination record can describe a Claude Code worker and a Codex worker without requiring their separate conversations to contain identical context. The coordination protocol still needs to be integrated into each worker's workflow.

Our Claude Code agent teams guide covers delegation within one tool. The additional question here is how workers and teammates agree on changes to the repository they share.

Our controlled shared-file experiment

On November 1, 2026, we ran a new deterministic collision fixture against the local SYNCStaff HTTP API, backed by an isolated, temporary PostgreSQL database. We also ran the repository's 16 existing lease regression tests. All passed. The new fixture contains seven explicit checkpoints; it runs as one automated test.

The clients were registered with Claude Code and Codex labels, but they were deterministic programs. They made no model calls and no production API calls. This was a test of coordination decisions and client behavior, not a comparison of coding models or a productivity benchmark.

The experiment used this starting file:

{"version":1,"route":"old","cache":false}

The control: two stale snapshots lose a change

First, both clients read the starting state. A wrote its route change. B then wrote its cache change using the old snapshot. The resulting file had cache: true and route: "old": A's route change was lost.

We deliberately scheduled those operations to expose the failure. The result demonstrates that this interleaving can lose an update. It does not tell us how often a real coding team experiences it, and it is not a measured failure rate for any coding agent.

The coordinated run: refusal plus useful independent work

We reset the file and let Worker A acquire the shared-path lease. Worker B then requested both its new cache file and the already-held client file. The API refused the full request and identified the contested path, its holder, and the holder's intent.

The response also reported that a partial grant was available for db/cache.json. B accepted that narrower grant and wrote the independent cache file while A retained the client path. Refusal therefore supplied a way to continue useful work.

Checkpoint Observed result
Uncoordinated stale-write control A's route change was lost
A requests the shared client path Lease granted
B requests both paths Full request denied; holder and intent identified
B requests only the free cache path Partial lease granted; independent file written
Preflight checks the shared path A covered; B not covered
A releases, B acquires and re-reads Both route and cache changes preserved
Experiment releases remaining leases Zero active experiment leases

B's client obeyed the denial. We compared the shared file's SHA-256 before and after that refused write attempt; both hashes were ba75494160ba690af1375c299263ad76e5b41ad22e537130768600c2a5e7f13d. That checks the absence of a write in this fixture, rather than assuming a refusal message means nothing changed.

After A released its lease, B acquired the contested path and read the latest file before applying its change. The final state preserved both edits:

{"version":1,"route":"new","cache":true}

Re-reading matters. Acquiring a lease after waiting does not make an old snapshot current. A worker must reconcile the state it receives before writing its result.

What the experiment does not establish

A lease is a coordination mechanism. The fixture's write guard honored the API decision; the server did not apply an operating-system lock to the file. A process with filesystem access could write around that client-side guard. Use filesystem permissions and sandboxing where you need an execution boundary.

The new fixture disabled model-based arbitration and did not exercise a human approval flow. It also did not test Git merges, semantic compatibility between source changes, malicious workers, or live model-generated edits. Those are separate checks, and none should be inferred from seven passing coordination checkpoints.

Replay the client decisions yourself

The real API experiment ran against internal source revision 1c0e7609ecb7b0aea5e81e83fbeed15a6b86a82f on Ubuntu with Node.js 22.22.1. It used the repository's temporary PostgreSQL test runner. The server repository is private, so those integration tests are not a public installation recipe.

We have provided a separate, self-contained client-contract replay. It contains normalized responses recorded from the API experiment, uses temporary local files, and requires only Node.js. Read the downloaded source before running it:

curl -fsS https://www.syncstaff.ai/downloads/agent-orchestration-collision-lab.mjs.txt \
  -o /tmp/agent-orchestration-collision-lab.mjs
node /tmp/agent-orchestration-collision-lab.mjs

The replay performs seven checks of the client decisions and local file outcomes. It makes no network requests and no model calls. Grant, refusal, preflight and cleanup responses are recorded constants. It demonstrates why the client should honor a refusal and re-read after a handoff; it does not independently test the private server or issue real leases.

We ran both exercises. The integrated fixture exercised actual server decisions, while the public replay checked the client behavior against those recorded decisions. Keep that distinction when adapting the example to another coordination system: replace the recorded responses with your own test server's real responses before calling it an integration test.

Give each handoff a contract

A worker should receive enough context to perform its bounded task and return enough evidence for the coordinator to accept or reject the result. Copying the entire conversation into every worker is expensive and can carry irrelevant assumptions forward. Passing only a one-line request can omit the dependency that makes the work safe to combine.

This illustrative task brief is a portable design example, not a SYNCStaff API payload:

{
  "goal": "Enable cache reads without changing the client route",
  "starting_revision": "the reviewed base commit",
  "planned_paths": ["db/cache.json", "db/client.json"],
  "dependency": "Re-read the client after its current owner releases it",
  "acceptance": ["route stays new", "cache becomes true"],
  "return": ["changed paths", "test results", "unresolved questions"]
}

The coordinator should verify the outputs against that brief. An agent saying “done” is a report of execution, not proof that the intended behavior works. Preserve failed attempts and unresolved questions too, so the next worker does not repeat a route already shown to fail.

Set retry, budget and escalation rules before scaling

An orchestrated system needs a stopping condition. Otherwise a supervisor can repeatedly delegate an impossible task, or two workers can keep returning the same problem to one another. Define a maximum number of attempts, a time or cost budget, and the evidence required to justify another attempt.

Record these fields per task: worker identity, starting revision, declared paths, dependency state, attempts, acceptance results, and remaining questions. For blocked work, record the conflicting resource and the action that would unblock it. This makes waiting distinguishable from failure and gives a teammate something concrete to resolve.

Apply human review to consequential decisions: a changed shared interface, a permission expansion, a migration, or a deployment. The responsible person should see the proposed change, its impact, and the available options. A generic approval prompt without that context is a weak checkpoint. Our human-in-the-loop guide explains how to choose decisions worth a person's attention.

For parallel coding work, worktrees can keep filesystem changes separate while each task develops. They still need an integration plan: separate directories do not make two incompatible interface changes compatible. Use our Claude Code worktrees guide for that execution setup, then test the combined result before merging.

Evaluate the orchestration on completed work

Before adding workers, compare the same bounded task with a single worker and with your proposed orchestration. Keep acceptance criteria and starting state consistent. Measure completed, accepted work; include coordination overhead, waiting, repeated attempts, and human review time.

For shared-repository work, useful diagnostics include blocked edit attempts, available partial work, stale state at handoff, conflicting interface changes, and acceptance checks that fail after integration. Inspect individual failures before turning those observations into a headline success rate.

The experiment here gives a narrow, reproducible result: explicit path ownership plus a compliant client prevented the scheduled overlapping write, while partial ownership let independent work proceed. It gives an engineering team a concrete coordination behavior to check without asking it to believe an unmeasured productivity claim.

Before your next parallel coding run, name the shared goal, declare each worker's likely paths, choose who decides disputed changes, and rehearse one overlapping-file handoff. If you use SYNCStaff, put those tasks on the shared board and inspect the intent and lease decisions as the work moves. Confirm that a refused worker actually replans, and that the final change passes the original acceptance checks.

Frequently asked questions

What is agent orchestration?

Agent orchestration controls which worker handles each task, what inputs and permissions it receives, and how its result is checked. In a coding workflow, useful orchestration also tracks shared dependencies, conflicting edit plans, retries and the decisions a human must make before the work proceeds.

How is agent orchestration different from an LLM router?

An LLM router selects a model for a request. Agent orchestration manages a wider task lifecycle: delegation, context, dependencies, execution and acceptance. A worker can use a model router internally, but selecting a model does not establish ownership of a shared file or resolve overlapping changes.

When should coding agents run in parallel?

Run coding agents in parallel when their outputs can be developed independently and you have defined the interfaces they share. Check likely file overlap before editing, keep acceptance criteria consistent, and test the combined result. When one task changes another task’s contract, settle that dependency first.

Do file leases stop another process from editing a file?

A coordination lease does not by itself impose an operating-system write lock. In our experiment, the denied client deliberately honored the API decision and left the file unchanged. Processes that bypass the coordination workflow need separate controls, such as filesystem permissions or sandboxing, where an execution boundary is required.

What should happen when two agents need the same file?

Identify the current owner and its planned change, then choose an explicit handoff. The blocked worker can take independent work or a permitted partial scope while it waits. After acquiring the shared path, it should read the latest state, apply its change and run the agreed acceptance checks.

Powered by Sync Chief of Staff