Two Agents Split a Receipt. It's Wrong by One Cent.

Two subagents build the two halves of a receipt module in total isolation. One half won't compile. The fixed version compiles, runs, looks correct — and is silently wrong by a cent.

Two posts in this series measured what an isolation boundary costs and what’s inside that cost. Neither one produced a single bug. That wasn’t skill — both experiments were read-only and deterministic by design, so every arm returned the identical answer no matter how many agents touched it. MAST, the largest published taxonomy of multi-agent failure, names 14 distinct failure modes across three categories.1 My first two experiments were structurally incapable of triggering any of them, because none of the 14 has anything to do with an agent counting words correctly.

This one does. I gave two isolated subagents the two halves of one deliverable — the shape of Cognition’s now-familiar Flappy Bird anecdote, a bird drawn in one style glued to a background drawn in another2 — and let them write real files with no channel between them. One half didn’t compile. The fixed version compiled, ran, and produced a receipt that looked completely reasonable. It was silently wrong by a cent.

MAST, briefly

MAST was built from 150 hand-annotated multi-agent traces at inter-annotator agreement κ = 0.88, then extended to 1,600+ traces across seven frameworks — ChatDev, MetaGPT, HyperAgent, AppWorld, AG2, Magentic-One, OpenManus.1 It sorts every failure it found into three categories: system design issues (5 modes — a single agent disobeying its own spec, repeating steps, losing history), inter-agent misalignment (6 modes — agents talking past each other, resetting a conversation, ignoring input), and task verification (3 modes — nobody checking the work, or checking it wrong).

The middle category is the one that matters here, and it’s the one my first two posts couldn’t reach: it requires two agents to actually interact. A read-only fan-out where every shard reports back an independent number has no dialogue to misalign.

The experiment

One deliverable, split down an interface no one wrote down: a receipt-printing module for a point-of-sale system, needing a line-item formatter and a total/tax calculator that both consume the same cart. Two fresh general-purpose subagents, launched from a single message so neither could see the other start, each given only its own half of the spec and told the module was “brand new, no existing code to match.”3

flowchart TB
O["Orchestrator"]
O -->|"half-spec: line-item formatter"| A["Agent A · general-purpose<br/>writes lineitems.ts<br/>49,262 tokens"]
O -->|"half-spec: total + tax calculator"| B["Agent B · general-purpose<br/>writes total.ts<br/>49,594 tokens"]
A -.->|"no channel — 0 messages exchanged"| B
A --> I["Integration attempt"]
B --> I
I -->|"tsc --strict"| F1["FAIL — TS2306<br/>lineitems.ts is not a module"]
F1 --> FIX["add two export keywords"]
FIX --> R["clean compile, runs,<br/>looks correct on sample data"]
R -->|"adversarial input:<br/>3 items @ $0.125 each"| F2["line items read $0.39,<br/>total reads $0.38"]
Neither worker agent has a channel to the other — the orchestrator is the only node that ever sees both files. Both failures below were found at the one arrow labeled 'integration attempt.'

What converged

The first surprise is what the two agents agreed on without ever comparing notes. Agent A’s LineItem and Agent B’s PurchasedItem are structurally identical: name: string, quantity: number, unitPrice: number, both in whole dollars. Different type name, same three fields, same types, same units. For a domain this well-trodden, two independent runs landed on the same “obvious” shape — implicit decisions don’t conflict uniformly across a task, they cluster where there’s no obvious convention to fall back on. Money formatting and module boundaries turned out to be exactly that kind of place.

Failure 1: it doesn’t compile

Agent A’s file, in full save for comments:

interface LineItem {
  name: string;
  quantity: number;
  unitPrice: number;
}

function formatLineItems(items: LineItem[]): string[] {
  // ...
}

Agent B’s:

export interface PurchasedItem {
  name: string;
  unitPrice: number;
  quantity: number;
}

export function formatTotal(items: PurchasedItem[], taxRate: number): string {
  // ...
}

Both agents were told to keep their file “self-contained — no imports from other project files.” Agent B read that as a constraint on what it consumes. Agent A read it as a constraint on what it produces, and exported nothing at all. Importing formatLineItems anywhere else fails before a single line of logic runs:

integration.ts:1:43 - error TS2306: File 'lineitems.ts' is not a module.

Real error, from this repository’s own TypeScript compiler (v6.0.3) run in strict mode against both files unmodified.3 Neither agent violated its instructions. Assembled, the module doesn’t exist.

