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