Repository navigation
Description
What is wrong
core/test/follow-through.test.js:374 ("a message sent again is forgotten everywhere once a turn takes it up") failed on a real CI run and passed on the rerun of the same head.
- PR ci: Compatibility nightly + local Mac CI (follows #31) #35, head
f027324e:Testrun39917377405attempt 1 —ubuntu-latest · Node 24andtests-okred on this case, withsettle(:99) reportingcondition not reached; attempt 2, same head, all green. - The PR touched
plugins/social/, docs and a changelog fragment only;core/was unchanged. Dev2 reran the case locally 5/5 green.
So it is a race, not a regression, and it costs a rerun every time it lands on a green PR.
Why it races
settle (:96-99) spins on setImmediate against a wall-clock 3 s deadline (:97). The case enables t.mock.timers.enable({ apis: ['setTimeout'] }) (:380), so any write that a setTimeout callback would flush only happens if the test calls t.mock.timers.tick(...); the file already carries flushWrites() (:104) for the same class of early read ("Writes to inflight.json land on a later tick"). On a loaded runner the wall-clock budget can therefore expire on a condition that no tick will ever satisfy, and the failure arrives as condition not reached — which names the helper, not the case.
Expected behaviour
A wait in this case is driven by the clock the test controls (or the section does not mock setTimeout while waiting), so the test cannot lose a race with the runner. A genuine failure names the condition that was not reached.
Acceptance
- The failing case's waits no longer depend on wall-clock: every wait that needs a mocked timer advances it explicitly, or
setTimeoutis not mocked in that section.settlemay keep a deadline as a guard, but a passing run must not rely on it. - Negative control: remove the clearing write the case is about, and it fails on its own assertion (
the exchange a turn took up is still noted on disk), not onsettle'scondition not reached. node --test test/follow-through.test.js100 consecutive runs, green; plus one CI run of the file green (thepackagejob step that runscore/suffices).- No retry, no skip, no larger timeout as the fix.
Kill conditions
- Raising the 3 s budget: it hides the race and keeps the same failure on a slower runner.
--test-retry,test.skip, or catching the assertion.- Loosening the assertion (for example accepting either
0or1noted items). - Fixing only this case while leaving
settle/flushWritesas the default way every other case waits: the next case added to this file inherits the race.
Out of scope
- The
:347case ("a message a shutdown cancelled is sent again once …"), unless the same fix covers it for free. - The host's write timing itself: if a write can land after every tick, that is a product question and needs its own issue with evidence.
- Other suites; this issue is about this file's waits.
Activity
HugoXK commentedon Oct 23, 2026
Written-by: Dev-2
Claiming #821 for
fix/821-follow-through-mocked-clock(head repo HugoXK/FlyBot, cut from upstreammain6f9b60c). Scope as accepted: the waits incore/test/follow-through.test.jsadvance the clock the case controls instead of racing the wall clock, and the cleared-note wait is judged by its own assertion so the negative control fails there. No other branch or PR exists for this issue.Fix the real-clock wait bound in follow-through.test.js (#1012)
Give a real-clock wait in follow-through.test.js a real bound (ThinkF…
test(follow-through): keep a real-clock floor under a mocked-clock wa…