fleetd #489: nudge the /clear submit keystroke before bootstrapText #490
Reference in New Issue
Block a user
Delete Branch "worker/489-001902-2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
fleetd #489 — LeadRollover /clear pickup race
Fault 1 (measured live 2026-09-12):
runRollover's second wait (after/clear) polled for IDLE/DONE, which/clearitself never leaves — it starts no real turn — so the wait always returned true on the first poll. It was a no-op.Fault 2:
runRolloverdeliberately bypassesInjectorfor/clear(to avoid wedging the pane), so it inherits none ofInjector's Enter-nudging (CB-113). The submit Enter that accompanies/clearcan race the paste and leave it unsubmitted.Combined: the roll sent
/clearthenbootstrapTextright after, in ~438ms, with no gate in between — landing as one concatenated line (/clearFresh lead session...), answeredUnknown command: /clearFresh.Fix
Replace the second wait with a new private method
waitForClearPickupAndSettle, copying the pickup-nudge patternInjectoralready ships for its own post-turn/clearhousekeeping (fleetd #306, seeInjector.java:288-340and:437-442):WORKINGsample means/clearwas picked up as a real turnagents.submit), up toPICKUP_GRACE_POLLS = 8times, then releases rather than wedges (logged atinfo)WORKINGis observed, nudging stops and it waits for a realWORKING -> IDLE/DONEboundary before returning trueBLOCKEDis deliberately excluded from both the nudge and the boundary check, same reasoning as the (unchanged) first wait (waitUntilAtTurnBoundary): a paused live turn is not settled, and nudging Enter into an open approval prompt could wrongly answer itagents.submitis swallowed atdebug, same asInjector.java:437-442The first
turnSettleSecondsgate (waitUntilAtTurnBoundary) is unchanged; its javadoc's stale "used twice" sentence is corrected to describe the split.Tests
Four new tests in
LeadRolloverTest:agent.send_keysnudge happens, in order, between the/clearprompt and thebootstrapTextprompt.bootstrapTextis sent and no nudge occurred once WORKING was observed.bootstrapTextis never sent and no nudge occurred.submit()— asserts the roll still completes andbootstrapTextis still sent.No changes to
FakeHerdrwere needed: nudge counting uses its already-exposedcallslist, and status-sequence scripting follows the file's existing idiom (a thinHerdrClientwrapper keyed on the/clearprompt call, matching the two tests already in the file —blocksAfterClear/flipsAfterClear).Verification
Tests run: 29, Failures: 0forLeadRolloverTest.agents.submit(...)nudge call):Tests run: 29, Failures: 1.waitForClearPickupAndSettlereturnstrueimmediately, the old no-op):Tests run: 29, Failures: 4.Tests run: 29, Failures: 0, green.mvn clean installfromfleetd/—Tests run: 1677, Failures: 0, Errors: 0, Skipped: 0,BUILD SUCCESS.Scope: only
LeadRollover.java(runRollover's second wait + the new method) andLeadRolloverTest.java, per the ticket.Three review corrections on top of the previous commit: 1. The class javadoc's four-step continuation list (lines 47-58) was stale. Step 1 said "report an injectable state", but waitUntilAtTurnBoundary's own javadoc excludes BLOCKED - fixed to say IDLE or DONE. Step 3 still described the old plain re-check ("the original, pre-correction wait... still here") - fixed to describe what waitForClearPickupAndSettle actually does: nudge while unpicked-up, then wait for a real WORKING -> IDLE/DONE boundary, releasing rather than wedging if WORKING never shows. 2. PICKUP_GRACE_POLLS=8 bounds the number of consecutive not-yet-picked-up polls, not the number of nudges - the 8th poll releases instead of nudging again, so 8 polls produce 7 nudges. The log.info in the release branch and two javadoc spots said "8 nudges"; fixed all three to state the poll count and the nudge count separately and correctly. Behavior and the constant are unchanged. 3. pickupSeenStopsNudgingAndBootstrapTextIsSent asserted only promptCallCount and sendKeysCallCount, both of which a return-true stub also satisfies. Added an assertion on the already-tracked postClearGetCalls counter (>= 2), which only a real post-/clear poll loop can produce - this is what makes the test fail against a return-true mutant.Follow-up: three review corrections, pushed to this branch (
f687046)1. Class javadoc's four-step list corrected
waitUntilAtTurnBoundary's own javadoc, which was already right).waitForClearPickupAndSettleactually does — nudge while no pickup seen, then wait for a real WORKING → IDLE/DONE boundary, release rather than wedge if WORKING never observed.2. Off-by-one in the nudge count fixed
PICKUP_GRACE_POLLS = 8bounds polls, not nudges — the 8th poll releases instead of nudging, so 8 polls produce 7 nudges. This was measured (setting the constant to 1 made the paste-race test see zeroagent.send_keyscalls). Fixed in three places: the constant's own javadoc, thewaitForClearPickupAndSettlejavadoc bullet, and thelog.infoin the release branch — all now state poll count and nudge count separately and correctly. Behavior and the constant value (8) are unchanged.3. Strengthened
pickupSeenStopsNudgingAndBootstrapTextIsSentAdded an assertion on the already-tracked
postClearGetCallscounter (>= 2) — this is what areturn true;stub cannot satisfy, since it never polls status again after/clear. Previously the test only checkedpromptCallCountandsendKeysCallCount, both of which a stub also passes.Verification
mvn test -Dtest=LeadRolloverTest→Tests run: 29, Failures: 0, Errors: 0, Skipped: 0.waitForClearPickupAndSettlebody withreturn true;. Proof:grep -n "MUTATION C"found the marker (mutant present);grep -n "idlePollsAwaitingPickup"found nothing, exit 1 (original loop gone). Ran suite:Tests run: 29, Failures: 5. Failing tests:clearPickupIsNudgedBeforeBootstrapTextWhenPaneStaysIdlepickupSeenStopsNudgingAndBootstrapTextIsSent← now fails, as required (it did not before this follow-up)clearThatNeverSettlesAfterwardsNeverSendsBootstrapTextblockedAfterClearNeverSendsBootstrapTextclearPickupNeverSettlesWhenStatusNeverReachesABoundarydiffagainst the saved corrected copy.mvn test -Dtest=LeadRolloverTest→Tests run: 29, Failures: 0, Errors: 0, Skipped: 0— green, so the mutation failures above aren't a broken harness.mvn clean installfromfleetd/→Tests run: 1677, Failures: 0, Errors: 0, Skipped: 0,BUILD SUCCESS.Scope: only
LeadRollover.java(javadoc + log-line text, no behavior change) andLeadRolloverTest.java(one strengthened assertion), per the ticket.