More Agents, More State to Own

Part 5 of 6 Harness Engineering: How Coding Agents Turn Decisions into Work

In brief

In brief

A subagent is a separate agent that a parent agent hands part of a coding task. Give subagents narrow read-only research questions, let one agent write the combined patch, and test that final patch, because separate conversations can still share files.

  • New sessions separate chat history. Worktrees separate checked-out files. Permissions and process controls still need their own checks.
  • Assign research questions that use different sources, then let one agent combine their findings into a patch and rerun the tests.
  • A finished or cancelled worker may leave stale findings or running processes. Check the source version and clean up what it started.

Two agents inspect the same cache bug. One finds the faulty key. The other discovers that the test fixture always renders English. If both then edit the helper and fixture and run tests in one directory, the parent agent that started them has another problem: which files did each result describe?

The trouble starts before a merge conflict. A test can pass against another agent's unfinished edit. A reported line number can move while a worker reads it. A clean final diff can hide that nobody tested the combined change.

This is the series' document-preview repair. A cache keyed only by document ID can return an English preview to a German request. The goal is to include the relevant locale in the key and add a regression test.

A subagent is an agent assigned part of the parent's task. Here I would use two read-only research workers with different questions, then let one agent write and test the patch. Split independent research. Give one writer responsibility for the shared change.

Split the questions before splitting the work

The first worker asks where locale enters rendering and where the cache key loses it. Its report should trace locale resolution, lookup, rendering, and storage. A function named getPreview proves little. A parent object might already partition the cache by locale. Rendering might also use the locale after fallback, such as de-DE for a de request. This post calls that the effective locale.

The second worker asks which tests could expose cross-locale reuse and what they miss. It reads fixture setup and cleanup, test isolation, and the renderer stub. Do successive requests share a cache? Does the returned preview show which locale was rendered?

Assignment Evidence to return Authority in this design
Trace preview identity Lookup/store call chain and effective locale source Read the assigned source snapshot
Inspect regression coverage Relevant fixture, missing sequence, suitable assertion Read tests; run only explicitly bounded checks
Integrate the repair One patch, its regression, and final verification Write the implementation workspace

The workers can uncover different reasons a plausible fix would fail. One might find that de resolves to de-DE before rendering. The other might find that every test recreates the cache, so no test observes reuse. Together, those findings tell the writer to keep the cache alive across requests and check the locale the renderer actually uses.

Asking both workers to “fix the locale cache bug” may send them to the same helper while both miss the fixture problem. A reviewer added later cannot recover information their assignments never asked them to gather.

Harness Engineering surveys orchestration designs.

A fresh conversation can still share files

For the cache-key investigation, give the worker the symptom, expected locale behavior, source snapshot, entry points, and requested report. Include the constraints: preserve same-locale reuse and the developer's unrelated edits. The worker should not have to recover them from the parent's compacted conversation.

Forking the whole history may carry forward a discovery or a mistaken premise. Starting empty may remove that premise but lose why an unusual fallback matters. Both choices change the worker's inputs. Neither makes it scientifically independent.

Record the question, inherited context, allowed tools, and working directory. If the parent changes the task from the requested locale to the effective locale, send that correction or discard findings based on the old assumption.

A separate session gives the worker its own messages. Two sessions may still share a checkout, test cache, local database, package store, network account, or service port. Separate boxes in an agent diagram can hide those shared resources.

In this design, researchers read a fixed snapshot before the writer starts. That is a scheduling choice. If the source must keep changing, each report needs a revision plus quoted code or stable symbols that the writer can recheck against the current files.

Worktrees separate edits but require integration

In a shared directory, each worker's partial edits become visible immediately. “Only touch your own files” helps when files divide cleanly. It helps less when a cache helper, test, and shared fixture belong to one change.

The Git 2.56.0 worktree manual describes linked checkouts with separate per-worktree state, including the index and HEAD, while sharing repository data. A worktree gives each worker its own files. It does not give it its own database or processes, and a worker can still open another checkout unless permissions block it.

Worktrees could let two workers try alternative fixes without overwriting each other. Someone must then choose a candidate, inspect overlapping changes, and rerun tests after combining them. A passing test on candidate A covers A, not the merged A-and-B code. Checkouts separate the candidates. They do not test the combination.

Competing implementations add little here. I would finish the two read-only investigations, then let one writer edit one workspace. Where practical, the execution layer should withhold write access from researchers. An instruction asks researchers not to write. Filesystem permissions enforce that limit. If only a shared writable checkout is available, record the limitation and avoid overlapping writers.

Research checks can also write: a test runner may update snapshots, refresh fixtures, or change a database. Allow only specific checks with acceptable side effects and put temporary output in a separate location. Calling the assignment “research” does not restrict its tools.

Fig. 1. Two read-only research sessions may inspect a snapshot in parallel. One writer waits for both version-checked reports, writes the patch and regression, then tests the final workspace. Missing research blocks writing. Full interactive figure

Ask subagents for a report with four fields

