From cabcd87b6630bfb7bf18c4d096c77e158b9d29ba Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Tue, 1 Sep 2026 13:26:05 +0700 Subject: [PATCH] #211 follow-up: the raw-scrape fallback must carry the pane, not just the matched line The normal backend-error path appends the pane tail to the failure reason on purpose (fleetd#164): the BACKEND_ERROR pattern is a heuristic, and a member that reported *about* an error while forgetting fleet_reply matches it too, so dropping the rest of the pane destroys the report. The new raw-scrape fallback did not do that. It matters more there, not less: the fallback only runs when the trimmed assistant block was empty, so the raw scrape is the ONLY copy of whatever the member managed to say. A lead read the matched line and nothing else. Clipped to the same cap the normal path uses, since a raw screen has no boundary trimming to bound its size. The assertion was watched failing without the fix: AssertionFailedError: fleetd#164: the failure must carry the pane, not only the matched line ... expected: but was: mvn clean install: Tests run: 1079, Failures: 0, Errors: 0, Skipped: 0 --- .../java/dev/ltms/fleet/inject/CompletionResolver.java | 7 ++++++- .../java/dev/ltms/fleet/inject/CompletionResolverTest.java | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) 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 e865058..20ca1b2 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/inject/CompletionResolver.java +++ b/fleetd/src/main/java/dev/ltms/fleet/inject/CompletionResolver.java @@ -355,7 +355,12 @@ public final class CompletionResolver implements TurnListener { } String backendError = firstMatchingLine(raw, BACKEND_ERROR); if (backendError != null) { - fail(target, turn, "member " + target + " ended on a backend error: " + backendError); + // Carry the pane, not just the matched line — the same fleetd#164 rule the normal path + // above applies. Here it matters more, not less: the trimmed block was empty, so the raw + // scrape is the ONLY copy of whatever the member managed to say. Clipped to the same cap + // the normal path uses, since a raw screen has no boundary trimming to bound it. + fail(target, turn, "member " + target + " ended on a backend error: " + backendError + + "\n--- pane tail ---\n" + clip(raw)); return true; } return false; 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 326594b..d837f1f 100644 --- a/fleetd/src/test/java/dev/ltms/fleet/inject/CompletionResolverTest.java +++ b/fleetd/src/test/java/dev/ltms/fleet/inject/CompletionResolverTest.java @@ -682,6 +682,12 @@ class CompletionResolverTest { "classified BACKEND_ERROR from the raw scrape even though the trimmed block was empty"); assertTrue(waiter.getNow(null).text().contains("API Error: 400 invalid request body"), "the failure carries the matched line: " + waiter.getNow(null).text()); + assertTrue(waiter.getNow(null).text().contains("--- pane tail ---"), + "fleetd#164: the failure must carry the pane, not only the matched line — with an " + + "empty trimmed block the raw scrape is the only copy of what the member said: " + + waiter.getNow(null).text()); + assertTrue(waiter.getNow(null).text().contains("╭"), + "the carried pane is the raw scrape, chrome included: " + waiter.getNow(null).text()); } @Test