Failure 2: it compiles, runs, and lies by a cent

Two export keywords fix the compile error. What’s left is a structurally sound module — and the two functions still disagree on something neither of them wrote down: where to round.

Agent A rounds per line, for display:

const lineTotal = formatCurrency(item.quantity * item.unitPrice); // toFixed(2), per line

Agent B sums the raw, unrounded values first and rounds once, at the very end:

const subtotal = items.reduce((sum, i) => sum + i.unitPrice * i.quantity, 0);
// ...formatMoney(subtotal) — toFixed(2) happens once, after the sum

Run both against the original sample cart — $9.00 espresso beans, $4.50 oat milk — and the receipt looks perfect: line items sum to $22.50, subtotal reads $22.50, everything reconciles. If I’d stopped there and called the module verified, that’s MAST’s FM-3.3, incorrect verification1 in miniature: I checked the output, on data too clean to expose the disagreement.

Three items at $0.125 each is not clean:

$ node -e "..."
per-line displayed: [ '0.13', '0.13', '0.13' ]  → a customer adding by eye gets $0.39
raw subtotal 0.375 → displayed subtotal            $0.38

The line items say $0.39. The subtotal says $0.38. Both numbers are the honest output of correct, tested-in-isolation code. Neither agent is wrong on its own terms. Assembled, the receipt doesn’t add up — by exactly the kind of small, plausible, easy-to-miss margin that erodes trust in a payments system.

Where this lands in the taxonomy

Where this experiment’s bugs land in the MAST taxonomyMAST · 14 FAILURE MODES · 3 CATEGORIESSYSTEM DESIGN (5)INTER-AGENT MISALIGNMENT (6)TASK VERIFICATION (3)1.1 Disobey task specification1.2 Disobey role specification1.3 Step repetition1.4 Loss of conversation history1.5 Unaware of termination conditions2.1 Conversation reset2.2 Fail to ask for clarification2.3 Task derailment2.4 Information withholding2.5 Ignored other agent’s input2.6 Reasoning-action mismatchblocked here — 0-byte transcripts,0 sidechain records (previous post)3.1 Premature termination3.2 No or incomplete verification3.3 Incorrect verificationfound here — lineitems.ts exported nothing (Failure 1)found here — nobody ran the integration until asked (Failure 2)found here — passed on clean data, wrong on real data (Failure 2)
This experiment's two findings, placed against all 14 MAST failure modes. Inter-Agent Misalignment needs a channel between agents to misfire on. Two parallel subagents — measured in the previous post at 0-byte transcript files and 0 sidechain records — don't have one, so none of its 6 modes can fire here. Both bugs land in the other two categories instead.

Isolation doesn’t stop conflicting decisions — Cognition already showed that without a shred of MAST’s vocabulary. What isolation does is move where the failure can be observed. Six of MAST’s 14 modes live in a category that presupposes two agents talking to each other mid-task. Parallel subagents on this harness don’t talk to each other at all, mid-task or otherwise — the previous post in this series measured that directly: 0-byte transcript files, 0 sidechain records, for eight subagents that ran side by side. There is no wire for FM-2.1 through FM-2.6 to travel on. That’s not a safety property. It’s a redirection: every failure that inter-agent misalignment would have caused instead shows up as an agent quietly over- or under-interpreting a spec it never got to check against anyone else’s (FC1), or as a verification step that either didn’t happen or didn’t happen against the right data (FC3). Both of the bugs above are exactly that — one in each of the two categories isolation didn’t block.

What the verification pass costs

Show data table
tokens
Read-only task · post 1 baseline34.2K
Write task · lineitems.ts49.3K
Write task · total.ts49.6K
Token cost of a read-only shard (from the first post in this series) against the two write shards measured here. Writing ~50 lines of a real module and a short report cost about 1.45x a single wc -w call — before anyone paid for the integration attempt that found either bug.

98,856 tokens produced two files that, delivered as-is, did not compile together. Neither number includes the integration step: reading both files, writing the driver, running tsc, then — the part that actually mattered — thinking of an input adversarial enough to expose the rounding disagreement. That last step is not a boundary crossing the harness bills you for. It is judgment, and nothing about spawning two agents in parallel supplies it for free.

