diff --git a/12-Claude-to-OpenCode.md b/12-Claude-to-OpenCode.md
index 6696474..9b26dfc 100644
--- a/12-Claude-to-OpenCode.md
+++ b/12-Claude-to-OpenCode.md
@@ -2,7 +2,7 @@
**What this page is for.** Taking a project a Claude Code session already works in, and making an
OpenCode session a first-class participant in the same workspace — same instructions, same MCP
-servers, same bridge mount.
+servers, same `fleet` mount.
**The short version: there is almost nothing to port.** OpenCode reads `CLAUDE.md` natively. The
only artifact you create is one `opencode.json` mapping MCP servers. Everything below was verified
@@ -64,11 +64,18 @@ belong in the global file and shared ones in the project file.
"$schema": "https://opencode.ai/config.json",
"instructions": ["CLAUDE.md"],
"mcp": {
- "fleetd": { "type": "remote", "url": "http://127.0.0.1:8765/mcp", "enabled": true }
+ "fleet": { "type": "remote", "url": "http://127.0.0.1:8765/mcp", "enabled": true }
}
}
```
+The mount name here must be `fleet`, not `fleetd`. Every launcher writes this same constant name
+(`PeerLauncher.MCP_MOUNT_NAME = "fleet"`, `PeerLauncher.java:34`) into the config it builds for a
+spawned worker — `ClaudeCodeLauncher` into an inline `--mcp-config` (`ClaudeCodeLauncher.java:348`),
+`OpenCodeLauncher` into the `opencode.json` it writes (`OpenCodeLauncher.java:345`). A hand-written
+config using a different key mounts a server under a name the role-detection ladder in `CLAUDE.md`
+does not check, so an opencode peer set up by hand must match this name exactly.
+
Set `instructions` explicitly even though `CLAUDE.md` is found anyway — the fallback applies only
while no `AGENTS.md` exists.
@@ -91,8 +98,9 @@ files opencode never reads. Verified here: a clean login shell
exist only inside processes Claude Code spawned. Run `opencode mcp list` from such a process and
everything is green; run it from a real terminal and context7 is `⚠ needs authentication`.
-Worse, the two launch paths disagree on names. A bridge-spawned worker gets `GITEA_TOKEN` +
-`GITEA_HOST` from `applyGitToken` (see [Features](11-Features) → the git-forge token grant), not
+Worse, the two launch paths disagree on names. A fleetd-spawned worker gets `GITEA_TOKEN` +
+`GITEA_HOST` from `applyGitToken` (see [Features](11-Features) → the git-forge token grant; the
+method lives at `HerdrPeerLauncher.java:893-900` and both launchers call it), not
`GITEA_ACCESS_TOKEN` — so an `opencode.json` written against the terminal's environment fails
inside a worker, and vice versa.
@@ -101,7 +109,7 @@ So the supply route follows from who launches opencode:
| Launcher | Route | Here |
|---|---|---|
| a human, from a terminal | `{file:.secrets/…}` | gitignored `.secrets/`, workspace-scoped |
-| a bridge-spawned worker | `{env:…}` | `applyGitToken` + the profile's `env:` block |
+| a fleetd-spawned worker | `{env:…}` | `applyGitToken` + the profile's `env:` block |
For the terminal case this repo uses `{file:}` with **workspace-relative** paths — verified: opencode
resolves them against the project root, so no shell export and no direnv is needed, and the secret
@@ -163,21 +171,23 @@ authenticated tool per server, and confirm nothing you withheld appears.
- **A leftover `AGENTS.md` silently shadows `CLAUDE.md`.** If one exists from an earlier port,
delete it, or opencode reads the stale copy instead of the live rules. Check this first.
-- **Skills do not port.** OpenCode uses its own agent markdown under `.opencode/agents/`;
- `.claude/skills/**` is not read. A delegation brief that says "load the *implementer* skill" has
- no effect on an opencode peer — spell the procedure out in the brief instead.
+- **Skills do not port.** OpenCode uses its own agent markdown under `.opencode/agents/`
+ (`HerdrPeerLauncher.java:322-326` builds that path for both launchers); `.claude/skills/**` is not
+ read. A delegation brief that says "load the *implementer* skill" has no effect on an opencode
+ peer — spell the procedure out in the brief instead.
- **Identity comes from the terminal, not the runtime.** A peer must be spawned into its own pane;
running `opencode` in the primary's pane resolves as the primary.
-- **The subscription guard doesn't apply.** It is `ANTHROPIC_BASE_URL`-shaped; opencode
- authenticates through its own credentials, so `maxLoad` is the only spend control that reaches it.
+- **The subscription guard doesn't apply.** It is `ANTHROPIC_BASE_URL`-shaped and lives only in
+ `ClaudeCodeLauncher`; `OpenCodeLauncher.buildLaunch` never calls it. opencode authenticates
+ through its own credentials, so `maxLoad` is the only spend control that reaches it.
---
## Appendix — the Codex port (superseded)
Chapter 12 previously documented porting to OpenAI Codex. That path was abandoned in favour of
-opencode, which the bridge already supports through `OpenCodeLauncher`. Four findings are kept
-because they were verified by hand and explain *why* the comparison went this way:
+opencode, which fleetd already supports through `OpenCodeLauncher`. Four findings are kept because
+they were verified by hand and explain *why* the comparison went this way:
- **Codex has no `CLAUDE.md` fallback.** Instructions had to be translated into `AGENTS.md` by a
third-party tool, whose terminology map rewrote file paths into sentences that were wrong rather
diff --git a/3-Approaches.md b/3-Approaches.md
index 37b6449..424fab1 100644
--- a/3-Approaches.md
+++ b/3-Approaches.md
@@ -20,7 +20,7 @@ flowchart TD
Q -->|"raw keystrokes into the tmux pane"| TMUX["tmux send-keys
/ PTY paste"]
HD --> V0["✅ our leading choice
(status events + multiplex;
symmetric single-host)"]
- AA --> V1["◐ fallback injector
(swappable behind fleetd)"]
+ AA --> V1["✕ never built
(discarded research)"]
SDK --> V2["✅ if driver is our own code"]
BUS --> V3["⚠ async bus events only
(lands at turn boundary)"]
TMUX --> V4["⚠ fragile — the raw primitive
herdr/AgentAPI productize"]
@@ -71,26 +71,35 @@ Unix-socket JSON API. `fleetd` (see [Message Server](2-Message-Server)) drives i
- **Subscription-safe:** only the worker pane launches with `ANTHROPIC_BASE_URL`; `fleetd`
is a plain daemon (no quota) that enforces the boundary in code.
- **Trade-off:** herdr's socket is **local-only** (`fleetd`'s MCP/HTTP spans hosts, not
- herdr), and it is a young, single-dev project — so `fleetd` keeps the injector **pluggable**
- and its durability in an **internal** queue behind the gateway. Replies are best carried as a
+ herdr), and it is a young, single-dev project — so `fleetd` keeps delivery durability in an
+ **internal** queue behind the gateway. The shipped injector is the herdr one: a
+ status-gated, single writer per worker (`Injector.java:48`), and it is a `final` class —
+ not a pluggable interface a second injector could slot into. Replies are best carried as a
structured envelope, not scraped.
-## 2. AgentAPI — HTTP over terminal emulation *(fallback injector)*
+## 2. AgentAPI — HTTP over terminal emulation *(never built — research only)*
[`coder/agentapi`](https://github.com/coder/agentapi) wraps the Claude Code **CLI** as an
HTTP server and drives the CLI's terminal underneath (essentially a hardened, stateful
`tmux send-keys` with parsing): `POST /message`, `GET /events` (SSE), `GET /status`. It was
-the original leading choice; herdr now supersedes it, but it remains a **swappable fallback
-injector** behind `fleetd`'s interface.
+the original leading choice, until herdr superseded it. **It was never built**: a
+case-insensitive search for `agentapi` under `fleetd/src/main/java` returns nothing, and
+both `Profile.kind` values (`claude-code`, the default, and `opencode`,
+`FleetConfig.java:265-270`) run through `HerdrPeerLauncher`
+(`ClaudeCodeLauncher.java:43`, `OpenCodeLauncher.java:54`) — herdr is the only injection
+path that ships. The mechanism below describes the external project, not this codebase.
-- **Injects into a live session:** yes — but only the *worker* (it wraps one CLI); the
- primary direction still needs `fleetd`'s async path (idle-injection, or a split-host
- `Stop`-hook polling `fleetd`).
+- **Injects into a live session:** in its own design, yes — but only the *worker* (it wraps
+ one CLI); the primary direction still needs `fleetd`'s async path (idle-injection, or a
+ split-host `Stop`-hook polling `fleetd`).
- **Completion signal:** a **screen-stability heuristic**, not structured events.
- **Cross-host:** native HTTP — its one edge over herdr, but `fleetd` already provides the
HTTP layer on top of herdr, so that edge is neutralized.
-- **When to reach for it:** if herdr can't run, or as the second injector implementation to
- de-risk herdr's immaturity. Its `msgfmt` reply parser is worth reusing regardless.
+- **Why it was dropped:** herdr's structured status events beat a screen-stability
+ heuristic, and `fleetd` already covers the cross-host part, so that one edge buys
+ nothing here. It stays as discarded research: a second injector implementation to
+ de-risk herdr, and the possible reuse of its `msgfmt` reply parser, which is not in this
+ codebase.
## 3. Agent SDK — streaming input *(good if the driver is our own code)*
@@ -106,7 +115,7 @@ events and permission callbacks instead of scraping a terminal.
## 4. Message-queue + Stop-hook long-poll *(raw async primitive — behind the gateway in our design)*
The **only pure-hooks** way to pull an external message into the **same** session. In
-`claude-bridge` this is **not** how a Claude session normally receives async work — under the
+`fleet` this is **not** how a Claude session normally receives async work — under the
sole-gateway rule `fleetd` delivers async by **injecting an idle pane**, and no Claude session
polls a queue. The Stop-hook survives in exactly one place: a **split-host primary** that isn't
a herdr pane, where the hook long-polls **`fleetd`** (not the queue) for wake-ups. The raw
@@ -161,14 +170,15 @@ live session, works today, but it is terminal automation — timing-sensitive, n
**herdr's `send_text`/`send_keys` is this, productized** — with a stable session, structured
status events, and multiplexing — which is why we build on herdr rather than hand-rolled
-`send-keys` (and why AgentAPI, the other productized form, is only the fallback).
+`send-keys` (AgentAPI is the other productized form of the same idea; it was never built
+and is research only).
## Research matrix
| Approach | Transport | Inject into running session? | Completion signal | Symmetric (both panes)? | Cross-host | Subscription-safe | Fragility |
|---|---|---|---|---|---|---|---|
| **herdr via `fleetd`** ✅ | socket → terminal + events | ✅ (idle-gated) | ✅ status events² | ◐ single-host¹ | via `fleetd` MCP/HTTP (sole gateway) | ✅ (guard in code) | Low–Med (herdr young) |
-| **AgentAPI** ◐ (fallback) | HTTP → terminal emulation | ✅ (worker only) | ⚠ screen-stability | ❌ | ✅ native HTTP | ✅ (worker-only env) | Low |
+| ~~AgentAPI~~ (never built — research) | HTTP → terminal emulation | ✅ (worker only) | ⚠ screen-stability | ❌ | ✅ native HTTP | ✅ (worker-only env) | Low |
| **Agent SDK streaming** | in-process generator | ✅ | ✅ typed events | n/a | ✅ | ✅ | Low (driver = code) |
| **Queue + Stop-hook** | hook long-poll | ✅ (turn boundary) | via turn end | ✅ (symmetric) | ✅ | ✅ | Medium |
| **tmux `send-keys`** | keystrokes / PTY | ✅ | ❌ scrape `capture-pane` | ✅ | ⚠ ssh | ✅ | High |
@@ -187,18 +197,20 @@ structured `fleet_reply` (preferred), with a `Stop`-hook envelope as fallback**
## Recommendation
-- **Primary Opus → worker (the bridge's main path):** **herdr via `fleetd`** —
+- **Primary Opus → worker (the main path of this system):** **herdr via `fleetd`** —
status-gated injection, structured completion/blocked events, symmetric (single-host), multiplexed,
persistent, with the subscription boundary enforced in code. Selected. See
[Message Server](2-Message-Server) / [Architecture](1-Architecture).
-- **Keep AgentAPI as a swappable fallback injector** behind `fleetd`'s interface, so
- herdr's immaturity is a de-riskable risk rather than a load-bearing one.
+- **AgentAPI — never built:** considered and dropped. This page keeps it as research
+ (section 2), not as a fallback an operator can select; herdr's immaturity stands as a
+ known risk, and `fleetd` de-risks it with the internal queue and status-gated injection
+ instead of a second injector.
- **External event bus → worker (async wake-ups):** the bus hits **`fleetd`'s REST ingress**;
`fleetd` enqueues internally if needed and **injects the idle worker** — the worker runs no
queue-polling hook. Complementary to the sync path, not a replacement — different trigger
shape, same single gateway.
-- **Avoid hand-rolled `tmux send-keys`** unless neither herdr nor AgentAPI can run; it's the
- same idea with all the fragility left in.
+- **Avoid hand-rolled `tmux send-keys`** unless herdr can't run on the host; it's the same
+ idea with all the fragility left in.
## Sources
@@ -209,5 +221,4 @@ structured `fleet_reply` (preferred), with a `Stop`-hook envelope as fallback**
- [Stop-hook task-enforcement pattern](https://claudefa.st/blog/tools/hooks/stop-hook-task-enforcement)
- [claude-mem hooks architecture](https://docs.claude-mem.ai/hooks-architecture)
- [Issue #27441 — inter-agent message injection](https://github.com/anthropics/claude-code/issues/27441) · [Issue #24947 — `claude inject`](https://github.com/anthropics/claude-code/issues/24947)
-- [Claude-Code-Remote](https://github.com/JessyTsui/Claude-Code-Remote) · [OpenACP guide](https://dev.to/tigergethigher/how-to-control-claude-code-from-telegram-discord-or-slack-self-hosted-open-source-1jk8)
-
+- [Claude-Code-Remote](https://github.com/JessyTsui/Claude-Code-Remote) · [OpenACP guide](https://dev.to/tigergethigher/how-to-control-claude-code-from-telegram-discord-or-slack-self-hosted-open-source-1jk8)
\ No newline at end of file
diff --git a/Home.md b/Home.md
index 8a7ce0e..733c3db 100644
--- a/Home.md
+++ b/Home.md
@@ -1,4 +1,4 @@
-# claude-bridge
+# fleet
A **subscription-safe bridge** that lets a lead **Claude Code** session on Pro/Max delegate work to
**members running on other models and other vendors** — without ever putting a proxy on the lead.
@@ -8,7 +8,7 @@ A **subscription-safe bridge** that lets a lead **Claude Code** session on Pro/M
> the shape of the design and why it was chosen.
> Sibling of [`crush-bridge`](https://git.ltms.dev/systems/vms) (Tier 2 → headless Crush
-> on GX10 DeepSeek). `claude-bridge` keeps a Claude member a *real Claude Code process* so it
+> on GX10 DeepSeek). `fleet` keeps a Claude member a *real Claude Code process* so it
> inherits `CLAUDE.md`, hooks, skills, and MCP — just pointed at a cheaper model. It also runs
> non-Claude members (`kind: opencode`) as first-class peers.
@@ -17,25 +17,26 @@ A **subscription-safe bridge** that lets a lead **Claude Code** session on Pro/M
A small always-on message server, **`fleetd`**, controls
[herdr](https://herdr.dev) (an agent multiplexer, "tmux for agents") over its Unix-socket
API and exposes a clean 2-way messaging API as an **MCP server that both the primary and the
-workers mount** — one unified Claude setup and the **sole communication gateway** (REST/SSE
-stays for non-Claude clients; any broker is `fleetd`-internal, below the gateway). herdr owns
+workers mount** — one unified Claude setup and the **sole communication gateway**. A plain
+REST surface covers the same features for non-Claude clients; there is no event-stream route.
+Any broker is `fleetd`-internal, below the gateway. herdr owns
the PTYs, multiplexing, persistence, and **agent-status
events**; `fleetd` owns policy (subscription boundary, session lifecycle, status-gated
-delivery) and the client contract. A Claude member launches with `ANTHROPIC_BASE_URL` pointed at
-the gateway, `https://llm.ltms.dev/anthropic`, plus a bearer token; the lead stays env-clean and
-calls `fleetd`'s MCP tools.
+delivery) and the client contract. A Claude member launches with `ANTHROPIC_BASE_URL` pointed at whichever gateway its profile
+names, plus a bearer token. The allowed hosts are a config value, so they differ per
+deployment. The lead stays env-clean and calls `fleetd`'s MCP tools.
```mermaid
flowchart LR
OPUS["Opus — primary
(Claude Code, env CLEAN)
MCP client"]
subgraph BD["fleetd — standalone daemon (not a claude process)"]
- SRV["SERVER face
MCP · REST/SSE · policy"]
+ SRV["SERVER face
MCP · REST · policy"]
CLI["CLIENT face
status-gated injector · herdr socket"]
SRV --> CLI
end
HERDR["herdr
panes · agent-status"]
W["member pane
ANTHROPIC_BASE_URL set
MCP client"]
- M["llm.ltms.dev
(the one gateway)"]
+ M["the configured gateway"]
OPUS -->|"MCP fleet_send (blocks)"| SRV
W -.->|"MCP fleet_reply"| SRV
@@ -70,11 +71,11 @@ flowchart LR
**injects the lead's own idle pane** to wake it.
- **Different model** per member process sidesteps Claude Code's lack of per-subagent
provider routing — the worker isn't a subagent, it's its own configured process.
-- **AgentAPI** ([`coder/agentapi`](https://github.com/coder/agentapi)) was kept on paper as a
- swappable *fallback injector*. It was **never built** — `grep -ri agentapi fleetd/src/main`
- returns nothing, and the only injection path in the shipped code is the herdr one. Treat it as a
- discarded option, not a fallback you can switch to. See [Approaches](3-Approaches) for why herdr
- won and [Message Server](2-Message-Server) for the full design.
+- **AgentAPI** ([`coder/agentapi`](https://github.com/coder/agentapi)) was considered and dropped:
+ it was **never built**. No AgentAPI code exists under `fleetd/src/main/java`, and the shipped
+ injection path is the herdr one, which every profile selects through `Profile.kind`
+ (`claude-code` or `opencode`, `FleetConfig.java:265-270`). See [Approaches](3-Approaches) for
+ why herdr won and [Message Server](2-Message-Server) for the full design.
## Pages
@@ -96,11 +97,14 @@ Read in order (the sidebar mirrors this):
## Status
-🟢 **Running.** Release 1.1 is code-complete (2026-08-16): 20 of 20 tickets closed, 870 tests green,
-the daemon live on this host. Chapters 4 and 5 were never written past their scope note; chapter 13
-replaced them.
+🟢 **Running.** The daemon is live on this host. Chapters 4 and 5 were never written past their
+scope note, and chapter 13 replaced them.
+
+This page does not carry release numbers, ticket counts or test totals. Those go stale within days
+and then read as current facts. See [8 Roadmap](8-Roadmap) for the delivery record.
The **herdr-centric `fleetd` message server** was selected on 2026-07-11, superseding the AgentAPI
-plan of 2026-07-08. AgentAPI is retained as a fallback injector and has not been needed. See
+plan of 2026-07-08. AgentAPI was considered and dropped: it was never built, so there is no
+fallback injector to switch to. See
**[Message Server](2-Message-Server)** for the design and **[13 User Guide](13-User-Guide)** for how
-to run it.
+to run it.
\ No newline at end of file
diff --git a/_Sidebar.md b/_Sidebar.md
index 6f955f6..10dd9d2 100644
--- a/_Sidebar.md
+++ b/_Sidebar.md
@@ -1,4 +1,4 @@
-### 📖 claude-bridge
+### 📖 fleet
[Home](Home) — overview & the decision
@@ -11,7 +11,7 @@
5. [Operations](5-Operations) — ⚫ superseded by 13
6. [Team](6-Team) — orchestrating a mixed fleet
7. [Use Cases](7-Use-Cases) — the review scenario + mechanisms
-8. [Roadmap](8-Roadmap) — stages, tech stack, tickets
+8. [Roadmap](8-Roadmap) — delivery record: what is live, what is off, what was dropped
9. [Implementation](9-Implementation) — as-built code map · classes · flows · state machines
10. [Cross-Host Messaging](10-Cross-Host-Messaging) — broker topology · exchanges · queues per entity
11. [Features](11-Features) — what it can do · the knob that turns it on · why · the gotcha
@@ -19,4 +19,4 @@
13. **[User Guide](13-User-Guide)** — 🟢 install · configure · run · delegate · the traps
---
-🟢 herdr-centric `fleetd` · AgentAPI = fallback
+🟢 herdr-centric `fleetd` · AgentAPI = research, never built