From e93b5f651270551cfab72518f2c3f69a6c9aec0a Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Fri, 4 Sep 2026 10:59:10 +0700 Subject: [PATCH] #282: record that answer()'s QUESTION guard is defence in depth, not load-bearing Measured after merging: removing the guard alone leaves the new test green, because ask() calls markAsyncQuestion before resolveQuestion, so the task has already moved to the new turnId. The PR claimed each half was necessary; only the pair is. Keeping the guard, with the ordering written down so nobody deletes it as dead code or trusts it as the only protection. --- .../src/main/java/dev/ltms/fleet/msg/MessageService.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fleetd/src/main/java/dev/ltms/fleet/msg/MessageService.java b/fleetd/src/main/java/dev/ltms/fleet/msg/MessageService.java index 04e825b..2f9869a 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/msg/MessageService.java +++ b/fleetd/src/main/java/dev/ltms/fleet/msg/MessageService.java @@ -948,6 +948,14 @@ public final class MessageService { // the worker chained a second fleet_ask before replying. Mirror sendAsync's own guard // (:1000) and leave the ticket open (markAsyncQuestion above already re-armed it under // the new turnId) instead of completing it here with a QUESTION "reply". + // Measured when #282 was merged: this guard is DEFENCE IN DEPTH, not the thing + // that makes the chained ask work. ask() calls markAsyncQuestion (:860) before + // resolveQuestion (:861), so by the time this thread wakes, the task has already + // moved to the new turnId and finishAsyncTask(oldTurnId, ...) finds nothing. Removing + // this guard alone leaves the test green. Keep it anyway: it mirrors sendAsync's + // sibling guard, and that sibling's own comment (:1017) warns the two orderings are + // not something to rely on. Do NOT delete it as dead code without re-checking that + // ordering, and do not treat it as the sole protection either. if (result.outcome() != Outcome.QUESTION) { finishAsyncTask(turnId, result); }