Swarm — refuted, and what replaced it
Date: 2026-08-22
Status: closed. This spec exists to close a question, not to open work.
Roadmap: replaces spec 4 of 2026-08-21-architecture-roadmap.md
Supersedes: the swarm section of that roadmap
Why this document exists instead of the spec that was planned
The roadmap's spec 4 proposed per-domain LLM agents (ingest, rerank, knowledge,
provenance, citations) coordinated by a user-interaction supervisor, defined in
a swarm.toml. It was the largest of the five and the one the whole effort
started from — the opening request of this project was for exactly that
hierarchy.
That roadmap also wrote its own exit condition:
Measure before building. Spec 1 alone may substantially resolve the confusion this spec exists to fix. […] Re-check whether the confusion persists after spec 1 lands before committing to this one — it is the expensive spec and the one that spends determinism.
The measurement was run. The swarm lost.
Writing the spec anyway would specify a mechanism this repo has already disproved on its own hardware. Writing nothing would leave a roadmap entry that reads as pending work, and the question would be reopened by the next person — or the next model — who reads it. So the spec slot is spent on the refutation.
The measurement
Three architectures, the same 15 realistic user turns, the same model
(gemma4:e2b-it-q4_K_M), three runs each.
| architecture | correct | latency |
|---|---|---|
| Routed — 4 intent tools, deterministic dispatch | 13/15 | 1.3s |
| Flat — one namespace (37 tools at the time of measurement) | 8/15 | 1.3s |
| Swarm — supervisor LLM + namespace subagent LLM | 7.3/15 | 2.8s |
The swarm performed worse than doing nothing, at twice the latency.
The mechanism is not subtle. A second model call is a second chance to be wrong, and a namespace miss is unrecoverable: once the supervisor routes "which arm of my sweep won?" to the knowledge agent, no amount of competence inside that agent recovers the turn. Errors compound rather than cancel. Two 80%- accurate stages in series are a 64%-accurate pipeline, and neither stage here was at 80%.
This is the determinism objection from the roadmap, now with numbers instead of an opinion.
What was built instead
Two things, both shipped:
Deterministic routers (src/attestation/mcp/ask.py). Four intent tools —
feed.ask, runs.ask, kg.ask, sym.ask — that map a question to a tool by
rule table, with no model call and no database read. They return a Pydantic
Answer, so MCP emits a real outputSchema. This is the 13/15 arm.
Per-domain agent surfaces (src/attestation/mcp/__init__.py). The
AGENT_SURFACES table plus ATTEST_TOOLS restricts tool registration to one
namespace. Measured 2026-08-22 with ATTEST_EXPAND=1: feed 22, symbolic 9,
provenance 8, knowledge 8 (kg's 7 plus feed.search), against 46
unrestricted. Without expansion each surface shows 2 — its ask router and one
companion — which is the progressive-disclosure default, not the surface size.
Four entries exist in ~/.hermes/config.yaml today.
(Note that a surface count is not a namespace count: kg.* is 7 tools, but the
knowledge surface serves 8 because it also carries feed.search. CLAUDE.md
said 37 and 19/7/7/8 when this was written -- correct once, silently wrong
later, and quoted into a draft of this spec before anyone measured. It has since
been corrected to 46 and is now pinned by
test_architecture.py::test_claude_md_tool_counts_match_the_live_surface, which
asserts the per-namespace split as well as the total: a total can stay right
while two namespaces drift in opposite directions. Re-measure rather than
quoting either number.)
Note what the second one is: the roadmap's deliverable, by a different mechanism. Spec 4 wanted per-domain agents. There are four per-domain agents. What was rejected is not the decomposition — it is having a model choose between them at runtime.
That distinction is the whole finding, and it generalises:
Separate agents help when a person chooses which to talk to. They hurt when a model chooses at runtime.
So the split is by session, selected at launch, and enforced at tool registration — where it costs one environment variable and cannot be gotten wrong mid-conversation.
Two findings worth keeping
Both moved routing from 9.7 to 13, and both are encoded as tests in
tests/test_ask_routing.py rather than left as prose.
No catch-all destination. An early routed version had a doctor tool for
"diagnose the system". It became a magnet: three of the four remaining misses
went to it. An ambiguous question must return options and ask back, never pick
a default. A catch-all does not absorb the hard cases, it attracts the
ordinary ones.
Descriptions must contain the words users actually say. "which topics are most central or most read about" catches a turn that "what is central" does not. This is unglamorous and it is worth more than architecture.
What survives of the swarm idea
One narrow case, deliberately left unbuilt.
The measurement tested routing — one question, one tool. It did not test orchestration — a multi-step task where an agent decides which deterministic function runs next, and the sequence itself is the work. The roadmap's determinism constraint was written for exactly that case:
Agents may orchestrate only — choose which service calls to make and in what order. Every service call is pure Python. The supervisor writes the full call trace to the ledger, so any run stays reconstructable.
That constraint is sound and the refutation above does not touch it. But the
case is currently hypothetical: there is no multi-step workflow in this repo
that a user performs today and that a planner would improve. runs.scan then
runs.compare is two calls, and a rule can sequence two calls.
The condition for reopening this, stated so it is checkable rather than a matter of taste:
Reopen when a real workflow exists that (a) requires three or more calls whose order depends on intermediate results, and (b) a rule table cannot express. Then measure the planner against a rule table on that workflow before building it.
Absent that, a planner is a second model call looking for a job, and this document is the record of what that cost last time.
What is closed
- No
swarm.toml. - No LLM subagents for routing.
- No inter-agent messaging. The four surfaces never talk to each other; a message bus between them would reintroduce the exact compounding-error mechanism the swarm arm demonstrated.
- No supervisor process.
Consequences for the roadmap
Roadmap spec 4 is closed by this document. Roadmap spec 5 (agent-config
emitters) declared a dependency on spec 4 and on the swarm.toml it would have
produced. That dependency is void; spec 5 is rescoped in
2026-08-22-config-emitters-design.md to generate from AGENT_SURFACES, which
exists, instead of from a file that will not.