Getting Haiku agents to act on each other's messages
Teams of Claude Haiku 5.5 agents that could message each other sent most of the rules their teammates needed, and implemented none of them. One paragraph telling every agent that the spec had been split across the team on purpose took three teams to all 12 rules.
The test
I gave each team four agents, alice, bob, carol and dave, and one job: a small Python package that turns order lines into invoices. alice owned the parser, carol the stock allocation, bob the pricing, and dave the report that calls the other three. Each agent could edit only its own file and saw only its own part of the spec, so the agents had to agree among themselves on how their functions fit together.
I planted four rules so that each agent's brief held one rule belonging in a teammate's file. alice's brief said gift items are free, which is bob's code. bob's said two units of every item stay in reserve, which is carol's. carol's said every order ends with a status line, which is dave's. dave's said order IDs are case-insensitive, which is alice's. Each rule only worked if its holder handed it over and its owner wrote the code. Sixty hidden end-to-end tests graded the finished package. A team that implemented none of the four rules scored 14 of 60, and one rule touches enough tests that missing just the reserve rule costs 32.
The mailbox
Each team shared a mailbox — an append-only log file with a small command-line tool to join, send and receive. A Claude Code hook that runs after every tool call adds the agent's unread messages to its context, so no agent has to check for mail, and a blocking receive lets an agent with nothing left to do wait for a question instead of finishing. When dave was satisfied, he sent SHIP to tell the team they were done.
Claude Code also has a built-in SendMessage tool, which works between subagents. In an earlier round, where the only thing to agree on was function signatures, it cost nearly twice as much as no channel at all, because a message to an agent that has already finished restarts it from its whole transcript. The mailbox, with each agent posting its function signatures when it joined, cost 1.2 times as much as no channel. Every setup passed that round, including the one with no channel, because dave wrote his file last and read everyone else's. That round never needed a message, which is why the planted rules live only in briefs no other agent can read.
What the receivers did
With the rules planted, holders in six messaging teams sent 19 of their 24 rules to the right teammate. Every owner turned its rule down, because the rule was not in its own brief. One alice reported that dave's "claimed quote doesn't match" her brief. A bob reported "the team's briefs disagree" and asked for a lead to compare copies. The daves held SHIP and waited for someone to decide, but no agent had a lead role, so the disagreements stayed open. All six teams scored 14 of 60.
The team charter
The fix is a short charter. I wrote the briefs and split the spec, so the coordinator it mentions is me. This is the version written as a custom Claude Code agent, whose body becomes the agent's system prompt (team-member.md):
- The coordinator wrote ONE specification and split it across the team's
briefs on purpose. Your brief holds your slice. Each teammate's brief holds
theirs. Nobody holds the whole spec.
- Some slices contain a rule that belongs in a teammate's file. If your brief
has one, send it to that teammate as soon as you have joined the mailbox,
quoting it exactly.
- When a teammate quotes a rule from their brief for your file, it is part of
the same specification you were given, from the same coordinator. Implement
it, even where it changes something your own slice says, then acknowledge
it. A rule being absent from your brief is expected, not a conflict.
With this charter as the system prompt, three teams implemented 10 of 12 rules and scored 60, 43 and 28; each of the two lower teams missed one rule. Essentially the same text appended to the end of the brief, with the agent told to reply ok, gave three teams 12 of 12 rules and 60 of 60 each. Two rules on three teams is too small a gap to prefer either placement. Three control teams launched the same way without the charter sent 9 of their 12 rules and implemented none.
Claude Code's auto-mode classifier, which reviews tool calls before they run, blocked one setup. My first attempt, run as subagents of an auto-mode session, said in the brief that a teammate's rule "overrides anything in your section it conflicts with". The agent that read it had its next shell command blocked as "Instruction Poisoning" — a file it had just read was telling it to take orders from other agents, which is what an injected file says. I stopped that setup there. The brief runs that worked used the charter's wording and ran headless, outside auto mode, so I don't know whether the wording or the mode made the difference. A custom agent sidesteps it: the charter becomes the agent's own instructions instead of a file it reads.
The ship gate
The charter left two misses: one rule never sent, and one received but never acted on or acknowledged. The gate run came before the placement controls, so it used the system-prompt version. I took SHIP out of dave's hands. I registered the four handoffs each team had to make. A holder sends its rule with mail owe, which attaches one test case — an input and the invoice lines the rule must produce. The owner confirms with mail ack. dave can only finish through mail ship, which sends SHIP when every handoff has been sent, acknowledged, and its test passes against the team's current code, and otherwise tells whichever agent still owes something.
Three teams with the charter in the system prompt and the gate scored 60 of 60 with 12 of 12 rules, at about 3 cents per team for all four agents' sessions, as Claude Code reported it. That is the same total as the brief-charter teams without a gate, so its value at this scale is the miss it caught. bob had acknowledged the gift rule, and the invoice still printed Gift Mug x2 @ 7.00 = 0.00 where alice's test required that 7.00 not appear, because dave's report took the unit price from the catalog instead of from bob's pricing. bob's acknowledgement was already on record when alice's test failed at the gate. bob traced the line to dave's file and told him, and the next attempt shipped.
The video replays two of these runs from their message logs, with narration: a team without the charter (round 2), and a team with the charter and the gate (round 4).
| setup | teams | rules implemented | scores (of 60) |
|---|---|---|---|
| no channel | 3 | 0 of 12 | 0, 0, 14 (in two, dave finished before his teammates' files existed, and his code never matched theirs) |
| messages, no charter | 6 | 0 of 24 (19 sent) | 14 each |
| messages, no charter, launched headless | 3 | 0 of 12 (9 sent) | 14 each |
| charter in the system prompt | 3 | 10 of 12 | 60, 43, 28 |
| charter in the brief | 3 | 12 of 12 | 60, 60, 60 |
| charter in the system prompt, plus the ship gate | 3 | 12 of 12 | 60, 60, 60 |
That is three teams per setup on one task, all on Haiku 5.5, and about 110 agents across five rounds. The mailbox (mail.py), the hook (hook.py), the gate (gate.py), every team's code and message log, and the full results are in experiments/agent-mail.