Mythical Agent Month: coordination surface is what a crowd costs
Bob's relay is five agents, not one, and he is explicit that staging costs something. The seats have to hand work to each other. Each new seat pays "startup times are high… 10, 15 seconds to even start up… then it's got to figure out its whole context all over again" (C10). The whole stack is still worth it only because the margin survives: "factor of four, factor of five improvement… and very high quality" (C5). This island is the sizing question underneath that. How many agents before the handoffs eat the margin?
The answer is fifty years old. The seventies-canon brief records Brooks's law and the surgical team from The Mythical Man-Month (1975), and argues both generalise to agent fleets: adding agents to a late project adds coordination surface, not progress. The arithmetic is just the count of distinct pairs in a set of n, n(n-1)/2. It grows quadratically, while your throughput gain grows linearly at best. Bob's own fleet note points straight at the tension: "you could have three coders running at the same time. And my little laptop can support a lot more than three" (C10). The machine can host more agents than the work can absorb, and that gap is where fleets go wrong.
A grounding note, because this island is about honest cost
The roster line for this island carries the phrase "communication overhead like crazy" as a C9 quote, and for a while it reached the roster without passing through the concept ledger — the only place an island may quote the conversation from (CONTEXT.md); claims that go beyond the conversation cite a brief in research/ instead. The gap is now closed at the source rather than by demoting the words: C9 carries the sentence verbatim with its locus (auto-captions ~00:17:52–00:18:01, SRT cues 903–907, RCM's turn), so every quote on this island is ledger-backed, and re-greppable in source/ once the transcript is regenerated. Re-grep the ledger rather than trusting this sentence.
The re-grep has two halves, and a clone can only run one of them unaided. The ledger is tracked, so its half reproduces anywhere. source/transcript.txt is deliberately gitignored — it is the whole of someone else's recorded conversation, cited here and not redistributed — so a fresh clone has nothing to grep until it runs the two regeneration commands in source/README.md. The third line below is written to say so rather than to fail:
bash -c 'grep -qF "communication overhead like crazy" ../../docs/01-CONCEPT-LEDGER.md' # exit 0
bash -c 'grep -qF "Diet Coke" ../../docs/01-CONCEPT-LEDGER.md' # exit 1
bash -c '[ -f ../../source/transcript.txt ] || exit 0; grep -qF "communication overhead like crazy" ../../source/transcript.txt' # exit 0
The second fails at the ledger grep, which is what a roster quote with no ledger entry looks like — so the first is not a grep that always says yes. That failure is the whole of what the control proves in a fresh clone. "Diet Coke" is also in the recording, which is what makes it the right control rather than a random string, but confirming that half needs the regenerated transcript like everything else below.
The third is the transcript half, and its guard is the honest part rather than a way around the check. With no transcript present it skips and reports 0 having graded nothing; with one present the grep -qF runs and a phrase the source does not carry returns 1. So its 0 means "checked, or not there to check", and ls ../../source/transcript.txt is what tells you which of the two you got. The guard stays silent by choice, not by force. An announcement written with bash's builtin echo does take SIGPIPE when this pack's own closed-stream harness closes stderr — measured, 141, and 141 names no verdict — because the builtin makes the shell itself the writer. An external /bin/echo does not: the child dies and the guard's own exit 0 stands, so a loud guard is reachable. Silence is preferred here because the skip is not news at the point it happens, and ls ../../source/transcript.txt distinguishes the two zeros afterwards.
Where this island sits
- The five-seat relay itself is
seat-relay: specifier, coder, cleaner, hardener, QA, the batons between them, the born-do-die lifecycle (C9, C10). That island runs the relay. This island owns FLEET SIZE and its coordination cost: how many agents, in what topology, before the handoffs stop paying. - The continuous agent-versus-human productivity ledger is
margin-ledger: per-story gated wall clock against an honest human baseline, defending the 2–4x band and the 1x floor (C5). That island prices the gate stack as stories ship. This one prices the fleet before you spawn it. They meet at one number. An oversized fleet shows up in the margin ledger as a thinning margin, and this island is where you find out which agent caused it. - Per-step model picks are a Forge concern:
model-routing. Cheapening a seat's model changes what an agent costs per minute. It never changes how many communication paths a fleet has. Size first here, then route each seat there. - Same-machine isolation mechanics belong to
worktree-fleet. Once this island says "three coders", giving each its own worktree is that island's job. So is its rule that worktree artifacts are inadmissible as gate evidence until re-derived from a fresh clone.
Two shapes of work, and only one of them divides
| Shape | Example | What n does to it |
|---|---|---|
| Partitionable | five independent endpoints, five test files, five unrelated bug fixes | divides by n; this is the whole reason to run a fleet |
| Needs shared context | one domain model three seats must agree on, an interface being designed while it is being used | does not divide at all, and every agent holding it is one more that can disagree |
The second row is why "just add another agent" fails. Work needing shared context is not made shorter by spreading it, and spreading it manufactures the pairs that then have to reconcile. Bob's compartmentalisation argument is the same claim at module scale: "If you load up a module with every bit of stuff under the of under the sun [sic], the poor agent is going to wonder, 'What the heck am I doing in here?'" (C15). A fleet with no partition is one loaded module wearing five hats.
Why a serial relay beats a crowd
Compare the two topologies at the same headcount, using nothing but edge counts:
- A crowd of n peers on shared-context work is a mesh:
n(n-1)/2paths. Five peers is ten reconciliations, and every one of them is a chance to disagree. - A relay of n seats is a chain:
n-1handoffs. Five seats is four batons. Each baton is a typed artifact (Gherkin, code, CRAP report, mutation log, QA verdict), not a conversation, so the coordination cost per edge is bounded by what the artifact contains rather than by how long two agents argue (C9).
Five seats: 4 edges. Five peers: 10 edges. Same headcount, two and a half times the surface, and the relay's edges are the cheap kind. That is the whole case for staging specialists in series. It is also why the relay can afford five agents on work that would drown five peers.
The relay's costs are still real and still additive, and Bob says so about the very relay he runs: "there's communication overhead like crazy in that. and yet it's still faster by a large token than a human" (C9). Each seat pays the fixed startup and context-rebuild toll (C10), and the stack must keep the margin above the human floor or "you've lost the game" (C5). Serial staging buys a better edge count; it does not buy immunity.
The sizing model
elapsed(n) = S * ((1 - p) + p/n) + h * n(n-1)/2
\___ partition term ___/ \_ pair term _/
| Symbol | Meaning | Where the number comes from |
|---|---|---|
S |
serial minutes: the work if one agent did all of it | historical actuals, or the same honest-baseline discipline margin-ledger demands |
p |
partitionable share, 0 to 1 | your read of the story; the estimate most likely to be wrong, and the one to write down before you are attached to an answer |
h |
handoff minutes per communication path | measure it once: how long two agents' work actually took to reconcile |
n |
fleet size | the thing being decided |
The partition term divides only the partitionable share. Shared-context work does not divide at all. The pair term is Brooks's. elapsed is convex in n: the first term falls at a decreasing rate, the second rises at an increasing one. So the first fleet size whose successor costs at least as much is the global optimum, and no search past it is needed. That "at least as much" is the script's own rule (crossover() triggers on >=): an exact tie counts as not paying, the same way the exit table does.
One command
scripts/fleet-cost.py reads a four-key plan file and answers one question: does the last agent in the proposed fleet still reduce elapsed time?
python3 scripts/fleet-cost.py <plan-file>
python3 scripts/fleet-cost.py --help # -h is accepted as the same flag
| Exit | Meaning |
|---|---|
0 |
the proposed fleet pays — the last agent reduces elapsed time. Also --help (or its accepted synonym -h) on a working stdout. |
1 |
Brooks — the last agent does not reduce elapsed time. A tie counts as not paying: it bought nothing and added a path. |
2 |
not a verdict — usage error, a path that is not a regular file (directory, FIFO, /dev/zero and other character devices), a plan over 64 KiB, unreadable file, non-UTF-8, malformed line, unknown key, duplicate key, missing key, out-of-range value, dead output stream, internal failure. |
Three codes. The script emits no others, and every input reaches one of them. The path is stated and refused unless it is a regular file, and the read is capped at 64 KiB, so no input class runs forever instead of returning a code (/dev/zero on an uncapped read is a hung CI job, not a refusal). All arithmetic runs in exact rationals (fractions.Fraction), so no verdict is ever decided by a float. Every printed minute figure is rounded for display, and when rounding would hide a non-zero difference the exact ratio is printed beside it.
Red and green, both run
python3 scripts/fleet-cost.py scripts/fixtures/oversized-fleet.txt # exit 1
python3 scripts/fleet-cost.py scripts/fixtures/right-sized-fleet.txt # exit 0
The two fixtures carry identical values for serial_minutes, partitionable and handoff_minutes, and differ only in fleet: 5 versus 2. Their comment lines differ in wording and are not parsed. So the red run fails for the reason claimed, the fifth agent's marginal cost, and not for malformed input. The red run prints crossover fleet 2 is the last size that still pays and elapsed fleet 4: 414.00m -> fleet 5: 499.60m (delta +85.60m).
The pack's own acceptance ritual agrees (known-dirty-fixture):
bash ../known-dirty-fixture/scripts/prove-gate.sh scripts/fixtures/oversized-fleet.txt \
scripts/fixtures/right-sized-fleet.txt -- python3 scripts/fleet-cost.py # exit 0, ACCEPTED
A gate that only ever saw the input its author imagined is not hardened. So the same violation was re-run through the encodings a real editor or CI produces: a UTF-8 BOM plus CRLF line endings on the dirty plan. It still lands on the same verdict:
python3 scripts/fleet-cost.py scripts/fixtures/dirty-bom-crlf.txt # exit 1
A closed hole, kept as a fixture
str.splitlines() breaks on eight separators beyond LF/CRLF/CR: \x0b, \x0c, \x1c, \x1d, \x1e, U+0085 (NEL), U+2028 and U+2029. While the parser used it, a line whose first character was # was not ignored. Everything after an embedded separator on that line was read as live configuration. The worst shape for this island is handoff_minutes 0 smuggled inside a comment. It zeroes the Brooks pair term, the one number the island exists to charge, so every fleet size then reports PAYS. U+2028 is what macOS TextEdit and Notes emit for a soft break, and what survives a paste out of a PDF; \x0c is a routine Emacs page break.
The parser now splits on \r\n|\r|\n only, and tokenises on spaces and tabs only. A comment line is therefore ignored whole, and every other whitespace character stays inside its token, where it is refused as an unknown key or a malformed value. The fixture is a three-key plan whose comment carries a U+2028 ahead of handoff_minutes 0:
python3 scripts/fleet-cost.py scripts/fixtures/comment-smuggle-u2028.txt # exit 2, missing required key(s): handoff_minutes
The byte-identical control, with an ordinary space in place of the U+2028, returns that same code. That is the point: the separator now changes nothing.
Hostile inputs were swept separately. Every one returned 2, never a verdict code: nan, inf, 4.8e2, -25, 3/5, 4_80, a 4400-digit value, a duplicate key, a missing key, Serial_Minutes (case variant, refused rather than silently re-routed), a trailing # comment after a value, a # line carrying any of the eight exotic separators above ahead of a live key, an NBSP used as the key/value separator, /dev/zero, a FIFO with no writer, a 64 KiB+1 plan, fleet 0, fleet 99999, fleet 5.0, partitionable 2, an empty file, a UTF-16 file, a missing file, a directory, no arguments, two arguments. Recompute them rather than trusting this sentence.
A dead output stream is swept the same way, in both of its shapes: a closed-reader pipe, and an fd closed before the process starts. In the second, CPython sets the stream to None and print() becomes a silent no-op, so an unguarded script would report a verdict into an empty log:
python3 scripts/fleet-cost.py scripts/fixtures/right-sized-fleet.txt >&- # exit 2, not 0
python3 scripts/fleet-cost.py scripts/fixtures/oversized-fleet.txt >&- # exit 2, not 1
python3 scripts/fleet-cost.py --help >&- # exit 2, not 0
python3 scripts/fleet-cost.py scripts/fixtures/right-sized-fleet.txt 2>&- # exit 2
Known limit, disclosed rather than hidden
The model charges an all-pairs mesh. A hub-and-spoke fleet (one lead agent, k workers who never talk to each other) really has k paths, not k(k+1)/2, so this script over-charges it. The failure direction is over-strict: it refuses a plan the real topology would still pay for, never a false green. It has a captured run:
python3 scripts/fleet-cost.py scripts/fixtures/hub-topology-blindspot.txt # exit 1
At S=480, p=0.9, h=8, fleet=6 the mesh model charges 15 paths and returns 1. Under hub-and-spoke the same fleet has 5 paths and would still be improving. Until a topology field exists, read a 1 on a genuinely hub-shaped fleet as "check the topology", not "cut an agent".
Enforced vs advisory
enforced— the arithmetic and the refusals.fleet-cost.pyexits1on a fleet size whose last agent does not reduce exact-rational elapsed time, and2on every input it cannot parse into exactly the four named keys. Its own red/green acceptance is enforced byprove-gate.sh(run above, exit 0). This island's shape is enforced byvalidate-island.py.advisory— every input number.S,p, andhare estimates, and the script cannot check any of them. A wrongpproduces a confident wrong verdict. Write the three numbers down with their source before running it, exactly asmargin-ledgerdemands of a human baseline, or the output is a guess wearing an exit code.enforced— the ledger half of the provenance re-grep.docs/01-CONCEPT-LEDGER.mdships in the repository, so the first two greps above reproduce in any clone, and the control still fails there for the reason claimed.advisory— the transcript half, in a clone that has not regeneratedsource/transcript.txt. The guard turns the absent file into a skip, so its0certifies nothing about the source. Run the two commands insource/README.mdand that same line becomes enforced. Provenance breadth stays advisory in both worlds: no checker sweeps every quote in the pack for the same double-sourcing.advisory— the topology judgement (mesh versus chain versus hub), the choice to stage serially rather than fan out, and everything about which agent to cut. No checker sees your fleet's shape.
Done when
- The story's partitionable share is written down as a number with its reasoning, before the fleet size is chosen.
-
python3 scripts/fleet-cost.py <plan>exits0for the fleet you intend to spawn, and the plan file is kept beside the run. - Shared-context work is named explicitly and assigned to one agent or one relay chain, never split across peers.
- The crossover size is recorded, so the next person adding an agent knows what they are spending.
- If the fleet is hub-shaped, the limit above is acknowledged in the run notes rather than silently ignored.
No authority without evidence. Paths grow as n(n-1)/2 and progress does not — count the edges before you spawn the crowd.