fleetd #241: bound the echo match so a real report is never swallowed
Round 1 used plain bidirectional containment. The direction that catches the real bug -- the pane holds the brief plus a status bar, so the scrape contains the brief -- also fires when a member restates the whole brief and then writes a genuine report under it. That threw the report away and told the lead nothing was produced, which is worse than the bug being fixed: it destroys a delivery instead of merely obscuring one. The safe direction (the scrape is a fragment of the brief) stays unbounded, because a fragment of the brief is by definition not a report. The dangerous direction now requires the scrape to add at most MAX_ECHO_EXCESS_CHARS beyond the brief, which is the amount of TUI chrome a real echo carries. Work by the cb241 worker, committed by the lead: its backend stopped answering after the fix was written, so two turns ended with no commit and no reply. Verified by the lead: 1169 tests, 0 failures; removing the bound turns pinsTheMaximumTuiChromeExcess and completionFallbackKeepsARealReportThatRestatesTheWholeBrief red with 0 compile errors.
This commit is contained in:
@@ -89,6 +89,9 @@ public final class CompletionResolver implements TurnListener {
|
||||
/** A pane echo must be this large before it can replace a completion report. */
|
||||
static final int ECHO_MIN_CHARS = 400;
|
||||
|
||||
/** Normalised TUI chrome may add this many characters to an otherwise echoed brief. */
|
||||
static final int MAX_ECHO_EXCESS_CHARS = 160;
|
||||
|
||||
/** The explicit result returned instead of a lead's echoed injected brief. */
|
||||
public static final String NO_REPORT_PREFIX = "[no report — the member ended its turn without fleet_reply, "
|
||||
+ "and the pane still shows the injected brief. Nothing was produced on the pane. Check the "
|
||||
@@ -421,9 +424,9 @@ public final class CompletionResolver implements TurnListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* A full echoed brief is at least 400 normalised characters and one normalised value contains the
|
||||
* other. This accepts harmless TUI whitespace and punctuation changes, but preserves a real report
|
||||
* that quotes only one part of the brief.
|
||||
* A full echoed brief is at least 400 normalised characters. A scrape that contains the brief may
|
||||
* add no more than 160 normalised characters of TUI chrome. This accepts harmless status text, but
|
||||
* preserves a real report that restates the full brief before adding substantive content.
|
||||
*/
|
||||
static boolean echoesInjectedBrief(String scrape, String injectedText) {
|
||||
String normalScrape = normalize(scrape);
|
||||
@@ -431,7 +434,11 @@ public final class CompletionResolver implements TurnListener {
|
||||
if (normalScrape.length() < ECHO_MIN_CHARS || normalInjected.length() < ECHO_MIN_CHARS) {
|
||||
return false;
|
||||
}
|
||||
return normalInjected.contains(normalScrape) || normalScrape.contains(normalInjected);
|
||||
if (normalInjected.contains(normalScrape)) {
|
||||
return true;
|
||||
}
|
||||
return normalScrape.contains(normalInjected)
|
||||
&& normalScrape.length() - normalInjected.length() <= MAX_ECHO_EXCESS_CHARS;
|
||||
}
|
||||
|
||||
private static String normalize(String text) {
|
||||
|
||||
@@ -196,6 +196,26 @@ class CompletionResolverTest {
|
||||
assertEquals("complete report", waiter.getNow(null).text());
|
||||
}
|
||||
|
||||
@Test
|
||||
void suppressesABareEchoWithOnlyTuiChrome() {
|
||||
String injected = "Load the implementer skill. You own fleetd #999. ".repeat(12);
|
||||
String scrape = injected + "\nDev auto - GPT-5.6 Terra OpenAI";
|
||||
|
||||
assertTrue(CompletionResolver.echoesInjectedBrief(scrape, injected));
|
||||
}
|
||||
|
||||
@Test
|
||||
void pinsTheMaximumTuiChromeExcess() {
|
||||
String injected = "a".repeat(CompletionResolver.ECHO_MIN_CHARS);
|
||||
String underMargin = injected + "b".repeat(CompletionResolver.MAX_ECHO_EXCESS_CHARS);
|
||||
String overMargin = injected + "b".repeat(CompletionResolver.MAX_ECHO_EXCESS_CHARS + 1);
|
||||
|
||||
assertTrue(CompletionResolver.echoesInjectedBrief(underMargin, injected),
|
||||
"the configured excess itself remains an echoed brief");
|
||||
assertFalse(CompletionResolver.echoesInjectedBrief(overMargin, injected),
|
||||
"one character beyond the excess must preserve the scrape as a real report");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolvesSynchronouslyBeforePostTurnContextClearing() {
|
||||
FakeHerdr herdr = new FakeHerdr().readText("⏺ previous answer\n❯ ");
|
||||
|
||||
@@ -109,11 +109,11 @@ class MessageServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void completionFallbackKeepsARealReportThatQuotesPartOfTheBrief() throws Exception {
|
||||
String quotedAcceptance = "Acceptance: the new test passes and the build is green. ".repeat(4);
|
||||
String brief = quotedAcceptance + "Implement the requested change. ".repeat(20);
|
||||
String report = "I changed the fallback and added tests. " + quotedAcceptance
|
||||
+ "The final build passed.";
|
||||
void completionFallbackKeepsARealReportThatRestatesTheWholeBrief() throws Exception {
|
||||
String brief = "Load the implementer skill. You own fleetd #999. ".repeat(12);
|
||||
String report = brief + "\n\n## Report\n"
|
||||
+ ("I implemented the fix in GitWorktrees.java, added five tests, ran mvn clean install "
|
||||
+ "and got 1168 tests with 0 failures. Commit 321d8dc pushed. ").repeat(8);
|
||||
CompletableFuture<MessageService.Reply> send = sendAsync(brief);
|
||||
awaitWaiting();
|
||||
|
||||
@@ -125,7 +125,7 @@ class MessageServiceTest {
|
||||
|
||||
MessageService.Reply reply = send.get(5, TimeUnit.SECONDS);
|
||||
assertEquals(MessageService.Outcome.COMPLETED_UNREPLIED, reply.outcome());
|
||||
assertEquals(report, reply.text(), "a report that quotes part of the brief must survive unchanged");
|
||||
assertEquals(report.strip(), reply.text(), "a report that restates the whole brief must survive unchanged");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user