Repository navigation
test(guard): hold the clock still in three batch-deadline tests - #407
Merged
nissa-seru merged 1 commit intoOct 17, 2026
Merged
Conversation
…oad can't spend a deadline before its write starts Two of the tool-result guard's anima-research#281 deadline tests failed now and then on a loaded machine, on main and on branches that didn't touch the guard: - "a write still under way at the deadline is named as unfinished": the first write hadn't started when the batch's 50 ms ran out; - "batches behind one stalled write cost one deadline": the third batch, whose write is a real one with nothing stalling it, ran out of its 50 ms before that write began. The batch's deadline is a Date.now() time taken when the batch is queued, and spillWithheld skips the write once `deadline - Date.now()` is spent. Both tests drive whole runs, so on a busy host the steps between the queueing and the write can take longer than a short deadline. A longer deadline only makes that rarer. Both tests now hold Date still with node:test's mock timers (Date only), so no batch's deadline can be spent before its write starts, however long the steps before it take. The spill's own timer is a real setTimeout, so the first write's deadline still passes 50 ms after it starts, while the stalled write is under way, which is what the tests check. The third batch's real write gets a deadline of 60 s, far beyond what a write there takes, so a slow write isn't named unfinished. "writes that stalled together each hold the workspace as unanswered" calls spillWithheld directly, which reads the clock for its 30 ms deadline and again before the write starts, with the write's place found between: a pause of the process there can spend the deadline too, so it holds Date still as well. The file's two 50 ms tests that drive the spill's timer already hold Date and setTimeout. The tests' own wall-clock bounds stay: bounded() and until() fail a run only when it hangs for seconds, and no assertion about the guard rests on them. The code under test is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[Low impact] This test-only change appears safe to merge. SummaryMakes three guard tests less sensitive to host load by freezing
Reviews (1) · Last reviewed commit: "test(guard): hold the clock still in thr..." · Reviewed by Greptile |
This was referenced Oct 17, 2026
nissa-seru
pushed a commit
to nissa-seru/agent-framework
that referenced
this pull request
Oct 17, 2026
nissa-seru
pushed a commit
to nissa-seru/agent-framework
that referenced
this pull request
Oct 18, 2026
Brings in anima-research#407 (the guard's batch-deadline tests hold the clock), so CI tests a merge that has it; the one red job at 4275ef7 was anima-research#281's deadline test, which anima-research#407 fixed on main. No conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 task done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two of the tool-result guard's batch-deadline tests fail now and then on a loaded machine, with no change to the code they test. They now hold the clock still, so the host's load can't spend a batch's deadline before its write starts, and so does a third test with the same window. It changes tests only.
What was wrong
The batch's deadline is a
Date.now()time taken when the batch is queued:Date.now()pluswithheldSpillDeadlineMs(src/framework.ts:4175).spillWithheldskips the write oncedeadline - Date.now()is spent (:14545–:14547). These two tests set the deadline to 50 ms, and both drive whole runs, so on a busy host the steps between the batch's queueing and its first write can take all of it:A longer deadline only makes that rarer.
Changes
Both tests hold
Datestill withnode:test's mock timers, mockingDatealone. No batch's deadline can then be spent before its write starts, however long the steps before it take. The spill's own timer is a realsetTimeout, so the first write's deadline still passes 50 ms after the write starts, while the stalled write is under way, which is what both tests check. The second test's third batch, whose write is a real one with nothing stalling it, gets a deadline of 60 s, far beyond what a write there takes, so a slow write on a loaded host isn't named unfinished.The file has three other short-deadline tests:
spillWithhelddirectly with a 30 ms deadline.spillWithheldreads the clock for the deadline and again before the write starts, with the write's place found between, so a pause of the process there on a loaded host can spend it. It holdsDatestill too.DateandsetTimeoutstill, and advance them by hand.The tests' own wall-clock bounds stay as they are:
bounded()fails a run only once it has taken 5 s, anduntil()a wait after about 2 s, so a run that hangs fails rather than hanging the suite. No assertion about the guard rests on them.What each part is for
The change is for one thing: three tests of the guard's batch deadline give the same verdict on a loaded host as on an idle one, and still check what they checked.
Datestill in the three tests whose deadline is aDate.now()time, one line each. What each test checks depends on its deadline not being spent before the write starts, and with the clock held it can't be, however long the steps before the write take. The spill's own timer stays real, so a deadline still passes while a stalled write is under way. Weighed: a longer deadline, which was this change's first form. It only makes the failure rarer.Left as they are:
bounded()'s 5 s anduntil()'s 400 polls 5 ms apart, which turn a hang into a failure, and whose lengths decide no verdict on a working guard. Cut: nothing.Evidence
--test-force-exit, both failed in one on Node 20, and the second failed in a cross-package check of fix(stop): a stop at a tool boundary stops the turn #382 and in a run of the file on Node 24 at loads of 55 to 66.toolResultFilePlace, whichspillWithheldcalls between its two readings of the clock). With it, at this head, all three tests pass in this form and all three fail inmain's, the direct-call test included, on Node 24.22.0 and on Node 20.20.2.test/tool-result-guard.test.tspassed 124/124 in each of 3 runs on Node 24.22.0 without--test-force-exitand 3 on Node 20.20.2 with it, at 1-minute load averages of 39 to 51. Typecheck and build are clean.--test-force-exitand 8 on Node 20.20.2 with it, at 1-minute load averages of 31 to 61. Committed, it passed in 3 of 3 more runs on Node 24.22.0 at a clean checkout, starting at a 1-minute load average of 47.@antra-tess, @Anarchid: it will merge once its checks pass, and not before 18:00 UTC on October 17, unless you ask for changes before then.
— Ada-1017 (Claude Opus 5.5)