Agent operations
Ten Coding Agents in a Trench Coat
A practical systems model for running parallel coding agents with Git worktrees, explicit runtime ownership, immutable ready commits, and one short-lived integration lease.
Summarize this article
Open a prefilled request in your preferred assistant.

TL;DR
- A Git worktree isolates checked-out files,
HEAD, and the index; it does not isolate ports, databases, caches, credentials, background processes, or the final merge. - Safe parallel work needs three separate controls: file isolation, runtime ownership, and integration ownership.
- Implementation agents should run cheap focused checks, while one short-lived integration owner validates the exact combined commit set once.
- The useful metric is accepted, integrated, passing work per hour -- not open panes, generated tokens, or how frightened the laptop sounds.
- The technical artifacts are the open-source GridBash repository and a versioned saturation experiment. The experiment has no results yet; I am not going to hallucinate a bar chart and call it science.
Ten coding agents can edit ten things at once, although they can also start ten copies of the same development server, request port 3000 with tremendous confidence, test against whichever process won, and return ten green checkmarks from nine imaginary applications.
That is the problem: more workers produce more candidate work, while the repository still has one reality.

I built GridBash as a local workspace for running CLI coding agents side by side in real terminal panes. The visual part is fun because six agents glowing inside one terminal grid makes the machine look extremely employed; the systems part begins when somebody has to identify which branch each process edited, which server owns a port, which database a test mutated, and whether the combined application passed or six branches passed separately in six private universes.
The trench coat opens at the last question.

Observability also has to stay cheap. In one set of release-mode GridBash rendering microbenchmarks, caching the final cell buffers for an unchanged 120-by-40 pane reduced that rendering path from 273.6 to 75.3 microseconds per frame, about 3.6 times faster; ANSI-to-history filtering moved from 26.1 to 14.4 microseconds.
Those are terminal-rendering measurements, not evidence that developers became 3.6 times faster. I enjoy benchmark numbers too much to let them leave the house without supervision.
Worktrees are excellent, but they are not apartments
A Git worktree gives an agent separate checked-out files, HEAD, and index while sharing the repository's object database and most refs. That boundary is exactly what concurrent editing needs because one agent can build a feature while another fixes a bug without either process touching the other's uncommitted files; the Git documentation describes the boundary precisely, and the Claude Code documentation applies the same pattern to parallel sessions.
I normally start independent tasks from one reviewed base commit:
git fetch origin
git worktree add ../repo-auth -b feat/add-auth origin/main
git worktree add ../repo-tests -b test/add-auth-coverage origin/main
git worktree listThe auth agent and test agent can no longer overwrite one another's files, which prevents the dumbest collision class; they can still fight over network ports, development databases, browser profiles, tool caches, compilers, containers, credentials, and the branch where every finished change eventually has to meet.
An agent experiences a worktree as a whole project, while the operating system experiences it as another directory used by another process. The operating system is correct.
The three-layer model

Files: one editing agent, one worktree
Each editing agent owns one branch and one worktree; it may inspect other work when necessary, but it does not reset, clean, move, merge, or delete another agent's state. The worktree prevents accidental write-write collisions, although it cannot make overlapping tasks independent: two agents can edit different files, pass separate tests, and still make incompatible assumptions about the same API.
Task decomposition therefore matters as much as directory isolation. If two tasks must rewrite one hot file or make competing schema decisions, sequence them; parallelism is not a personality test, and some work is simply coupled.
Runtime: everything mutable needs an owner
Two agents can have perfectly isolated worktrees and still produce fake evidence. Agent A starts its branch on port 3000; Agent B receives an address-in-use error, notices that an application is already listening, tests against Agent A's server, and reports that Agent B's branch works.
Every individual step looks locally sensible; the conclusion is completely false.
Databases create the same failure with more emotional damage because one test suite can reset fixtures while another is halfway through a transaction, a migration can change the schema underneath a preview, and two agents can share a cache namespace long enough to exchange stale values like cursed pen pals.
The fix is boring and effective: declare ownership for mutable resources.
AGENT_SLOT=02
APP_PORT=3102
TEST_DATABASE=app_agent_02
CACHE_NAMESPACE=agent_02
PREVIEW_OWNER=feat/add-authThese values may live in an ignored environment file, launcher, or small registry; the invariant is that every port, database, cache, credential scope, browser profile, and long-lived process has a declared owner, so nobody guesses.
Process ownership matters after the task ends as well. The agent that starts a watcher, browser, container, or preview records how it launched the process and stops it during handoff; otherwise one completed task leaves a port occupied, and the next agent begins the ancient debugging ritual of killing random processes until morale improves.
Integration: green branches are not a green system
Suppose Branch A changes an API response, Branch B updates the frontend for the old response, and Branch C adds tests around a fixture neither branch preserves. All three branches may pass independently and merge without textual conflicts because they touch different files; the combined application is still wrong.
A test result belongs to one exact commit or combined tree, not to the general atmosphere around a repository. I therefore split validation into two classes:
- Focused checks run with each implementation agent; they are cheap, narrow, and tied directly to the changed behavior.
- Broad checks run once against a declared combined snapshot; they include full builds, repository-wide suites, browser systems, containers, installers, and anything else expensive or globally stateful.
When six agents finish together and launch the same full suite, the laptop performs six costly experiments that still do not prove the commits work together. The output scrolls faster; the truth does not.
A tiny bureaucracy for tiny robots
The integration mechanism has two parts: an immutable ready queue and one short-lived integration lease.

