← Back
Field notes

Failure modes from running a multi-agent Claude Code harness solo

Anthony Rasch·6 min read

I run a solo studio. Six mobile apps in 133 countries, a SaaS, a marketing pipeline, local AI inference jobs that run while I sleep. The whole thing is held together by a Claude Code harness I’ve built up over the last year: custom skills, subagent controllers, a typed memory system, scheduled headless agents, a CLI that ties them together.

That harness is what makes the studio possible at this size. It’s also where I’ve learned the most about how multi-agent systems actually break under real load. What follows is field notes from three failure modes I’ve hit, what I did about each, and what they have in common.

These aren’t hypotheticals. They’re things that broke at 11pm with a release due, that I fixed the next morning, and that I’ve since put guardrails around.

01

Subagent commit pollution

The symptom. You dispatch a subagent to implement Task X. It works, it tests, it commits, it returns a DONE report with a SHA: abc123, done, all tests green. You trust the SHA because the report looks fine. You move on.

Two days later you notice a regression in a different product. You bisect. You land on abc123, the commit that was supposed to only touch Task X. Except git show --stat abc123 reveals it also bundled in unrelated changes: stuff the subagent explored, regenerated, or left staged from a previous run. The DONE report didn’t mention any of it.

Why this happens. Subagents report intent, not reality. They know what they meant to change. They don’t actually inspect the diff they committed and compare it against the task scope. If the working tree had pre-existing staged files, or if the subagent’s exploration touched files as side effects, those ride along in the commit silently.

The countermeasure. After every subagent reports a commit, the controller runs git show --stat <SHA> and reads the file list. If anything outside the expected scope appears, split the commit: cherry-pick the intended files, reset the rest. The DONE report is now treated as a claim, not a confirmation.

This sounds obvious. It is, in retrospect. It wasn’t obvious before I’d lost a Saturday morning to it.

Principle

Subagents report intent. Controllers verify reality. Anything else is a trust gap waiting to ship.

02

Parallel session git race

The symptom. You have two Claude Code sessions open in the same repo. This is normal for a monorepo studio. Session A is working on Product 1. Session B is working on Product 2. They share a working tree and an index.

Session A stages its files and runs tests. While the tests are running, Session B stages its files. Session A’s tests pass. Session A runs git commit -m "...". The commit includes both sets of changes, because git commit commits whatever is in the index, not whatever the session thinks it staged.

You now have a commit on Product 1’s branch that contains Product 2’s changes. If you push before noticing, history is contaminated across products.

Why this happens. The git index is shared mutable state across sessions. Each Claude Code session has its own conversation, its own memory of what it staged. Neither session knows about the other’s stage operations. The only authoritative state is the index itself, and neither session reads it before committing.

The countermeasure (immediate). git show --stat HEAD after every commit. If foreign files appear, git reset HEAD~1, restage only the intended paths, recommit. Same pattern as Failure 1: verify the diff against intended scope, treat the agent’s bookkeeping as untrusted.

The countermeasure (structural). git commit -- <paths> instead of committing the whole index. Narrows the race window. Doesn’t close it (a parallel session can still stage the same paths), but removes the most common form: collateral inclusion of unrelated files.

The cleaner structural fix is git worktrees, one per session. I’m currently in a release window where I’ve explicitly disabled worktrees for solo-operator overhead reasons, so I rely on the verification pattern. Worktrees are the right answer for most teams.

Principle

Anywhere multiple agents share mutable state, the bookkeeping of any single agent cannot be trusted as authoritative. Read the state, don't trust the cache.

03

Long-session scope drift

The symptom. A session starts on Task A. Twenty minutes in, A is mostly done. The agent or the operator notices something adjacent: a small bug, a quick refactor, a “while we’re here” improvement. The session pivots. The new thing pulls in a third thing. By the end of the session, what shipped has nothing to do with what was planned, and Task A isn’t actually finished.

You discover this the next morning when you check the original task and find it still open, and the commits from yesterday went somewhere else entirely.

Why this happens. Agents are tuned to be helpful. Saying yes to scope expansion feels helpful. Operators (especially solo ones) are opportunistic. If the agent is already loaded with context, why not drain the adjacent backlog? Together they drift, and neither party is wired to push back.

This isn’t a single-session bug. It’s a structural feature of how helpful systems behave under loose constraints.

The countermeasure. Move scope discipline into the system prompt where the agent enforces it against the operator. My harness uses a “focus lock” pattern that names the sanctioned work and explicitly instructs the agent to push back when I drift, not as a one-time nudge but on every drift attempt. There’s a release valve (a tangent-capture file so ideas aren’t lost) and an explicit override phrase for genuine re-prioritization.

The key move is that the agent is asked to be the disciplinarian. Not me policing myself. The agent has the consistency to push back ten times in a row where I’d cave after two.

Principle

The agent shouldn't only follow instructions. It should also enforce them. Session-level commitments and operator constraints are constraints to defend against the operator's own drift, not just suggestions to interpret.

What the three have in common

All three failures share a shape. The agent has more information about its own actions than it surfaces. Subagents know what they actually changed, but they report what they intended. Sessions know what they actually committed, but they trust their own bookkeeping. Long sessions know they’ve drifted, but they don’t flag it.

The countermeasures all push observation outward. To the controller’s git show --stat. To the index itself. To system-prompt guardrails that the agent is asked to apply to its own behavior.

The deeper tension: agents are most useful when trusted with autonomy, and least reliable precisely because of that autonomy. Strip the autonomy and you lose the value proposition. Trust it blindly and you ship contaminated commits and half-finished tasks.

What I find genuinely interesting is the in-between. Agents that have autonomy in execution but produce auditable, verifiable claims at every trust boundary. Subagents that report what they did with the diff, not as a claim about the diff. Sessions that watch their own index. Long-running agents that flag their own drift before the operator has to.

I don’t know what the right shape of that is. I have opinions, but they’re operator opinions, not researcher opinions. I’d like to find out.

What I’d want to work on

The version of Claude Code I’d want to build is the one where the failure modes above don’t require an operator with scars to work around them. Where the controller verification of subagent claims is structural, not voluntary. Where parallel sessions can’t silently contaminate each other’s commits. Where scope drift is observable before it ships.

I have a year of pressure-tested ideas about how each of these could work. I’d rather build them at Anthropic than keep patching them in my own harness alone.

Anthony Rasch