Type-level verification is nearly free by comparison: tsc --strict caught Failure 1 in about a second, no model involved, the moment a driver file tried to import both modules. That’s the cheap half of MAST’s task-verification category, and it’s the half most CI pipelines already run. Failure 2 is the expensive half — no type checker flags it, because both functions are individually well-typed and individually correct. It only surfaces if someone runs the combined system against a value neither agent happened to pick, which is precisely the class of check that Anthropic’s own guidance singles out a verifier subagent for: a reader with no access to either writer’s reasoning, whose only job is to try to break what they built.4

Trade-offs

Convergence is not proof of compatibility. The item shape matched by coincidence of domain familiarity, not by design, and that made it easy to assume the rest matched too. It didn’t.

Cheap verification and expensive verification catch different bugs. A compiler catches structural mismatches for the cost of a CI step. It does not catch two internally-correct rounding strategies disagreeing by a cent — that requires someone to go looking for adversarial input, which is exactly the work an isolated writer has no reason to do and an isolated reviewer has every reason to do.

One task, one pair of agents, one model generation. The subagent model here was left at the harness default rather than pinned to Haiku 4.5 as in the read-only baseline, so the token chart above compares harness and agent type cleanly but not model-for-model. The qualitative shape — a loud compile failure plus a silent semantic one — is the kind of result I’d expect to generalize across models; the exact one-cent gap is a property of this specific input, not a universal constant.

The taxonomy’s shape is itself informative. Six of MAST’s 14 modes require a conversation this harness’s parallel subagents structurally don’t have. That’s worth knowing before reaching for a mitigation built for a failure category that can’t occur on the system you’re running.

Takeaways

  • Isolation relocates conflicting decisions; it doesn’t remove them. Two agents with zero shared context and zero communication still produced two artifacts that didn’t compose — a compile-time failure and a silent one-cent semantic disagreement.
  • Six of MAST’s 14 failure modes need a channel this harness’s subagents don’t have. Inter-Agent Misalignment presupposes agents that talk mid-task. Parallel subagents here have 0-byte transcripts and 0 sidechain records — there is no wire for FM-2.1 through FM-2.6 to travel on, so all of the risk this experiment found landed in System Design and Task Verification instead.
  • A passing integration test is not verification — it’s verification against whatever data you happened to try. The receipt looked perfect on the original sample cart. It broke on the first adversarial input I tried on purpose.
  • Type checking is the free 10% of verification. It caught the compile failure instantly, for the price of a CI step. The semantic failure needed a human — or a dedicated verifier agent — to go looking for it.
  • Convergence on the “obvious” parts of a shared domain is real, and it’s a trap. Two agents independently choosing the same field names for a receipt item is a fact about how well-trodden that shape is, not a fact about whether the rest of the contract holds.

References

Footnotes

  1. Cemri, Pan, Yang et al., Why Do Multi-Agent LLM Systems Fail? (arXiv:2503.13657) — the MAST taxonomy: 14 failure modes across three categories (system design issues, inter-agent misalignment, task verification), developed from 150 annotated traces at inter-annotator κ = 0.88 and extended to 1,600+ traces across seven frameworks (ChatDev, MetaGPT, HyperAgent, AppWorld, AG2, Magentic-One, OpenManus). ↩ ↩2 ↩3

  2. Cognition, Don’t Build Multi-Agents — Principle 2, “Actions carry implicit decisions, and conflicting decisions carry bad results,” illustrated with the Flappy Bird clone whose background and character were drawn in mismatched styles by two agents that never compared notes. ↩

  3. Methodology. Measured 2026-09-20 against this blog’s own repository. Two general-purpose subagents were launched in the same message from the Agent tool, each given only its own half of the receipt-module spec and no visibility into the other’s file, prompt, or output; the harness reported 49,262 tokens / 2 tool calls / 13,915 ms for the line-item agent and 49,594 tokens / 2 tool calls / 15,411 ms for the total agent. Subagent model was left at the harness default (not pinned), unlike the Haiku-4.5-pinned baseline this post compares against. Both generated files were typechecked with this repository’s own TypeScript compiler, v6.0.3, in --strict mode (moduleResolution: bundler); the compile failure (TS2306) was reproduced by importing both files, unmodified, from a third driver file. The rounding disagreement was reproduced with Node v24.14.1 by comparing per-line-rounded display values against a once-at-the-end-rounded subtotal for three items priced at $0.125 each. ↩ ↩2

  4. Anthropic, When to use multi-agent systems (and when not to) — the verification-subagent pattern, run by a reader with no access to the writer’s context, that the guidance reports works consistently across domains. ↩