“The bug is in the cache key; add locale” leaves the writer guessing. Which locale? Were lookup and store both inspected? Did the files change during research? Did the worker run anything? What remains unknown?

Ask each researcher for four fields: observation, supporting location, consequence for the repair, and unresolved question. They connect a conclusion to code the writer can inspect.

A cache-key report might say that the request resolves de to de-DE, rendering receives that value, and both cache operations use the document ID alone. Both operations need the same locale-bearing key. The report might still ask whether persisted entries survive deployment and need a namespace change.

A test report might find that every render gets a fresh map and the renderer stub always returns the same HTML. The regression needs a shared fixture and different output for each locale. The writer can choose assertion syntax once that behavior is clear.

If workers disagree about whether the key needs the requested or the effective locale, inspect what rendering consumes. A vote cannot answer that source question. Even if both agree, check the decisive code. Shared instructions can produce shared mistakes.

One agent writes the combined patch

The writer builds the key from document ID and the effective locale used by rendering, uses it for both lookup and store, and adds a cross-locale regression. Follow the application's storage rules. A tuple-like encoding can avoid collisions from naive delimiter concatenation.

Test a sequence with the same cache: render a document in English, then request German. The second result must come from German rendering rather than the English entry. Repeat German to check cache reuse. The fixture should expose those outcomes without a real translation service.

Inspect the surrounding code too. If fallback already resolves locale before the helper, resolving it again in the key builder creates a second policy. If the cache persists, old entries may remain after the key changes. A local map just starts empty. A remote store may need a new namespace or an expiry rule. Find out which case applies before adding requirements.

Rerun focused tests after the last relevant edit and record the command, result, and tested source state. A researcher's earlier command ran before this patch. Its proposed assertion may refer to a fixture that has since changed. Only tests on the combined workspace check whether the final implementation and regression agree.

“One writer” describes this shared change. Research can stay parallel, and independent modules could be edited in separate workspaces. One agent must still answer for the delivered patch and explain which test results apply to it.

The parent budget includes every worker

A limit of ten tool calls per worker does not cap the task at ten. In this example, two workers, one retried assignment, and a reviewer can use up to forty. Track the total across the task.

For this workflow, I would start with two workers, one question each, no child spawning, and one bounded follow-up round. Reserve time for implementation and verification before spending the rest on research. A worker that cannot find the renderer's locale input should report the gap instead of redesigning the repository.

Track wall time, tokens, and external work separately. A worker can wait for a slow test while producing little output, or produce a long report quickly without reading more source. A second worker may shorten elapsed research while increasing computation and the parent's synthesis work.

Continue only if more research could change the repair or its acceptance. Finding out whether the cache persists may matter. Another general review of the same helper may add little once the cause and regression are clear. The single-agent loop needs limits too. Delegation adds more calls to the same task.

Cancellation must track processes and partial edits

Suppose the user narrows the task to the in-memory cache while a worker investigates persistence. Cancelling could mean stopping new model steps, interrupting a tool, terminating a subprocess, or ignoring a late report. The harness must state which actions it supports.

The Model Context Protocol (MCP) revision 2025-11-25 describes cancellation notifications racing with completion. The receiver may be unable to cancel the request. Requests that run as MCP tasks (long-running requests whose status the client can check) are cancelled through a separate task mechanism.

Here the parent marks the assignment as superseded, asks the worker to stop, and identifies its active processes. Keep late findings as history rather than silently using them for the current patch. If the worker could write files, inspect its partial edits before another writer continues.

A test server can keep running and hold a port after the conversation ends. Killing a process leaves files it already wrote. Cleanup needs the working directory, process ID, temporary outputs, and a record of who uses any shared service. Deleting a cancelled worker's directory can hurt another worker using it.

“Worker cancelled” reports scheduling status. “No child process remains; no tracked file changed” reports an inspected result. If cleanup cannot confirm that result, include the uncertainty in the handoff rather than describing cancellation as rollback.

Check the reports, then test the final patch

A completed worker may report an old revision, a nonexistent path, or a lookup without the relevant store call. Check the findings that affect the patch before acting on them.

After implementation, give a separate reviewer the patch and required behavior, not only the writer's explanation. Ask whether the test fails on the old code, still checks same-locale reuse, and confirms that lookup and store use the same effective locale. Let the reviewer inspect the actual test.

The writer can check that the regression fails when only the old key behavior is restored, then passes with the fix. Run that check in a controlled copy or carefully verify restoration afterward. This check helps when a fixture might hide the defect.

A focused unit test covers the helper under its fixture. Remote-store migration, production translation quality, and throughput need their own observations if those paths matter to the application. Reviewers agreeing cannot expand what a measurement tested.

Report the changed behavior, tests run on the final patch, and relevant unfinished checks. The parent is responsible for that account. Completed-worker counts help scheduling. They say little about whether the repair works.

The cache key gains one input, while every extra agent adds a history, a lifecycle, and potentially another route to shared resources. Two narrow investigations can justify that cost. Limited permissions and inspectable reports keep the final implementation small.

Series index · Next: When the Harness Becomes a Platform

Updated 

Related notes