From 8311f2db7fb44c03bc7d2c832cd30d5854b8036c Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Sat, 22 Aug 2026 22:08:12 +0200 Subject: [PATCH] CB-622: update the canonical block to fleet_*, and fix the broken charter quote The eleven tool names become fleet_* throughout the portable block. The fallback ladder keeps mcp__bridge__* unchanged and that is deliberate: both launchers still mount the server as "bridge", so a spawned member really does see that prefix. Renaming the mount is a separate surface CB-622 did not touch. Separately, a pre-existing defect: the ladder quoted the reply charter as "You are an off-subscription worker in the claude-bridge fleet", while REPLY_CHARTER says "You are a spawned member in the claude-bridge fleet". A member matching that quote found nothing, so the ladder's first rung could never fire. Now quoted verbatim. wiki/7-Use-Cases.md is advanced to the matching template commit; the sync check prints in sync: True. --- CLAUDE.md | 78 +++++++++++++++++++++++++++---------------------------- wiki | 2 +- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index c6f1081..59a6e0d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,31 +8,31 @@ > CLAUDE.md block*); improvements go to the template first, then out to each project. Anything > specific to *this* repo lives under §Project addendum below, never inline above it. -If no `bridge_*` MCP tools are mounted in this session, this section does not apply — skip it. +If no `fleet_*` MCP tools are mounted in this session, this section does not apply — skip it. `bridged` is the **sole communication gateway** between agents here. The orchestrating session (the **primary**) and every delegated peer (a **member**) mount the *same* MCP server and talk only -through its `bridge_*` tools. No session addresses a peer, a broker, or the network directly. +through its `fleet_*` tools. No session addresses a peer, a broker, or the network directly. ### Which role am I? — settle this before acting **Every role reads this file.** A member runs in a git worktree of this same repo, so it inherits this `CLAUDE.md` verbatim, and every rule below is role-conditional. -**Call `bridge_whoami`.** It returns `primary`, `worker`, or `architect`, resolved by the daemon from +**Call `fleet_whoami`.** It returns `primary`, `worker`, or `architect`, resolved by the daemon from your connection — unforgeable, and the same resolution its authorization gate uses. A worker also carries its `sessionId`, `profile`, `worktree` and `branch`; an architect carries the slot name it was bound to. Don't infer what you can ask. Only if that call is unavailable, fall back to these — each is one-way, so keep reading until one -fires: the reply charter in your system prompt (*"You are an off-subscription worker in the +fires: the reply charter in your system prompt (*"You are a spawned member in the claude-bridge fleet"*) ⇒ **spawned member**; bridge tools prefixed `mcp__bridge__*` ⇒ **spawned member** (the launcher fixes that mount name; a primary's mount is named by whoever wrote its `.mcp.json`, so it varies); `ANTHROPIC_BASE_URL` set ⇒ **spawned member** (Claude-model members run on a clean env, so its *absence* proves nothing). None of these separate a worker from an architect — -only `bridge_whoami` does. **Still unsure ⇒ act as a worker**, the most restricted member role. The +only `fleet_whoami` does. **Still unsure ⇒ act as a worker**, the most restricted member role. The two mistakes are not symmetric: a primary acting as a worker is refused by the authorization gate — -loud and self-correcting — while a member acting as the primary ends its turn with no `bridge_reply`, +loud and self-correcting — while a member acting as the primary ends its turn with no `fleet_reply`, and the sender silently receives nothing. Fail toward the recoverable error. ### Invariants — both roles, no exceptions @@ -41,7 +41,7 @@ and the sender silently receives nothing. Fail toward the recoverable error. stays on subscription; only the bridge puts a member off it, at spawn. Mounting the bridge must never move a session across that boundary. 2. **The bridge is the only channel.** Text you print in your terminal reaches nobody — the other - side cannot see your screen. An answer that isn't in a `bridge_*` call is silently discarded. + side cannot see your screen. An answer that isn't in a `fleet_*` call is silently discarded. 3. **Identity comes from the connection, never an argument.** Workers never pass a target; you cannot act as another session. Spawn/stop/drain are lead-only; **send is lead or architect**; reply/ask are only-as-itself — any peer may answer for its own pane, and for no other. A call @@ -57,10 +57,10 @@ and the sender silently receives nothing. Fail toward the recoverable error. **Delegate by default — that is the job.** With the bridge mounted you are an orchestrator on a metered subscription, and workers are cheap, parallel, and disposable. The default answer to "who -does this?" is **a worker**, not you. Reach for `bridge_send` before you reach for `Edit`. The steps +does this?" is **a worker**, not you. Reach for `fleet_send` before you reach for `Edit`. The steps below are the procedure — run them in order, every task, not only the big ones. -0. **Know your role** — `bridge_whoami`, once per session, before anything else. +0. **Know your role** — `fleet_whoami`, once per session, before anything else. 1. **Split.** Write the unit list. Every unit carries: scope · the files or PR in question · acceptance criteria · exactly what to report back. A unit with no acceptance criteria is not ready to delegate — refine it or keep it. @@ -70,17 +70,17 @@ below are the procedure — run them in order, every task, not only the big ones delegate. The keep-list is closed: the conversation with the user, decomposition and planning, the final judgment call, verification, merges, and anything that depends on context only you hold. Nothing else is yours by default. -3. **Spawn every delegated unit first** — `bridge_spawn{profile, worktree:true, ticket}`, one per +3. **Spawn every delegated unit first** — `fleet_spawn{profile, worktree:true, ticket}`, one per unit, *before* sending any. Pass `profile` explicitly: profiles differ in model and cost, not in tier, so the default is rarely what you want. -4. **Then send them all** — `bridge_send{sessionId, content, wait:false}`. Line 1 of every brief is +4. **Then send them all** — `fleet_send{sessionId, content, wait:false}`. Line 1 of every brief is `Load the skill.` naming the worker's playbook; those skills are opt-in and that line is what makes them reliable. Where the project ships no such skill, spell the procedure out in the brief instead. The brief is self-contained — the worker sees your message and the repo, nothing of your context, your plan, or your screen. -5. **Collect** — `bridge_poll{ticket}` → `bridge_ack{target, msgId}`. Answer a worker's `bridge_ask` - with `bridge_send{turnId, content}` — **not** `sessionId`. A worker gone quiet is diagnosed with - `bridge_status`, never by reading its terminal; it also reports an open question and the `turnId` +5. **Collect** — `fleet_poll{ticket}` → `fleet_ack{target, msgId}`. Answer a worker's `fleet_ask` + with `fleet_send{turnId, content}` — **not** `sessionId`. A worker gone quiet is diagnosed with + `fleet_status`, never by reading its terminal; it also reports an open question and the `turnId` that answers it. **A worker's ask waits ~55 seconds, and no nudge makes that longer** — so never brief a worker to "ask me". Decide before you delegate, or give it an explicit default. 6. **Verify yourself.** Re-run the build and the checks. A worker cannot run your IDE tooling, any @@ -93,11 +93,11 @@ below are the procedure — run them in order, every task, not only the big ones read it yourself. 8. **Adjudicate, merge, tear down — yours alone.** Read the diff yourself: fully if it is small, targeted at the reported findings and the risky paths if it is large. Reviewer findings direct - your attention; they never substitute for it. Then merge, then `bridge_stop{paneId}`. + your attention; they never substitute for it. Then merge, then `fleet_stop{paneId}`. **Steps 3 and 4 are separate on purpose** — spawning and sending in one loop is how parallel work silently becomes serial, and it is the most common way this layer is wasted. For the same reason, -prefer `wait:false` + `bridge_poll` for anything non-trivial: a blocking `bridge_send` is capped by +prefer `wait:false` + `fleet_poll` for anything non-trivial: a blocking `fleet_send` is capped by *your own* MCP client call timeout (~60s), well below the task's real runtime. **Delegating does not delegate responsibility.** Workers open PRs; you are the gate. Never delegate @@ -105,21 +105,21 @@ the merge — and merging on a reviewer's word is delegating it by proxy. | Intent | Tool | |---|---| -| Confirm your own role | `bridge_whoami` | -| See backends available | `bridge_profiles` | -| Start a member | `bridge_spawn{role?, profile?, cwd?, worktree?, ticket?, sessionName?, resumeSessionId?}` → `sessionId` + `paneId` | -| See the fleet | `bridge_list` → `leads` (your peers) + `members` (each carries `agentSessionId` when its backend knows one) · one peer's state: `bridge_status{sessionId}` | -| Delegate (blocking) | `bridge_send{sessionId, content}` | -| Delegate (long task) | `bridge_send{sessionId, content, wait:false}` → ticket → `bridge_poll{ticket}` | -| Answer a member's `bridge_ask` | `bridge_send{turnId, content}` — **not** `sessionId` | -| Message a **peer lead** | `bridge_send{sessionId: , content}` — `bridge_list` → `leads` reports it. Coordination only, **never** a task | -| Answer a peer lead that messaged you | `bridge_reply{content}` — the one case a lead replies | -| Collect a held reply | `bridge_poll{target}` · then `bridge_ack{target, msgId}` | -| Tear down a member | `bridge_stop{paneId}` | +| Confirm your own role | `fleet_whoami` | +| See backends available | `fleet_profiles` | +| Start a member | `fleet_spawn{role?, profile?, cwd?, worktree?, ticket?, sessionName?, resumeSessionId?}` → `sessionId` + `paneId` | +| See the fleet | `fleet_list` → `leads` (your peers) + `members` (each carries `agentSessionId` when its backend knows one) · one peer's state: `fleet_status{sessionId}` | +| Delegate (blocking) | `fleet_send{sessionId, content}` | +| Delegate (long task) | `fleet_send{sessionId, content, wait:false}` → ticket → `fleet_poll{ticket}` | +| Answer a member's `fleet_ask` | `fleet_send{turnId, content}` — **not** `sessionId` | +| Message a **peer lead** | `fleet_send{sessionId: , content}` — `fleet_list` → `leads` reports it. Coordination only, **never** a task | +| Answer a peer lead that messaged you | `fleet_reply{content}` — the one case a lead replies | +| Collect a held reply | `fleet_poll{target}` · then `fleet_ack{target, msgId}` | +| Tear down a member | `fleet_stop{paneId}` | ### Lead ↔ lead — coordinate, never delegate -`bridge_list` returns `leads` alongside `members`; your own row carries `self: true`. Every other row +`fleet_list` returns `leads` alongside `members`; your own row carries `self: true`. Every other row is a peer — an orchestrator with its own context, its own members, and its own judgment. An empty `members` array means no members are spawned; it says nothing about peers. @@ -140,7 +140,7 @@ The traffic between leads is coordination and nothing else: against the code, and re-run the build. A peer's correction gets the same treatment — right or wrong on the evidence, not on who said it. Neither of you merges the other's work unreviewed. -Being messaged by a peer does not make you its worker: answer with `bridge_reply`, and push back on +Being messaged by a peer does not make you its worker: answer with `fleet_reply`, and push back on the substance if it is wrong. A peer that simply complies has thrown away the reason there are two of you. @@ -148,11 +148,11 @@ you. 1. **Load the playbook skill the lead named** before doing anything else. 2. **Do the assigned scope only.** Note anything you spot outside it in one line; don't go hunt it. -3. **`bridge_ask{question}`** when a decision is genuinely the lead's (ambiguous requirement, two +3. **`fleet_ask{question}`** when a decision is genuinely the lead's (ambiguous requirement, two defensible fixes, "bug or intended?"). It blocks and you resume the *same* turn with the answer. Don't ask what you could decide yourself. -4. **End the turn with exactly one `bridge_reply{content}`**, carrying your complete answer. This is - the whole handoff. No `bridge_reply` ⇒ the sender gets nothing and the exchange stalls. +4. **End the turn with exactly one `fleet_reply{content}`**, carrying your complete answer. This is + the whole handoff. No `fleet_reply` ⇒ the sender gets nothing and the exchange stalls. Do **not** lean on the completion fallback to carry your answer for you: when you end a turn without replying, the bridge scrapes your pane, and it can return only the last 4000 characters. A clipped scrape is marked as partial, but the missing text is gone — your report reaches the @@ -171,7 +171,7 @@ you. | Layer | Scope | Reaches | |---|---|---| -| the launcher's reply charter | the one rule that must survive with no repo: *end every turn with `bridge_reply`* | every spawned member, at launch, every peer kind — never a lead | +| the launcher's reply charter | the one rule that must survive with no repo: *end every turn with `fleet_reply`* | every spawned member, at launch, every peer kind — never a lead | | **this section** | protocol + orchestration policy | primary **and** every member that reads the repo — tracked in git, so worktrees inherit it | | role agent definition files | role contract and per-job procedure | a member whose launcher binds its role to the matching file in its worktree | | role playbook skills | per-job procedure (commit/PR recipe, finding format) | a member told to load one | @@ -193,7 +193,7 @@ must obey belongs in the charter, not here. `port-to-opencode` (make an OpenCode session a participant in this workspace). - **Never commit** `.mcp.json` (the primary's local copy, flagged `--skip-worktree`) or `wiki/` (a submodule with its own remote). -- **Flows and the error model** — rendezvous, `bridge_ask`, detached delivery, turn-done fallback — +- **Flows and the error model** — rendezvous, `fleet_ask`, detached delivery, turn-done fallback — are diagrammed in `docs/MCP-Contract.md` **§6 only**. The rest of that page is a pre-build design doc whose tool names, parameter names and REST paths never caught up with the code, so do not use it as the tool reference (CB-609). Section 6 is kept out of this file because this file loads into @@ -231,13 +231,13 @@ if the script is unavailable or a step fails, this is what it was protecting you daemon starts fine, and the failure appears much later as workers that cannot open a PR. Nothing logs this at startup — the script's `--check` is the only thing that reports it, and it checks whether the name resolves without ever printing the value. -2. **Drain live members first.** `bridge_list`, then `bridge_stop` each member, and collect anything - you still want with `bridge_poll` before you kill anything. A restart drops in-flight tickets and +2. **Drain live members first.** `fleet_list`, then `fleet_stop` each member, and collect anything + you still want with `fleet_poll` before you kill anything. A restart drops in-flight tickets and rendezvous, and a member's report is not recoverable once its ticket is gone. 3. **A restart is the only way deferred config keys take effect.** That is usually the reason to do it. The startup log names which keys it accepted and which it deferred — read those lines rather than assuming. -4. **Re-check identity afterwards.** Call `bridge_whoami` and confirm it still answers `primary`. The +4. **Re-check identity afterwards.** Call `fleet_whoami` and confirm it still answers `primary`. The lead is found by its tab label (`fleet.leaders.*.tab`), and a lead whose tab no longer matches is demoted to worker, which refuses every orchestration call. 5. **Prove the new jar is the one running.** Confirm a *fresh* `bridged listening` line at the end of @@ -268,9 +268,9 @@ Before you call any work done, check the row that matches what you touched: | You changed… | Re-read and update… | |---|---| -| a `bridge_*` tool — added, removed, renamed, or its params/semantics | the primary's intent→tool table; any rule that names that tool | +| a `fleet_*` tool — added, removed, renamed, or its params/semantics | the primary's intent→tool table; any rule that names that tool | | `Authz` / the role table | invariant 3, and the primary-only vs worker-only claims | -| `ConnectionIdentity` / how a caller is resolved | the `bridge_whoami` paragraph and the fallback ladder | +| `ConnectionIdentity` / how a caller is resolved | the `fleet_whoami` paragraph and the fallback ladder | | `REPLY_CHARTER`, or a launcher's mount/flags | the fallback ladder (`mcp__bridge__*`), and the layering table's top row | | the injector / status gating | invariant 4 | | worktree provisioning or the parity overlay | the "both roles read this file" premise — it rests on the worker's worktree being a checkout of this repo | diff --git a/wiki b/wiki index aa750de..d526b43 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit aa750de78e45969e3c2fa5824524a9f872529024 +Subproject commit d526b43c87fdea6c8b257e79917bcc4b8a49f582 -- 2.52.0