diff --git a/.claude/skills/port-to-opencode/SKILL.md b/.claude/skills/port-to-opencode/SKILL.md index 74f5504..fe714e1 100644 --- a/.claude/skills/port-to-opencode/SKILL.md +++ b/.claude/skills/port-to-opencode/SKILL.md @@ -53,7 +53,7 @@ Map each entry from `.mcp.json`: "$schema": "https://opencode.ai/config.json", "instructions": ["CLAUDE.md"], "mcp": { - "bridged": { "type": "remote", "url": "http://127.0.0.1:8765/mcp", "enabled": true }, + "fleetd": { "type": "remote", "url": "http://127.0.0.1:8765/mcp", "enabled": true }, "context7": { "type": "remote", "url": "https://example.dev/mcp", "enabled": true, "headers": { "Authorization": "Bearer {env:CONTEXT7_TOKEN}" } }, "gitea": { "type": "local", "command": ["gitea-mcp", "-t", "stdio"], "enabled": true, diff --git a/bridged/bridged.example.yaml b/bridged/bridged.example.yaml index c1d962b..eb58a85 100644 --- a/bridged/bridged.example.yaml +++ b/bridged/bridged.example.yaml @@ -35,7 +35,7 @@ bind: # a caller whose connection maps to this pane resolves as the primary (no credential needed — # the pane mapping is as unforgeable as a worker's), and reply nudges are pushed to it. # REQUIRED when the primary runs inside a herdr pane — without it the pane match reads the -# primary as a worker and refuses spawn/send/stop. Get the id from bridge_whoami; re-pin if +# primary as a worker and refuses spawn/send/stop. Get the id from fleet_whoami; re-pin if # the primary moves panes. # primary: # terminal: term_0123456789abcd @@ -49,12 +49,12 @@ bind: # # tab → the ONLY field identity depends on (CB-579); the exact label of the tab hosting the lead. # Label the tab yourself, or let bridged label one it launches — see `fleet.leaders:` below. -# kind/model → descriptive; they document what runs in the pane and are echoed by bridge_whoami +# kind/model → descriptive; they document what runs in the pane and are echoed by fleet_whoami # # A lead's tab must already carry its label (or be launched by bridged, which labels it) — there is # no terminal id to paste in and nothing to re-pin when the session restarts: the tab survives, so # the same label resolves the same lead again on the next scan. -# `bridge_whoami` reports `{"role":"primary","leader":""}`; role stays "primary" because a lead +# `fleet_whoami` reports `{"role":"primary","leader":""}`; role stays "primary" because a lead # IS a primary for authorization, so nothing that keys on the role breaks. # # KEEP `primary:` when adding leads: it still addresses the CB-307 push loop, which needs a single @@ -69,7 +69,7 @@ bind: # never a capability: what a pane may do is decided by the role the daemon resolves for it. # CB-551: IDLE-LEAD HEARTBEAT — nudge the single lead back to work when it has been continuously -# idle (no open bridge_send driving it) past the quiet period. The fleet is one lead + architects + +# idle (no open fleet_send driving it) past the quiet period. The fleet is one lead + architects + # workers, so a lead that stalls is a single point of failure; the ReplyPushLoop only nudges when a # reply lands, and this timer catches the gap where nothing lands and the lead just sits idle. # @@ -99,7 +99,7 @@ bind: # them changes nothing right now, and no minimum is enforced on either, because # nothing reads them to enforce one. They exist so a later build can start # honouring them without another config-shape change. -# notifications.mode → "webhook" flips what bridge_list REPORTS (healthCoverage: "full" instead +# notifications.mode → "webhook" flips what fleet_list REPORTS (healthCoverage: "full" instead # of "detection-only") — it does NOT make bridged send any webhook call; no # delivery mechanism is implemented yet. Any other value, or omitting the # block, reports "detection-only". @@ -152,7 +152,7 @@ herdrSocket: ~/.config/herdr/herdr.sock # gitHostEnv → host env var holding the forge host (default GITEA_HOST). Injected as # GITEA_HOST *only* alongside a resolved gitTokenEnv. # exhaustedPattern → regex matched against a completion-fallback scrape (CB-578 stage A) to -# classify a turn that ended with no bridge_reply as the backend having +# classify a turn that ended with no fleet_reply as the backend having # refused on a subscription usage limit, rather than a real answer. Opt-in — # omit and this profile's completion fallback behaves exactly as before. # Every backend words its refusal differently, so this is config, never a @@ -200,12 +200,12 @@ profiles: # weight: relative selection weight for automatic placement (weighted, round-robin, and # fixed's fallback walk). Absent defaults to 1.0. An explicit 0 or negative value means # "never auto-select this profile" (CB-554) — it stays reachable via an explicit - # `bridge_spawn{profile:"gx10"}`, which bypasses placement entirely; only automatic + # `fleet_spawn{profile:"gx10"}`, which bypasses placement entirely; only automatic # selection skips it. weight: 0.5 # maxLoad: max live workers on this profile. Omit for unlimited. An explicit 0 (CB-585) caps # the profile at zero live members — it is excluded from automatic placement and an explicit - # `bridge_spawn{profile:"gx10"}` against it is refused too; a cap holds even when the profile + # `fleet_spawn{profile:"gx10"}` against it is refused too; a cap holds even when the profile # is named directly. Negative is refused at config load — there is no sane meaning for it. maxLoad: 2 # subscription: true @@ -259,8 +259,8 @@ profiles: # The bridge MCP + reply charter mount via a generated OPENCODE_CONFIG file, and the model is a # `provider/model` selector. Placement, tabs, cwd, and the readiness gate are shared with Claude. # - # Dogfood-verified 2026-07-29 against opencode 1.18.5 (spawn → readiness gate → bridge_send → - # structured bridge_reply → teardown). The `opencode/*-free` models run on opencode's own gateway + # Dogfood-verified 2026-07-29 against opencode 1.18.5 (spawn → readiness gate → fleet_send → + # structured fleet_reply → teardown). The `opencode/*-free` models run on opencode's own gateway # and need NO credentials — check `opencode models` for the current free list, since the names # change. That also makes the worker off-subscription by construction. # opencode-free: @@ -447,7 +447,7 @@ fleet: # # MUST NOT be a member workspace — those are excluded from the # # scan, so a lead placed in one is never found again. # cwd: /path/to/repo # the launched lead's working directory (default: bridged's own) - # kind: claude # descriptive; reported by bridge_whoami + # kind: claude # descriptive; reported by fleet_whoami # gpt-sol-5.6: # tab: "lead: gpt-sol-5.6" # kind: opencode @@ -600,7 +600,7 @@ guard: # uri: amqp://guest:guest@127.0.0.1:5672 # prefetch: 32 -# Active push-to-primary (CB-307 Stage 3). When a worker reply lands with no open bridge_send, +# Active push-to-primary (CB-307 Stage 3). When a worker reply lands with no open fleet_send, # the ReplyPushLoop injects a *drain nudge* (never the payload) into the primary's own herdr # pane — status-gated (only when injectable, never mid-turn) and bounded. Ack = drain: the loop # stops as soon as the primary's inbox is empty. @@ -615,7 +615,7 @@ guard: # classified as a WORKER and refused SPAWN/SEND/STOP. That failure is # self-locking: the learned terminal is populated by the very orchestration # calls being refused, so only this pinned value can break the cycle. Read the -# id off bridge_whoami (it reports the current terminal even while +# id off fleet_whoami (it reports the current terminal even while # misclassified) and re-pin whenever the primary moves panes. # pushReminders → max nudges before giving up (default 5) # pushBackoffMs → delay between nudges in ms (default 15000) diff --git a/e2e/bridge_ask_test.py b/e2e/bridge_ask_test.py index 20bbabd..e19d7f5 100644 --- a/e2e/bridge_ask_test.py +++ b/e2e/bridge_ask_test.py @@ -1,16 +1,16 @@ #!/usr/bin/env python3 -"""Live bridge_ask test — the REVERSE rendezvous (CB-205), watched end to end. +"""Live fleet_ask test — the REVERSE rendezvous (CB-205), watched end to end. -Every other harness drives the forward path: primary `bridge_send` → worker `bridge_reply`. +Every other harness drives the forward path: primary `fleet_send` → worker `fleet_reply`. This drives the one that runs the other way. A worker is told to pause its delegated turn, -ask the primary a question via `bridge_ask`, and only finish once it has the answer — so the +ask the primary a question via `fleet_ask`, and only finish once it has the answer — so the turn round-trips primary→worker→primary→worker inside a SINGLE delegation. The mechanics that only this path exercises: • a worker's mid-turn question surfacing on the primary's *own* blocked send (Outcome.QUESTION), • the `turnId` correlation that lets the primary answer the exact paused turn, - • the answer resuming that same turn and the worker's final `bridge_reply` landing on the + • the answer resuming that same turn and the worker's final `fleet_reply` landing on the re-opened forward waiter (never a stale or cross-wired one). It is two blocking REST calls, no polling: @@ -57,14 +57,14 @@ ANSWER_COLOR = "blue" # A task that CANNOT be completed without asking: the worker is not told which color to choose, # only that the primary will name one when asked. So a correct final reply is only reachable by -# actually calling bridge_ask and using the answer. +# actually calling fleet_ask and using the answer. TASK_PROMPT = ( "You are a bridge worker in a quick coordination game. You do NOT know which color to pick — " "only the primary does. Do exactly this, in order:\n" - "1. Call the `bridge_ask` tool with EXACTLY this question: \"PICK A COLOR: red or blue?\"\n" + "1. Call the `fleet_ask` tool with EXACTLY this question: \"PICK A COLOR: red or blue?\"\n" "2. The primary will answer with one color word. Take that color and uppercase it.\n" - "3. Call `bridge_reply` with EXACTLY one line: CHOSEN= (e.g. CHOSEN=GREEN if told green).\n" - "Do not guess a color. Do not call bridge_reply before bridge_ask has returned an answer. " + "3. Call `fleet_reply` with EXACTLY one line: CHOSEN= (e.g. CHOSEN=GREEN if told green).\n" + "Do not guess a color. Do not call fleet_reply before fleet_ask has returned an answer. " "Do nothing else — no file reads, no other tools." ) @@ -134,7 +134,7 @@ def run(base, profile, repo, send_timeout, answer_timeout): rec.update(tid=tid, pane=pane, spawned=True) await_ready(base, tid) - # 1) Delegate the ask-forcing task. This blocks until the worker calls bridge_ask, at which + # 1) Delegate the ask-forcing task. This blocks until the worker calls fleet_ask, at which # point our own send unblocks carrying the question and the turnId to answer on. print(f"[{now()}] delegating task (blocks until the worker asks; up to {send_timeout}s)…") t0 = time.time() @@ -159,7 +159,7 @@ def run(base, profile, repo, send_timeout, answer_timeout): rec.update(phase="no_turnid", detail="question surfaced without a turnId to answer on") return rec - # 2) Answer on that exact turn. This blocks again until the resumed worker calls bridge_reply. + # 2) Answer on that exact turn. This blocks again until the resumed worker calls fleet_reply. print(f"[{now()}] answering '{ANSWER_COLOR}' on turn {rec['turnId']} (blocks until reply; up to {answer_timeout}s)…") t1 = time.time() rec["phase"] = "awaiting_reply" @@ -188,7 +188,7 @@ def run(base, profile, repo, send_timeout, answer_timeout): def grade(rec): """PASS only if the worker asked, the turn resumed, and the reply reflects the answer.""" if rec["phase"] == "no_question": - return "NO_ASK", "the worker finished/stalled without ever calling bridge_ask" + return "NO_ASK", "the worker finished/stalled without ever calling fleet_ask" if rec["phase"] in ("send_error", "answer_error", "spawn"): return "ERROR", rec.get("detail") or "transport error before the round-trip completed" if rec["phase"] == "no_turnid": @@ -203,7 +203,7 @@ def grade(rec): return "OK", "asked, resumed the same turn, and the reply reflected the primary's answer" if reflected: return "DEGRADED", f"reply reflected the answer but resolved via {rec['replySource']} " \ - "(worker did not call bridge_reply cleanly)" + "(worker did not call fleet_reply cleanly)" return "WRONG_ANSWER", f"the worker replied but did not reflect '{ANSWER_COLOR}' — " \ f"the answer may not have reached the resumed turn: {rec['reply']!r}" return "WEDGE", f"unexpected terminal phase {rec['phase']}: {rec.get('detail')}" @@ -213,16 +213,16 @@ def write_transcript(out_dir, rec, meta): path = out_dir / "bridge_ask_transcript.md" g, note = grade(rec) with path.open("w") as f: - f.write(f"# Live bridge_ask — reverse rendezvous — {datetime.now():%Y-%m-%d %H:%M}\n\n") + f.write(f"# Live fleet_ask — reverse rendezvous — {datetime.now():%Y-%m-%d %H:%M}\n\n") f.write(f"One worker paused its delegated turn to ask the primary, then resumed with the " f"answer (profile `{meta['profile']}`). Result: **`{g}`**.\n\n") f.write("## Round-trip\n\n") f.write(f"1. **primary → worker** (delegation): the ask-forcing task.\n") - f.write(f"2. **worker → primary** (`bridge_ask`, {rec.get('ask_latency')}s): " + f.write(f"2. **worker → primary** (`fleet_ask`, {rec.get('ask_latency')}s): " f"{rec.get('question')!r} — surfaced on the primary's blocked send as a " f"`question` with `turnId={rec.get('turnId')}`.\n") f.write(f"3. **primary → worker** (answer on that turn): `{ANSWER_COLOR}`.\n") - f.write(f"4. **worker → primary** (`bridge_reply`, {rec.get('answer_latency')}s, " + f.write(f"4. **worker → primary** (`fleet_reply`, {rec.get('answer_latency')}s, " f"source={rec.get('replySource')}): {rec.get('reply')!r}\n\n") f.write(f"> **{g}:** {note}\n") if rec.get("detail"): @@ -231,7 +231,7 @@ def write_transcript(out_dir, rec, meta): def main(): - ap = argparse.ArgumentParser(description="Live bridge_ask reverse-rendezvous test (CB-205)") + ap = argparse.ArgumentParser(description="Live fleet_ask reverse-rendezvous test (CB-205)") ap.add_argument("--base", default="http://127.0.0.1:8765") ap.add_argument("--profile", default=None) ap.add_argument("--repo", default=str(REPO_ROOT)) @@ -241,7 +241,7 @@ def main(): ap.add_argument("--keep-worker", action="store_true") args = ap.parse_args() - print(f"[{now()}] live bridge_ask: 1 primary, 1 worker " + print(f"[{now()}] live fleet_ask: 1 primary, 1 worker " f"(profile={args.profile or 'default'}, repo={args.repo})\n") rec = {"spawned": False, "pane": None} @@ -262,7 +262,7 @@ def main(): print() print("=" * 72) - print("LIVE bridge_ask SUMMARY — reverse rendezvous (CB-205)") + print("LIVE fleet_ask SUMMARY — reverse rendezvous (CB-205)") print(f" asked: {rec.get('question')!r} (turnId={rec.get('turnId')}, {rec.get('ask_latency')}s)") print(f" answered: {ANSWER_COLOR!r}") print(f" replied: {rec.get('reply')!r} (source={rec.get('replySource')}, {rec.get('answer_latency')}s)") diff --git a/e2e/conversation_sustained_test.py b/e2e/conversation_sustained_test.py index a7f69de..f92d03f 100644 --- a/e2e/conversation_sustained_test.py +++ b/e2e/conversation_sustained_test.py @@ -24,7 +24,7 @@ Usage: --turn-timeout per-turn max wait, seconds (default 150) --keep-worker do not stop the worker at the end -Exit code: 0 if every turn in the window resolved via a clean bridge_reply with no channel +Exit code: 0 if every turn in the window resolved via a clean fleet_reply with no channel break; 1 otherwise. A live per-turn log streams to stdout so the run can be watched. """ import argparse @@ -47,12 +47,12 @@ STEPS = [7, 3, 11, 5, 9, 4, 13, 6, 8, 2] RULES = ( "Let's play a running-total game across several messages. The total starts at 0. " "In each message I'll tell you to add a number; keep the running total yourself and " - "reply via bridge_reply with ONLY the current total as a plain integer — no words, no " + "reply via fleet_reply with ONLY the current total as a plain integer — no words, no " "punctuation, just the number. Do not restate the arithmetic. First move: add {step}." ) -NEXT = ("Add {step}. Reply via bridge_reply with only the new running total.") +NEXT = ("Add {step}. Reply via fleet_reply with only the new running total.") REANCHOR = ("Let's re-sync — the running total is {total}. Now add {step}. Reply via " - "bridge_reply with only the new running total.") + "fleet_reply with only the new running total.") def parse_int(reply): @@ -142,15 +142,15 @@ def main(): print("=" * 72) print(f"SUSTAINED CONVERSATION SUMMARY — 1 primary <-> 1 worker over {dur}s (~{dur/60:.1f} min)") print(f" turns: {turns}") - print(f" clean bridge_reply exchanges: {oks + drifts}/{turns} (channel breaks: {breaks})") + print(f" clean fleet_reply exchanges: {oks + drifts}/{turns} (channel breaks: {breaks})") print(f" arithmetic correct (continuity held): {oks}/{turns} (drifts: {drifts})") print(f" latency: avg {avg}s over {turns} turns") ok = breaks == 0 and turns >= 2 if ok and drifts == 0: - print(" RESULT: PASS — every turn resolved via bridge_reply and the worker held the " + print(" RESULT: PASS — every turn resolved via fleet_reply and the worker held the " "running total across the whole window.") elif ok: - print(f" RESULT: PASS (channel) — every turn resolved via bridge_reply for the full " + print(f" RESULT: PASS (channel) — every turn resolved via fleet_reply for the full " f"window; {drifts} arithmetic drift(s) (worker recovered after re-anchor).") else: print(" RESULT: FAIL — the channel broke on at least one turn (see CHANNEL BREAK above).") diff --git a/e2e/conversation_test.py b/e2e/conversation_test.py index 9c7647d..b4f8f46 100644 --- a/e2e/conversation_test.py +++ b/e2e/conversation_test.py @@ -4,7 +4,7 @@ the running `bridged` daemon, fully captured, with automatic gap analysis. This is the repeatable form of the ad-hoc channel test that surfaced the CB-115 gaps (herdr `unknown` misclassification, dirty completion scrape, workers not calling -bridge_reply). It drives a real off-subscription worker over the live gateway exactly +fleet_reply). It drives a real off-subscription worker over the live gateway exactly as a primary Opus session would (async fire-and-poll), records every turn, and grades the channel. @@ -130,9 +130,9 @@ def grade(rec): phase, source, reply = rec["phase"], rec["source"], rec["reply"] has_reply = bool(reply and reply.strip()) if phase == "done" and source == "reply" and has_reply: - return "OK", "clean explicit bridge_reply" + return "OK", "clean explicit fleet_reply" if phase == "done" and has_reply: - return "DEGRADED", f"resolved via {source} (worker did not call bridge_reply)" + return "DEGRADED", f"resolved via {source} (worker did not call fleet_reply)" if phase == "done" and not has_reply: return "EMPTY", "turn completed but reply was empty" if phase == "failed": @@ -206,7 +206,7 @@ def main(): ok = all(g in ("OK", "DEGRADED") for g in grades) reply_clean = all(g == "OK" for g in grades) if reply_clean: - print(" RESULT: PASS — every turn delivered and got a clean bridge_reply.") + print(" RESULT: PASS — every turn delivered and got a clean fleet_reply.") elif ok: print(" RESULT: PASS (with notes) — every turn delivered & replied, but some via fallback.") else: diff --git a/e2e/issue_hunt_test.py b/e2e/issue_hunt_test.py index 9b706cc..f798b93 100644 --- a/e2e/issue_hunt_test.py +++ b/e2e/issue_hunt_test.py @@ -12,7 +12,7 @@ and collects every reply concurrently. That stresses what a single worker never • reply routing under concurrency (worker A's answer must never resolve worker B's send), and, as the payload, whether a fleet of off-subscription workers can actually surface -real issues in the repo and report them back structurally via bridge_reply. +real issues in the repo and report them back structurally via fleet_reply. It talks ONLY to the bridge's REST face on loopback — it never sets ANTHROPIC_BASE_URL and never touches herdr directly, so it is subscription-safe by construction. @@ -64,7 +64,7 @@ PROMPT_TMPL = ( "You are one of several issue-hunting workers in the claude-bridge repo (it is your " "current working directory). Your assignment: inspect the file `{target}` and find the " "SINGLE most important real bug, correctness gap, or risk in it. Read the file before " - "answering. Reply via bridge_reply with EXACTLY these four lines:\n" + "answering. Reply via fleet_reply with EXACTLY these four lines:\n" "1. {target}:\n" "2. issue: \n" "3. fix: \n" @@ -147,9 +147,9 @@ def grade(rec): phase, source, reply = rec["phase"], rec["source"], rec["reply"] has_reply = bool(reply and reply.strip()) if phase == "done" and source == "reply" and has_reply: - return "OK", "clean explicit bridge_reply" + return "OK", "clean explicit fleet_reply" if phase == "done" and has_reply: - return "DEGRADED", f"resolved via {source} (worker did not call bridge_reply)" + return "DEGRADED", f"resolved via {source} (worker did not call fleet_reply)" if phase == "done" and not has_reply: return "EMPTY", "turn completed but reply was empty" if phase == "failed": diff --git a/plugin/.mcp.json b/plugin/.mcp.json index 05a0289..e5b6f0f 100644 --- a/plugin/.mcp.json +++ b/plugin/.mcp.json @@ -1,6 +1,6 @@ { "mcpServers": { - "bridged": { + "fleetd": { "type": "http", "url": "http://127.0.0.1:8765/mcp" } diff --git a/plugin/skills/setup/SKILL.md b/plugin/skills/setup/SKILL.md index 02a03f2..65b7d52 100644 --- a/plugin/skills/setup/SKILL.md +++ b/plugin/skills/setup/SKILL.md @@ -1,6 +1,6 @@ --- name: setup -description: Make the current project bridge-ready — check the prerequisites, mount the bridged MCP gateway into the project's .mcp.json, apply standard Claude Code settings, and verify this session resolves as the primary. Writes no credentials. Load this when asked to set up, install, configure, or onboard a project onto claude-bridge, or when bridge_* tools are expected but absent. +description: Make the current project bridge-ready — check the prerequisites, mount the bridged MCP gateway into the project's .mcp.json, apply standard Claude Code settings, and verify this session resolves as the primary. Writes no credentials. Load this when asked to set up, install, configure, or onboard a project onto claude-bridge, or when fleet_* tools are expected but absent. --- # Bridge setup — make this project bridge-ready @@ -70,7 +70,7 @@ The entry to add, exactly: ```json { "mcpServers": { - "bridged": { + "fleetd": { "type": "http", "url": "http://127.0.0.1:8765/mcp" } @@ -78,12 +78,12 @@ The entry to add, exactly: } ``` -If `.mcp.json` already exists, add only the `bridged` key and leave every other server untouched. -If a `bridged` entry is already there with a different URL, **ask** rather than assuming yours is +If `.mcp.json` already exists, add only the `fleetd` key and leave every other server untouched. +If a `fleetd` entry is already there with a different URL, **ask** rather than assuming yours is right — a non-default port usually means a deliberate second daemon. > **If this plugin is installed, you can skip this step entirely.** The plugin ships its own -> `.mcp.json`, so `bridged` is already mounted for any session with the plugin enabled. Write the +> `.mcp.json`, so `fleetd` is already mounted for any session with the plugin enabled. Write the > project-level file only when the user wants the mount to work *without* the plugin — for > teammates who have not installed it, or for CI. @@ -111,18 +111,18 @@ project already set. "$schema": "https://json.schemastore.org/claude-code-settings.json", "permissions": { "allow": [ - "mcp__bridged__bridge_whoami", - "mcp__bridged__bridge_list", - "mcp__bridged__bridge_status", - "mcp__bridged__bridge_profiles", - "mcp__bridged__bridge_poll" + "mcp__fleetd__fleet_whoami", + "mcp__fleetd__fleet_list", + "mcp__fleetd__fleet_status", + "mcp__fleetd__fleet_profiles", + "mcp__fleetd__fleet_poll" ] } } ``` -Only the **read-only** bridge verbs are pre-allowed. `bridge_spawn`, `bridge_send`, and -`bridge_stop` start processes, deliver work, and tear down terminals — those stay behind a prompt +Only the **read-only** bridge verbs are pre-allowed. `fleet_spawn`, `fleet_send`, and +`fleet_stop` start processes, deliver work, and tear down terminals — those stay behind a prompt on purpose. Do not "helpfully" add them. Never write `settings.local.json` on the user's behalf; that file is personal and usually @@ -157,7 +157,7 @@ Write none of these into any file. Show the user the `export` lines to run thems Reconnect MCP if needed (`/mcp`), then confirm the tools are live and this session is the primary: ``` -bridge_whoami +fleet_whoami ``` - `{"role":"primary"}` — correct, you are done with this step. @@ -169,7 +169,7 @@ bridge_whoami ```yaml primary: - terminal: term_xxxxxxxxxxxx # the terminalId bridge_whoami just reported + terminal: term_xxxxxxxxxxxx # the terminalId fleet_whoami just reported ``` The daemon reads this **at boot**, so it needs a restart. Re-pin whenever the primary moves @@ -178,9 +178,9 @@ bridge_whoami Then prove the fleet actually works, with a real spawn: ``` -bridge_profiles → the configured backends -bridge_spawn{profile: ""} → must reach state "ready" -bridge_stop{paneId: ""} +fleet_profiles → the configured backends +fleet_spawn{profile: ""} → must reach state "ready" +fleet_stop{paneId: ""} ``` **`/healthz` reporting `ok` is not evidence that spawning works.** It reports that the daemon can @@ -216,7 +216,7 @@ State plainly: ``` prereqs: herdr · bridged · ccs · codex written: -role: +role: spawn: env: skipped: diff --git a/scripts/redeploy-bridged.sh b/scripts/redeploy-bridged.sh index 96ad365..0f7b796 100755 --- a/scripts/redeploy-bridged.sh +++ b/scripts/redeploy-bridged.sh @@ -212,7 +212,7 @@ if [ -n "$OLD_PID" ] && [ "$ASSUME_YES" = 0 ]; then echo " A restart drops every in-flight ticket and rendezvous. A member's report" echo " is NOT recoverable once its ticket is gone." echo - echo " Confirm with bridge_list that no members are live, and bridge_poll anything" + echo " Confirm with fleet_list that no members are live, and fleet_poll anything" echo " you still want, BEFORE continuing." echo read -r -p " Fleet drained? type yes to restart: " reply @@ -356,6 +356,6 @@ else ok "no ERROR lines since restart" fi echo -echo " Next: call bridge_whoami and confirm it still answers 'primary'. A lead whose tab label" +echo " Next: call fleet_whoami and confirm it still answers 'primary'. A lead whose tab label" echo " no longer matches fleet.leaders.*.tab is demoted to worker and refuses orchestration." echo