diff --git a/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java b/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java index ecf73e0..d06c01f 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java +++ b/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java @@ -673,9 +673,9 @@ public final class Fleetd { outageSource, new FleetMcp.LeadSeatSource(leadSeatLookup(() -> config.get().profiles(), leaders, leads)), // fleetd #602 gauge-wiring: threads each lead's configured configDir into the - // context gauge — see leadConfigDirLookup's own doc for why this, not a hardcoded + // context gauge — see leadConfigDirSource's own doc for why this, not a hardcoded // null, is what fleet_list's context row now reads. - new FleetMcp.LeadConfigDirSource(leadConfigDirLookup(() -> config.get().profiles(), leaders)), + leadConfigDirSource(() -> config.get().profiles(), leaders), // fleetd #361: the operator-declared peers this daemon's fleet_list should try to // reach. Read from the SAME snapshot leadMailbox itself opened from (cfg.coordinator()), // not the live config.get() — coordinator wiring is already boot-time-fixed (see @@ -1611,6 +1611,27 @@ public final class Fleetd { }; } + /** + * fleetd #602 gauge-wiring follow-up (PR #606 review comment 17353): {@code main} used to build + * {@code new FleetMcp.LeadConfigDirSource(leadConfigDirLookup(...))} inline, with nothing a test + * could call directly. Measured on that shape: replacing the whole expression with {@code + * FleetMcp.LeadConfigDirSource.none()} at the call site compiled with 0 errors and left the full + * 1822-test suite green — the daemon could be changed to always report every lead's context as + * {@code UNKNOWN}, forever, while every test stayed green. That is the same hand-built-vs-wired + * shape as fleetd #561/#248/#426/#562 ({@link #loopHealthSource}). + * + *

The fix extracts the inline {@code new} into this factory, in the same style as {@link + * #loopHealthSource}/{@link #capacitySource}/{@link #healthCoverageSource} — which is exactly + * what makes it directly callable from {@code FleetdLeadConfigDirSourceWiringTest}. That test + * calls this factory with real {@link FleetConfig.Profile}/{@link FleetConfig.Leader} fixtures and + * asserts the returned source resolves a real {@code configDir} — a property that would be false + * if this method's body were mutated to {@code return FleetMcp.LeadConfigDirSource.none();}. + */ + static FleetMcp.LeadConfigDirSource leadConfigDirSource(Supplier> profiles, + Map leaders) { + return new FleetMcp.LeadConfigDirSource(leadConfigDirLookup(profiles, leaders)); + } + /** * fleetd #248 / fleetd#201 Unit 5: package-private factory for the per-target backend-error * pattern lookup {@link CompletionResolver} classifies a pane scrape against. Closes over the diff --git a/fleetd/src/test/java/dev/ltms/fleet/FleetdLeadConfigDirSourceWiringTest.java b/fleetd/src/test/java/dev/ltms/fleet/FleetdLeadConfigDirSourceWiringTest.java new file mode 100644 index 0000000..9f2872a --- /dev/null +++ b/fleetd/src/test/java/dev/ltms/fleet/FleetdLeadConfigDirSourceWiringTest.java @@ -0,0 +1,98 @@ +package dev.ltms.fleet; + +import dev.ltms.fleet.config.FleetConfig; +import dev.ltms.fleet.mcp.FleetMcp; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * fleetd #602 gauge-wiring follow-up (PR #606 review comment 17353): {@code Fleetd.main}'s {@code + * LeadConfigDirSource} local used to be a bare {@code new FleetMcp.LeadConfigDirSource( + * leadConfigDirLookup(...))} built inline, with nothing a test could call directly. Measured on + * that shape: replacing the whole expression with {@code FleetMcp.LeadConfigDirSource.none()} at + * the call site compiled with 0 errors and left the full 1822-test suite green — the daemon could + * be changed to always report every lead's context as {@code UNKNOWN}, forever, and no test would + * notice. That is the same hand-built-vs-config-wired shape as fleetd #561/#248/#426/#562 + * ({@code FleetdLoopHealthSourceWiringTest}). + * + *

{@code FleetMcpLeadContextGaugeWiringTest} and {@code FleetdLeadConfigDirLookupTest} both + * predate this class and are both still correct — but neither can catch the mutation above. One + * builds its own {@code FleetMcp} and hands it its own {@code LeadConfigDirSource}; the other + * builds its own lookup and calls {@link Fleetd#leadConfigDirLookup} directly. Neither one ever + * calls the thing {@code Fleetd.main} actually calls. + * + *

The fix extracts the inline {@code new} into {@link Fleetd#leadConfigDirSource}, a + * package-private factory in the same style as {@link Fleetd#loopHealthSource}/{@link + * Fleetd#capacitySource}/{@link Fleetd#healthCoverageSource} — which is exactly what makes it + * directly callable here. This test calls that factory with real {@link FleetConfig.Profile}/ + * {@link FleetConfig.Leader} fixtures (the same shapes {@code FleetdLeadConfigDirLookupTest} + * already uses) and asserts the returned source resolves a real {@code configDir} — a property + * that would be false if {@link Fleetd#leadConfigDirSource} were mutated to {@code return + * FleetMcp.LeadConfigDirSource.none();}. Measured: mutating exactly that line makes + * {@link #resolvesTheRealConfiguredConfigDir()} fail ({@code expected: but was: + * }); restoring it makes the whole suite green again. + * + *

What this class does not and cannot cover. {@code main}'s own line — + * {@code leadConfigDirSource(() -> config.get().profiles(), leaders)} — could itself be swapped + * for a bare {@code FleetMcp.LeadConfigDirSource.none()}, bypassing this factory entirely. Measured: + * that exact mutation compiles with 0 errors and leaves every test in this file, and the full + * 1825-test suite, green. {@link Fleetd#loopHealthSource}'s own wiring test has the identical gap + * for its own one-line call in {@code main} — no test in this codebase calls {@code Fleetd.main} + * far enough to observe which factory call it made. This class narrows the gap from "nothing tests + * the wiring" (the pre-extraction state this ticket found) to "the factory's own logic is pinned, + * and main's call to it is a one-line, visually-verifiable delegation" — the same standard already + * accepted for {@code loopHealthSource}/{@code capacitySource}/{@code healthCoverageSource}. + */ +class FleetdLeadConfigDirSourceWiringTest { + + private static FleetConfig.Profile profileWithConfigDir(String name, String configDir) { + return new FleetConfig.Profile(name, null, "claude-sonnet-5", configDir, null, null, + "tab", "fleet", "w #{n}", null, null, null, null, null, null, null, + null, 3, true, null, null, null); + } + + private static FleetConfig.Leader leadOnProfile(String profile) { + return new FleetConfig.Leader(profile, "lead: primary", 1, "lead:", 10, "claude", "claude-sonnet-5"); + } + + @Test + @DisplayName("the returned source resolves the lead's REAL configured configDir, not a hardcoded null") + void resolvesTheRealConfiguredConfigDir() { + Map profiles = Map.of("opus", profileWithConfigDir("opus", "/mnt/opus-claude")); + Map leaders = Map.of("primary", leadOnProfile("opus")); + + FleetMcp.LeadConfigDirSource source = Fleetd.leadConfigDirSource(() -> profiles, leaders); + + assertEquals("/mnt/opus-claude", source.configDirFor().apply("primary"), + "the configDirFor function must delegate to the real leadConfigDirLookup — mutating " + + "Fleetd.leadConfigDirSource's own body to `return FleetMcp.LeadConfigDirSource.none();` " + + "must fail this assertion (measured: it does — see this test's class javadoc for the " + + "companion measurement on main's one-line call to this factory, which this assertion " + + "does not and structurally cannot cover)"); + } + + @Test + @DisplayName("a lead on a profile with no configDir override still resolves to null, not a crash") + void leadWithNoConfigDirOverrideResolvesToNull() { + Map profiles = Map.of("opus", profileWithConfigDir("opus", null)); + Map leaders = Map.of("primary", leadOnProfile("opus")); + + FleetMcp.LeadConfigDirSource source = Fleetd.leadConfigDirSource(() -> profiles, leaders); + + assertNull(source.configDirFor().apply("primary"), + "no configDir: override configured ⇒ null, so LeadContextGauge falls back to its own default"); + } + + @Test + @DisplayName("an unrecognised lead name resolves to null, not a thrown exception") + void unrecognisedLeadNameResolvesToNull() { + FleetMcp.LeadConfigDirSource source = Fleetd.leadConfigDirSource(Map::of, Map.of()); + + assertNull(source.configDirFor().apply("ghost-lead")); + } +}