From edabccd885c29cdf32aadff39754f46525e98f00 Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Mon, 24 Aug 2026 17:48:51 +0200 Subject: [PATCH] CB-637: relabel lead-comms (was colliding CB-635) + document coordId route in the CLAUDE.md tool table The lead-to-lead wiring shipped with CB-635 in its comments, but CB-635 is already the broker.uriEnv / unreachable-broker work. Relabel the mailbox + fleet_send{coordId} + receive loop to CB-637 so a ticket number names one feature. Add the cross-host peer-lead row to the primary intent->tool table (kept byte-identical with the wiki template). --- CLAUDE.md | 3 ++- bridged/src/main/java/dev/ltms/fleet/Fleetd.java | 12 ++++++------ .../src/main/java/dev/ltms/fleet/mcp/FleetMcp.java | 10 +++++----- .../ltms/fleet/FleetdLeadMailboxSelectionTest.java | 2 +- .../dev/ltms/fleet/mcp/FleetMcpLeadCoordTest.java | 2 +- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5dcf182..a404263 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -112,7 +112,8 @@ the merge — and merging on a reviewer's word is delegating it by proxy. | 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 | +| Message a **peer lead** on this host | `fleet_send{sessionId: , content}` — `fleet_list` → `leads` reports it. Coordination only, **never** a task | +| Message a **peer lead** on another daemon or host | `fleet_send{coordId: , content}` — needs a `coordinator:` block; your own coord-id is in `fleet_list`. 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}` | diff --git a/bridged/src/main/java/dev/ltms/fleet/Fleetd.java b/bridged/src/main/java/dev/ltms/fleet/Fleetd.java index 076a946..88fc751 100644 --- a/bridged/src/main/java/dev/ltms/fleet/Fleetd.java +++ b/bridged/src/main/java/dev/ltms/fleet/Fleetd.java @@ -84,7 +84,7 @@ public final class Fleetd { /** CB-504: how long to wait at startup for herdr's socket before serving degraded. */ private static final long HERDR_WAIT_SECONDS = 30; /** - * CB-635: how often the lead coordination loop looks for peer messages. A few seconds — slow + * CB-637: how often the lead coordination loop looks for peer messages. A few seconds — slow * enough that an idle fleet is not polling a broker in a tight loop, fast enough that a peer * lead's message is not left sitting once the local lead reaches a turn boundary. The mailbox * pushes into the loop's held set on its own consumer thread, so this interval bounds only the @@ -387,7 +387,7 @@ public final class Fleetd { // unusable), bridged stays soft-state on the in-memory inbox. The AMQP inbox owns a broker // connection, so keep the reference to close it in the ordered shutdown hook. final ReplyInbox replyInbox = selectReplyInbox(cfg.broker(), System.getenv(), AmqpReplyInbox::open); - // CB-635: this daemon's lead-to-lead mailbox on the SHARED coordination vhost — a separate + // CB-637: this daemon's lead-to-lead mailbox on the SHARED coordination vhost — a separate // broker from the reply inbox by design (see FleetConfig.Coordinator). Absent a coordinator: // block this is null and every lead path below is simply not wired, which is exactly the // behaviour before this ticket. It owns a broker connection, so keep the reference for the @@ -524,7 +524,7 @@ public final class Fleetd { }, quarantine), leadMailbox); - // CB-635: the receive half. Only constructed when a lead mailbox actually opened — with no + // CB-637: the receive half. Only constructed when a lead mailbox actually opened — with no // coordinator (or an unreachable one) there is nothing to deliver, so no scheduler is // created and no thread runs. It reads the SAME live lead supplier the injector's // deliverability gate does, so a lead found by the tab scan after startup is reachable @@ -562,7 +562,7 @@ public final class Fleetd { messages.close(); pushLoop.close(); if (heartbeat != null) heartbeat.close(); // CB-551: stop the idle-lead heartbeat scheduler - if (leadCoordLoop != null) leadCoordLoop.close(); // CB-635: stop delivering peer-lead messages + if (leadCoordLoop != null) leadCoordLoop.close(); // CB-637: stop delivering peer-lead messages if (leadCoordSchedulerRef != null) leadCoordSchedulerRef.shutdownNow(); if (healthMonitor != null) healthMonitor.stop(); if (configWatcher != null) configWatcher.stop(); // CB-559: stop polling the config file @@ -576,7 +576,7 @@ public final class Fleetd { log.debug("reply inbox close: {}", e.toString()); } } - // CB-635: the coordination connection goes with it — after the loop that reads it has + // CB-637: the coordination connection goes with it — after the loop that reads it has // stopped, so no tick can be mid-ack against a closed channel. if (leadMailbox != null) { try { @@ -631,7 +631,7 @@ public final class Fleetd { } /** - * CB-635: open this daemon's lead-to-lead mailbox, or return {@code null} to leave the feature + * CB-637: open this daemon's lead-to-lead mailbox, or return {@code null} to leave the feature * off. Package-private and env-injected for the same reason as {@link #selectReplyInbox}: the * selection is then testable without a broker or a mutable process environment. * diff --git a/bridged/src/main/java/dev/ltms/fleet/mcp/FleetMcp.java b/bridged/src/main/java/dev/ltms/fleet/mcp/FleetMcp.java index dd1bcae..7e5ef03 100644 --- a/bridged/src/main/java/dev/ltms/fleet/mcp/FleetMcp.java +++ b/bridged/src/main/java/dev/ltms/fleet/mcp/FleetMcp.java @@ -100,7 +100,7 @@ public final class FleetMcp { private final CapacitySource capacity; private final HealthCoverageSource healthCoverage; private final QuarantineSource quarantine; - /** CB-635: this daemon's lead-to-lead channel; {@code null} when no coordinator is configured. */ + /** CB-637: this daemon's lead-to-lead channel; {@code null} when no coordinator is configured. */ private final LeadChannel leadChannel; /** Capacity facts used by {@code fleet_list}; production must supply the placement live count. */ @@ -141,7 +141,7 @@ public final class FleetMcp { } /** - * As above, with this daemon's lead-to-lead channel (CB-635). {@code leadChannel} is + * As above, with this daemon's lead-to-lead channel (CB-637). {@code leadChannel} is * {@code null} whenever no {@code coordinator:} block is configured or its broker could not be * reached at boot — cross-daemon lead messaging is simply off, and {@code fleet_send{coordId}} * says so rather than failing obscurely. @@ -199,7 +199,7 @@ public final class FleetMcp { String turnId = str(a, "turnId"); String coordId = str(a, "coordId"); if (coordId != null && !coordId.isBlank()) { - // CB-635: a peer LEAD on another daemon, addressed by coord-id over the shared + // CB-637: a peer LEAD on another daemon, addressed by coord-id over the shared // coordination broker. Checked before the turnId branch so a call that sets both // is rejected as the conflict it is, rather than silently taking one route. return sendToLead(leadChannel, coordId, content, target, turnId); @@ -614,7 +614,7 @@ public final class FleetMcp { } /** - * {@code fleet_send} carrying a {@code coordId} (CB-635): a message to a PEER LEAD, published to + * {@code fleet_send} carrying a {@code coordId} (CB-637): a message to a PEER LEAD, published to * that lead's durable mailbox on the shared coordination broker. This is the only lead→lead path * that crosses hosts — the existing pane-injection route can only reach a lead whose herdr socket * this daemon shares. @@ -950,7 +950,7 @@ public final class FleetMcp { } /** - * As above, additionally reporting this daemon's own lead coordination id (CB-635) when one is + * As above, additionally reporting this daemon's own lead coordination id (CB-637) when one is * configured and its channel opened. There is no peer-discovery surface yet — a lead addresses a * peer by a coord-id it was told — so this row exists to answer the one question the operator * cannot answer any other way: what is MY coord-id, the one a peer must use to reach me. It is diff --git a/bridged/src/test/java/dev/ltms/fleet/FleetdLeadMailboxSelectionTest.java b/bridged/src/test/java/dev/ltms/fleet/FleetdLeadMailboxSelectionTest.java index 9cac1eb..82cc333 100644 --- a/bridged/src/test/java/dev/ltms/fleet/FleetdLeadMailboxSelectionTest.java +++ b/bridged/src/test/java/dev/ltms/fleet/FleetdLeadMailboxSelectionTest.java @@ -15,7 +15,7 @@ import java.util.Map; import static org.junit.jupiter.api.Assertions.*; /** - * CB-635: the daemon decides whether lead-to-lead messaging is on in + * CB-637: the daemon decides whether lead-to-lead messaging is on in * {@link Fleetd#openLeadMailbox}, not in the config record — so testing * {@code Coordinator.isConfigured()} alone would pass even if {@code Fleetd} never honoured it. * These drive the real selection with an injected env map and an injected opener, so no broker is diff --git a/bridged/src/test/java/dev/ltms/fleet/mcp/FleetMcpLeadCoordTest.java b/bridged/src/test/java/dev/ltms/fleet/mcp/FleetMcpLeadCoordTest.java index 10fe555..6a3b66c 100644 --- a/bridged/src/test/java/dev/ltms/fleet/mcp/FleetMcpLeadCoordTest.java +++ b/bridged/src/test/java/dev/ltms/fleet/mcp/FleetMcpLeadCoordTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** - * CB-635: the {@code fleet_send{coordId}} route — a message to a PEER LEAD on another daemon, + * CB-637: the {@code fleet_send{coordId}} route — a message to a PEER LEAD on another daemon, * published to its durable mailbox instead of typed into a pane this daemon can reach. * *

Hermetic: {@link FakeLeadChannel} replaces the AMQP-backed {@code LeadMailbox}, so these run