diff --git a/fleetd/src/main/java/dev/ltms/fleet/lead/LeadRollover.java b/fleetd/src/main/java/dev/ltms/fleet/lead/LeadRollover.java index 07bfd3a..b2054cd 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/lead/LeadRollover.java +++ b/fleetd/src/main/java/dev/ltms/fleet/lead/LeadRollover.java @@ -44,15 +44,18 @@ import java.util.function.Supplier; * {@code continuationRunner} before returning. That continuation is what actually touches the pane, * once the calling turn has ended, in this order: *
    - *
  1. wait for the lead's own pane to report an injectable state — i.e. wait for the very - * {@code confirm()} call that approved this roll to finish its turn — bounded by - * {@code turnSettleSeconds}. If this never happens, nothing else in this list runs: - * no {@code /clear} is ever sent. A lead that never goes idle is a lead still doing - * real work, and clearing it would throw away live context — exactly the failure this - * correction exists to prevent.
  2. + *
  3. wait for the lead's own pane to report a real turn boundary — {@code IDLE} or {@code + * DONE}, never merely {@code BLOCKED} — i.e. wait for the very {@code confirm()} call that + * approved this roll to finish its turn — bounded by {@code turnSettleSeconds}. If + * this never happens, nothing else in this list runs: no {@code /clear} is ever sent. + * A lead that never goes idle is a lead still doing real work, and clearing it would throw + * away live context — exactly the failure this correction exists to prevent.
  4. *
  5. {@code agents.send(lead, "/clear")}
  6. - *
  7. wait again for the pane to report injectable, bounded by {@code clearSettleSeconds} (this - * is the original, pre-correction wait — still here, just no longer the only one)
  8. + *
  9. wait for {@code /clear} to be picked up and settle, bounded by {@code clearSettleSeconds} + * (fleetd #489: no longer a plain re-check of the same boundary — {@code /clear} starts no + * turn of its own, so this instead nudges the submit keystroke while no pickup has been seen, + * then waits for a real {@code WORKING} → {@code IDLE}/{@code DONE} boundary once one has; + * see {@link #waitForClearPickupAndSettle})
  10. *
  11. {@code agents.send(lead, cfg.bootstrapTextFor(p.handoverPath()))}
  12. *
* A {@link #confirm} that returns {@link RollDecision#approved()} therefore means "every gate @@ -112,10 +115,13 @@ public final class LeadRollover { static final long SETTLE_POLL_MS = 250; /** - * How many consecutive not-yet-picked-up polls {@link #waitForClearPickupAndSettle} nudges the - * submit keystroke before releasing rather than wedging the roll — the same constant and the - * same release-not-wedge choice {@link dev.ltms.fleet.inject.Injector} already makes for its own - * post-turn {@code /clear} housekeeping (fleetd #306). + * How many consecutive not-yet-picked-up polls {@link #waitForClearPickupAndSettle} allows + * before releasing rather than wedging the roll — the same constant and the same + * release-not-wedge choice {@link dev.ltms.fleet.inject.Injector} already makes for its own + * post-turn {@code /clear} housekeeping (fleetd #306). This bounds the number of + * consecutive polls, not the number of nudges: the first {@code PICKUP_GRACE_POLLS - 1} + * of those polls each send a nudge, and the {@code PICKUP_GRACE_POLLS}th releases instead of + * nudging again — so 8 polls produce 7 nudges, not 8. */ static final int PICKUP_GRACE_POLLS = 8; @@ -510,13 +516,16 @@ public final class LeadRollover { * @@ -558,8 +567,9 @@ public final class LeadRollover { } if (++idlePollsAwaitingPickup >= PICKUP_GRACE_POLLS) { log.info("lead-rollover: /clear on {} was never observed as WORKING after {} " - + "nudges — releasing rather than wedging the roll", - target, PICKUP_GRACE_POLLS); + + "consecutive IDLE/DONE polls ({} of those were nudged) — " + + "releasing rather than wedging the roll", + target, PICKUP_GRACE_POLLS, PICKUP_GRACE_POLLS - 1); return true; } try { diff --git a/fleetd/src/test/java/dev/ltms/fleet/lead/LeadRolloverTest.java b/fleetd/src/test/java/dev/ltms/fleet/lead/LeadRolloverTest.java index 94ea58e..6d85796 100644 --- a/fleetd/src/test/java/dev/ltms/fleet/lead/LeadRolloverTest.java +++ b/fleetd/src/test/java/dev/ltms/fleet/lead/LeadRolloverTest.java @@ -548,6 +548,12 @@ class LeadRolloverTest { + "still complete the full roll — /clear then bootstrapText"); assertEquals(0, sendKeysCallCount(fake), "once WORKING was observed, nudging must stop " + "immediately — no agent.send_keys call should ever have been needed or sent"); + assertTrue(postClearGetCalls.get() >= 2, "the pane's status must have been polled AGAIN " + + "after the WORKING sample, before bootstrapText was sent — this is what proves " + + "the method actually waited for the WORKING -> IDLE completion boundary instead " + + "of returning as soon as pickup was seen (or worse, without polling at all, as a " + + "stub that just returns true would); got " + postClearGetCalls.get() + + " agent.get call(s) after /clear"); } @Test