From 9d37f3aa29c8100d2a37459112286c621e76fd22 Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Thu, 3 Sep 2026 12:29:27 +0700 Subject: [PATCH] fleetd #201: the coverage line must name the key its caller actually means MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found by reading a real boot log after the redeploy, not by a test. coverage() is shared by two call sites — CB-578's exhaustedPattern line and Unit 5's errorPattern line — but its 'off' branch hard-coded the word exhaustedPattern. So this daemon printed: backend-exhausted classification (CB-578 stage A): partial (configured: [sol, terra]; not configured: [...]) backend-error classification (fleetd #201 Unit 5): off (no profile has an exhaustedPattern configured; profiles: [...]) Two lines, one directly under the other, disagreeing about whether any profile has an exhaustedPattern. Both were individually defensible and together they were nonsense. Worse, the message sends an operator to set the wrong key: the thing that is missing is errorPattern. coverage now takes the key name. I changed the signature rather than adding an overload, so the compiler found all three existing callers instead of leaving them silently on the old path. Every earlier coverage test passed the exhaustion case only, which is why none of them could see this. The new test pins the errorPattern case. Reverting the fix turns it red with 0 compile errors. 1229 tests, 0 failures, BUILD SUCCESS. This is the second defect in two hours found only by reading the live startup log — see #115, where the noise of a false warning had been hiding a correct line saying a whole feature was off. --- .../src/main/java/dev/ltms/fleet/Fleetd.java | 6 +++-- .../ltms/fleet/inject/CompletionResolver.java | 4 ++-- .../fleet/inject/CompletionResolverTest.java | 24 ++++++++++++++++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java b/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java index bf9a5d0..8d4cae3 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java +++ b/fleetd/src/main/java/dev/ltms/fleet/Fleetd.java @@ -351,7 +351,8 @@ public final class Fleetd { .map(session -> exhaustedPatternsByProfile.get(session.profile())) .orElse(null); log.info("backend-exhausted classification (CB-578 stage A): {}", - CompletionResolver.coverage(cfg.profiles().keySet(), exhaustedPatternsByProfile.keySet())); + CompletionResolver.coverage("exhaustedPattern", cfg.profiles().keySet(), + exhaustedPatternsByProfile.keySet())); // fleetd #201 Unit 5: classify a completion-fallback scrape that matches a profile's // configured backend-error refusal (a credential outage, a provider 5xx) as a backend error // rather than handing it back as a real answer. Compiled once at startup, keyed by profile @@ -371,7 +372,8 @@ public final class Fleetd { BackendErrorPatternLookup backendErrorPatterns = backendErrorPatternLookup(sessions::roster, errorPatternsByProfile); log.info("backend-error classification (fleetd #201 Unit 5): {}", - CompletionResolver.coverage(cfg.profiles().keySet(), errorPatternsByProfile.keySet())); + CompletionResolver.coverage("errorPattern", cfg.profiles().keySet(), + errorPatternsByProfile.keySet())); // CB-578 stage B: on a classification that actually wins, quarantine the exhausted profile's // CREDENTIAL — not the profile name — so a profile sharing that credential (e.g. two models // on one OpenAI account) is refused too, not just the one that happened to report it. Reads diff --git a/fleetd/src/main/java/dev/ltms/fleet/inject/CompletionResolver.java b/fleetd/src/main/java/dev/ltms/fleet/inject/CompletionResolver.java index 202eab2..c806fd8 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/inject/CompletionResolver.java +++ b/fleetd/src/main/java/dev/ltms/fleet/inject/CompletionResolver.java @@ -620,9 +620,9 @@ public final class CompletionResolver implements TurnListener { * @param allProfiles every configured profile name * @param configuredProfiles the subset of {@code allProfiles} that carry an exhausted pattern */ - public static String coverage(Set allProfiles, Set configuredProfiles) { + public static String coverage(String patternKey, Set allProfiles, Set configuredProfiles) { if (configuredProfiles.isEmpty()) { - return "off (no profile has an exhaustedPattern configured; profiles: " + sorted(allProfiles) + ")"; + return "off (no profile has an " + patternKey + " configured; profiles: " + sorted(allProfiles) + ")"; } Set unconfigured = new TreeSet<>(allProfiles); unconfigured.removeAll(configuredProfiles); diff --git a/fleetd/src/test/java/dev/ltms/fleet/inject/CompletionResolverTest.java b/fleetd/src/test/java/dev/ltms/fleet/inject/CompletionResolverTest.java index ad11a37..e056b6b 100644 --- a/fleetd/src/test/java/dev/ltms/fleet/inject/CompletionResolverTest.java +++ b/fleetd/src/test/java/dev/ltms/fleet/inject/CompletionResolverTest.java @@ -768,19 +768,37 @@ class CompletionResolverTest { @Test void coverageIsOffWhenNoProfileHasAPatternConfigured() { assertEquals("off (no profile has an exhaustedPattern configured; profiles: [terra])", - CompletionResolver.coverage(Set.of("terra"), Set.of())); + CompletionResolver.coverage("exhaustedPattern", Set.of("terra"), Set.of())); } @Test void coverageIsFullWhenEveryProfileHasAPatternConfigured() { assertEquals("full (all profiles configured: [gx10, terra])", - CompletionResolver.coverage(Set.of("terra", "gx10"), Set.of("terra", "gx10"))); + CompletionResolver.coverage("exhaustedPattern", Set.of("terra", "gx10"), Set.of("terra", "gx10"))); } @Test void coverageIsPartialAndNamesWhichProfilesAreConfigured() { assertEquals("partial (configured: [terra]; not configured: [gx10])", - CompletionResolver.coverage(Set.of("terra", "gx10"), Set.of("terra"))); + CompletionResolver.coverage("exhaustedPattern", Set.of("terra", "gx10"), Set.of("terra"))); + } + + /** + * Found live on 2026-09-03, reading a real boot log rather than a test. {@code coverage} is + * shared by two call sites — CB-578's {@code exhaustedPattern} line and fleetd#201 Unit 5's + * {@code errorPattern} line — but its "off" branch hard-coded the word {@code exhaustedPattern}. + * So a daemon with no {@code errorPattern} anywhere printed "no profile has an exhaustedPattern + * configured" directly beneath a line reporting that two profiles DO have one. Both lines were + * individually defensible and together they were nonsense, and the message sent an operator to + * set the wrong key. + * + *

Every earlier test here passed the exhaustion case only, so none of them could see it. This + * one pins that the message names the key the caller actually meant. + */ + @Test + void coverageNamesTheConfigKeyItsCallerMeansRatherThanAlwaysSayingExhaustedPattern() { + assertEquals("off (no profile has an errorPattern configured; profiles: [gx10, terra])", + CompletionResolver.coverage("errorPattern", Set.of("terra", "gx10"), Set.of())); } // --- fleetd#201 Unit 1: target-keyed backend-error pattern + typed sink ----------------------