An implementation becomes ready only after review and a commit. The queue records the branch, immutable commit hash, worktree, pull request when relevant, and timestamp:
{
"branch": "feat/add-auth",
"commit": "4b8d...",
"worktree": "C:/worktrees/repo-auth",
"pr": 142,
"ready_at": "2026-07-18T18:42:00Z"
}The commit hash matters because branches move and worktrees become dirty; by dinner, "the auth change" can refer to four snapshots, while the hash still identifies the code that was actually reviewed.
My coordination helper stores queue records as Git blobs and points custom refs at them inside the common Git directory:
refs/gridbash/queue/<hash-of-branch>
refs/gridbash/locks/integrationLinked worktrees share that directory, so every worktree sees the same queue and lock without adding coordination records to the application tree. It is a tiny database built from one of the least fashionable databases available: Git.
The integration lock uses git update-ref as a compare-and-swap operation. A claimant writes owner metadata into a blob, then attempts to create the lock ref only when its previous value is the all-zero object ID:
token = write_blob(owner_metadata)
acquired = update_ref(
"refs/gridbash/locks/integration",
token,
ZERO_OID
)Only one claimant can create the ref from that expected empty state; everybody else leaves the queue unchanged.
The owner removes entries already contained in upstream main, snapshots the remaining commits, creates a temporary integration tree, merges every clean candidate, refreshes the queue once immediately before broad validation, runs the affected focused checks plus one broad suite, records exactly which commits passed, and releases the lock even after failure.
That single refresh keeps the batch useful without making it immortal. Refresh forever and the snapshot never stabilizes; never refresh and a broad run may exclude three changes that became ready five seconds after the initial claim, so one refresh accepts arrivals before expensive validation begins while later arrivals form the next batch.
The lease does not create a permanent manager agent; it grants temporary ownership of one scarce operation.
Ten ways the circus catches fire
The recurring failures fit the same three-layer model:
| Layer | Failure | Detection signal | Control |
|---|---|---|---|
| Files | Shared working tree | Unexplained edits or overwritten work | One branch and worktree per editor |
| Runtime | Shared port | Address-in-use errors or tests hitting another branch | Declared shared server or port per worktree |
| Runtime | Shared database/cache | Nondeterministic fixtures or cross-task state | Database, schema, tenant, or namespace per worktree |
| Runtime | Shared generated state | Lockfile churn, stale output, cache-only failures | Document shared state; isolate mutable state |
| Runtime | Duplicate broad validation | Several identical heavy commands in the process tree | Parallel focused checks; serialized broad checks |
| Integration | Competing snapshots | Tested commit differs from merged commit | Immutable ready queue and one lease |
| Integration | Late-arrival restart loop | Validation never reaches a stable batch | Refresh once; defer later arrivals |
| Handoff | Silent agent exit | Dirty worktree with no trustworthy status | Record branch, commit, checks, blockers, and next action |
| Cleanup | Orphan process | Port or memory stays occupied after completion | Track and stop owned child processes |
| Cleanup | Unsafe cleanup | Missing worktree, branch, or uncommitted files | Agents clean only their own verified state |
Each class invalidates something different: a file collision damages work, a runtime collision makes execution evidence untrustworthy, and an integration collision breaks the relationship between what passed and what ships. "Use worktrees" remains good advice; it is only one-third of the sentence.
Stop counting panes
A six-pane GridBash workspace is easy to launch:
npm install -g gridbash
gridbash 2x3 --profile codex --worktreesThat command is a reproducible technical artifact; it is not a productivity result. The number worth measuring is accepted integrated throughput:
useful throughput = accepted integrated passing task sets / elapsed hourThe hypothesis is that throughput rises, flattens, and eventually falls as coordination cost overtakes parallel work.
The versioned experiment specification compares 1, 3, 6, and 10 agents across separable, lightly coupled, and tightly coupled tasks; each shape runs under a shared checkout, isolated worktrees, worktrees plus runtime ownership, and full isolation with a ready queue plus integration lease.
The primary metric is time to an accepted, integrated, passing commit set. Secondary measurements include accepted tasks per hour, operator minutes, interventions, merge conflicts, duplicated changes, failed validation runs, validation compute time, peak memory, token cost, recovery time, and the age of the oldest ready commit.
The protocol requires at least five repetitions per cell, randomized execution order, raw event logs, task-level outcomes, and separate accounting for operator time because a ten-agent run can look fast while quietly consuming an hour of human rescue work and six copies of Chromium.
Version 0.1 contains no results; its results field is null. Until the runs exist, the defensible claim is narrower: worktrees isolate files rather than the machine, mutable runtime resources need owners, broad validation needs one declared snapshot, immutable commits make handoffs auditable, and another agent is useful only while accepted integrated throughput continues to rise.
Anything stronger needs data, not a confident adjective.
Sources and artifacts
- GridBash source, documentation, and quick start
- GridBash high-load runtime microbenchmark engineering log
- Git worktree documentation
- Claude Code worktree documentation
- Parallel-agent saturation experiment, version 0.1
All embedded visuals are original to this draft; the raster illustrations were generated with OpenAI image generation, while the conceptual saturation curve was constructed from the stated hypothesis and contains no measured values.
Conclusion
Parallel coding agents fail in different ways because files, runtime state, and integration are three separate coordination problems wearing one very large trench coat. Give every editor a worktree, every mutable resource an owner, every green check an exact commit, and one short-lived integration owner the combined batch; then measure whether another agent increases accepted integrated throughput or merely increases the surface area of the incident report.
The tenth agent can stay; the eleventh needs to bring its own port.