Three pages the audit asked for that did not exist.
14 Fleet Manager — fleet-manager had zero coverage in this wiki, so an
operator had no way to learn it exists. Written from its own source: the
fleets.json shape from the parser rather than the example file, what each
command does, and how the probe decides working vs stalled by comparing
worktree modification times twice. It also records the two limits that come
from fleetd rather than from the tool: two daemons sharing one herdr session
tear down each other's members, and a session inside a pane is resolved as a
worker, which is why the manager sits outside and speaks REST.
15 REST API Reference — the 14 routes were documented nowhere as a set. The
page leads with why that matters: MCP and REST are sibling adapters over one
shared MessageService, so REST behaviour cannot be inferred from the MCP
contract. fleet_ack and fleet_whoami have no route at all.
It also records a trap found while writing it: GET /members returns its
rows under a "workers" key (FleetApp.java:322). The route was renamed from
/workers in CB-557 and the body key was left behind, so a caller reading
body["members"] sees an empty fleet instead of an error.
16 Security & Trust Boundary — the guard, the role table, the member
credential scrub and token scope were spread across three pages. Collected,
with the limits stated rather than glossed: the effective allow-list is a
union and so a strict superset of what an operator writes under allow:; the
ZDOTDIR scrub is zsh-only; blocking SSH_AUTH_SOCK does not stop a member
reaching a passphrase-free key file; and argv is world-readable, which
bypasses every environment control described on the page.
Home and _Sidebar link all three.
#168: Home diagram claimed events.subscribe, which was never built
UnixSocketHerdrClient's own class doc says streaming methods
(events.subscribe) 'keep their own long-lived connection and land in a
later ticket'. The shipped path polls for status and delivers a turn with
agent.prompt. The audit flagged this edge label and it was the last one
left.
#168: revise the front matter, Approaches, and the OpenCode port
- Home and _Sidebar: renamed the product to fleet / fleetd. Dropped the claim
that AgentAPI is a "swappable fallback injector" — it was never built, and no
AgentAPI code exists under fleetd/src/main/java.
- Home also lost two claims that chapters 1 and 2 removed today: there is no SSE
route, and the page no longer names a gateway host as if it were fixed. The
allowed hosts are a config value and differ per deployment.
- Home no longer carries a release number, a ticket count or a test total. Those
go stale in days and then read as current facts; 8-Roadmap holds the record.
- 3-Approaches keeps AgentAPI as discarded research, which is that page's job,
but never in the present tense. Evidence: a search for agentapi under
fleetd/src/main/java finds nothing, and both Profile.kind values run through
HerdrPeerLauncher.
- 12-Claude-to-OpenCode: the sample mount name was `fleetd`, which teaches a
second product name. Every launcher writes the same constant,
PeerLauncher.MCP_MOUNT_NAME = "fleet". A hand-written config using another key
mounts under a name the role-detection ladder never checks.
Match the code cutover: daemon name, config (fleetd.yaml), scripts, launchd/
systemd units, module dir, and MCP tool prefix bridge_* -> fleet_*. Kept:
the BRIDGED_MEMBER security marker, mcp__bridge__ (historical mount name), and
the .bridged-worktrees on-disk path. The portable CLAUDE.md block stays
byte-identical with the repo's CLAUDE.md.
Two members audited Home.md + 4-Setup.md and 5-Operations.md against the
code. Three findings changed what Home.md says.
1. The AgentAPI fallback does not exist. The page said it was "retained as a
swappable fallback injector"; `grep -ri agentapi bridged/src/main` returns
nothing, and the only injection path in shipped code is the herdr one. It
is a discarded option, not something you can switch to, and reading it as
a fallback would send an operator looking for a lever that was never
built.
2. "One `claude mcp add` line for both sides" is claude-code framing only.
An opencode member mounts through a generated opencode.json and gets no
ANTHROPIC_* variables at all (OpenCodeLauncher). Also added what the
broker actually is - optional, and LavinMQ over AMQP when on, not the
Redis Streams / NATS the design era assumed.
3. The subscription boundary now has a deliberate exception. A profile with
`subscription: true` runs its members on the operator's own plan on
purpose, and an opencode member sits outside the boundary entirely on its
own provider credential. The old absolute wording hid the one setting in
the system that spends money.
Also corrected the reply paragraph: the blocking bridge_send is capped by the
lead's own MCP client timeout at about 60 seconds, so real work uses
wait:false and a ticket. The page described only the blocking path.
docs: add chapter 13, the operator user guide, for release 1.1
The wiki had twelve chapters and none of them told an operator how to run
the thing. Chapters 1-3 explain why the design is what it is, 9-12 explain
how the code is put together, 11 lists capabilities. The two pages that were
meant to cover bring-up and day-2 - 4-Setup and 5-Operations - were never
written past their scope note, and every technical detail in them had gone
wrong: a decommissioned model host, port 8080, herdr protocol 14, a systemd
unit that does not exist, Redis Streams and NATS that were never built,
"no per-session authz yet" after Authz shipped, and recycle() events that
have no code behind them.
So this adds 13-User-Guide.md, written against the running system on
2026-08-17, and points the two stubs at it rather than leaving wrong claims
in place.
The guide covers:
1. what this is and, more usefully, the five things it is NOT, each with
the reason it is not that;
2. install - herdr (check the PROTOCOL number, not the version), the
daemon, the login-shell rule for secrets.sh, and the lead's tab label;
3. configure - the four knobs that cost money, the live profile table with
who pays for each, the gateway paths, and memberCredentials' two halves;
4. run - the redeploy script, and the four checks that go beyond /healthz,
because health is green while every spawn fails;
5. delegate - the eleven tools, the spawn-all-then-send-all rule, the ~60s
client cap on a blocking send, and the authz table;
6. when it breaks - twelve traps hit for real this year, grouped by
bring-up, losing a member's work, and merging a member's work;
7. where to look next.
Home.md is corrected too: it claimed members launch against ollama.ltms.dev,
a host that no longer exists (the gateway is llm.ltms.dev), it framed the
system as Claude-only with one worker, it listed 8 of the 13 pages, and its
status still said "Design".
wiki: consistency pass vs rewritten Architecture (5-agent review)
Independent cold reads of every page against 1-Architecture found no invariant
violations; fixed the drift the rewrite introduced plus one real contradiction:
- terminology: Channel 1/2 -> Mode 1/2, 'two-channel' -> 'two invariants / two
modes' (Approaches, Team, Home, Sidebar, Message-Server); north/south face ->
SERVER/CLIENT face (Message-Server, 6 spots)
- contradiction reconciled: Architecture now acknowledges a non-MCP *worker*
Stop-hook (POSTs reply to bridged) as well as the split-host-primary hook -
both target bridged, never a broker; Message-Server tier table split into
Unified / Hooked / Unmodified to match
- Approaches: footnote credits bridge_reply (Stop-hook = fallback); §4 subtitle
reframed; <payload> mermaid label de-angled (parse-safe)
- Team: SERVER 'role router' -> 'policy brain'; fan-out sequence quoted; inference edges labeled
- Home/README: CLIENT-face node regains 'status-gated injector'
- Operations: 'broker' -> 'internal broker/queue'; Stop-hook framed as split-host exception
- async ticket/bridge_poll reframed as injection-first (push), poll = non-pane fallback
All 16 mermaid blocks validated with mmdc.
wiki: bridged is the sole communication gateway (no Claude<->broker, no mainline Stop-hook)
Now that every Claude session mounts bridged over MCP, make bridged the ONLY
thing a Claude session talks to. Claude never posts to / polls a broker; async
delivery is bridged injecting an idle pane (event-driven off agent_status). The
broker drops below the gateway line as bridged-owned durability/cross-host infra.
The Stop-hook survives only as a split-host escape hatch that polls bridged (not
the broker).
- 1-Architecture: add the gateway invariant; rewrite Channel 2 as bridged-mediated
async; redraw components + deployment diagrams (broker below gateway, drop
Claude/Hook -> broker arrows); guardrails now bridged-enforced; failure-modes
updated (bridged down = whole gateway down)
- 2-Message-Server: reply-model, reply-envelope (hook posts bridged not broker),
async-duplex sequence, components/API/tech-stack/milestones/trade-offs, both
deployment diagrams
- 3-Approaches: sole-gateway notes in herdr/AgentAPI/queue sections, matrix + recs
- 4-Setup: split-host Stop-hook polls bridged; queue is internal
- 6-Team: detached jobs via bridged async, not broker
- Home + README: 'one gateway' bullet; intros updated
All 16 mermaid blocks validated with mmdc; 2 rendered to PNG for layout.
Declutter every component diagram to show bridged as ONE standalone daemon
split into a SERVER (north) face — MCP server + REST/SSE + policy brain — and
a CLIENT (south) face — status-gated injector + herdr socket client. Claude
sessions are shown as herdr panes that mount the MCP server (call up) while the
client drives them down over the socket.
- 2-Message-Server: main architecture flowchart rebuilt (herd subgraph of
panes + bridged subgraph with srv/cli); intro reframed; split-host node label
- 1-Architecture: components diagram — bridged subgraph with SERVER/CLIENT faces
- 6-Team: topology — bridged split into faces + role router; worker bridge_reply
- Home + README: overview flowchart — bridged subgraph with SERVER/CLIENT faces
All 16 mermaid blocks validated with mmdc.
wiki: number page filenames (1-..5-) so Gitea Pages list sorts
- git mv content pages to N-Name.md (history preserved); Home + _Sidebar kept
- convert [[wiki-links]] to [display](numbered-slug) markdown links so
resolution is unambiguous and prose display stays clean