You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Open any PR whose branch is a few days behind main, or re-run test + coverage (latest ruby/rails) on a loaded runner.
Expected
A PR whose diff touches no lib/ file, and in two cases no test/ file at all, gets a green suite.
Actual
Four of the five PRs open on 2026-09-03 went red on test + coverage, and none of the failures was caused by the PR's diff. Six distinct timing-sensitive methods across five files failed on a loaded runner:
So the failures are a real, uncovered class: timing-sensitive integration tests that fail on a loaded runner. The cost is not theoretical — it is why four PRs sat red for days while their diffs were fine, and why a rebase is currently the standard remedy for a test result that had nothing to do with the change.
The three root causes are DIFFERENT — do not apply one remedy to all three
An earlier version of this issue folded all three under "make the assertion follow the clock or the
schedule rather than a fixed wall-clock margin". That fits two of them and actively misleads on the
third, which has no clock at all.
WebSearchTest — the bound hard-codes one queue; the driver is how many queues EXIST. The
original gloss ("assumes the queue did not grow under load") is wrong and the cited code says so: queue_scan_stop caps at [SCAN_LIMIT_PER_QUEUE, SCAN_BUDGET - @scanned].min
(lib/wurk/web/search.rb), so the seeded queue's SIZE cannot move the counter — the test's own
comment already notes the count is "never proportional to the queue's real size". What moves it is search_queues iterating Queue.all, which is SMEMBERS of the shared Keys::QUEUES_SET
(lib/wurk/queue.rb:33-36): one SMEMBERS + one LLEN + 100 LRANGE = exactly 102 for a single 10k
queue, which is max_round_trips with ZERO margin. Every additional member of that set adds at
least one more round trip.
WHO adds the extra members is NOT established — do not invent an answer. An earlier revision
of this issue blamed concurrent sibling methods under parallelize_me!. That is false: Wurk::Test::UnitCase overrides def self.parallelize_me! with an EMPTY body
(test/test_helper.rb:199-201), minitest-parallel_fork re-extends any genuinely parallel suite
with Minitest::Unparallelize anyway, and test/integration/swarm_boot_test.rb:92-97 states both
in prose. Two hypotheses remain consistent with the repo and neither is confirmed: a leaked
scheduled poller re-enqueuing due entries and SADDing their queues (the mechanism test: seed the API mutation sorted-set fixtures not-yet-due #520's own
comment records at test/engine/api_mutations_test.rb:297-309), or two suite runs sharing one
Redis instance on a loaded runner.
Remedy: because the writer is unidentified, the fix must hold when the set changes during the
measured window, not merely when it is larger at the start. Either derive the bound from the
queues present in a way that tolerates concurrent mutation, or give this test its own Redis
logical DB / capsule (as DemoWorkloadTest does with Wurk::Test::DEDICATED_DB) so Queue.all
can only ever see its own queue. Do NOT "isolate Keys::QUEUES_SET" by editing lib/wurk/keys.rb — that is a lib/ change, which the diff-scope criterion forbids and which
would make this slice runtime-affecting. Do NOT widen SCAN_LIMIT_PER_QUEUE or QUEUE_PAGE —
they are a real DoS guard ("a keystroke must never full-walk a multi-million-entry queue") and
this test is the only thing pinning them.
WebExtensionsTest — a due-NOW seed that a scheduled poller drains first.:189-192 seeds ZADD <name> Time.now.to_f, which is exactly the pattern test: seed the API mutation sorted-set fixtures not-yet-due #520 fixed in api_mutations_test.rb:296-310 by seeding a future score (+ 3600). That landed comment names
the mechanism: "a due entry in the worker's shared Redis DB is exactly what a scheduled poller
still running from an earlier class pops before the request lands." This is the clock/schedule
remedy, and it is a direct mirror of merged precedent.
LimiterStressTest — genuinely wall-clock dependent. 1000 acquires against limit 5 across 50
threads under a fixed wait_timeout: 15. This is the hard one; pick and state an approach in the
PR rather than tuning the number, and show how the replacement is demonstrated red.
Do not simply widen a constant, and do not delete an assertion that is testing something real.
Acceptance criteria
All three tests PASS on a quiet runner today, so bin/check green proves nothing on its own. These
criteria are what go red against current main:
WebSearchTest: a case that mutates Keys::QUEUES_SETconcurrently with the measured
window — a thread SADDing sibling queues while the search runs — still satisfies the bound
(red today, and unlike a population fixed before the counter is installed, this is the shape
that actually flakes; a Queue.all.size snapshot taken at assert time passes the static
version and still flakes)
WebExtensionsTest: a case where a due-now-seeded retry entry is drained before the request is
made still returns ["retry"] from the kinds filter (red today)
LimiterStressTest: the test asserts the ATOMICITY invariant it exists for — max_seen <= limit and limiter.size == 0 — without asserting over_limit == 0, and a case that
deliberately starves the limiter is added showing that new assertion red
no wait/timeout literal in test/integration/limiter_stress_test.rb increases relative to origin/main — wait_timeout: 15 at :36 lives inside test/**, so the diff-scope criterion
below does not reach it, and raising it is the exact "buy green by widening a constant" this
issue forbids
the diff touches only test/** — git diff --name-only origin/main...HEAD lists no path
outside test/. This is what stops "buy green by widening SCAN_LIMIT_PER_QUEUE"
the PR body states, per test, what the old assertion depended on and what the new one depends
on instead
Steps
Open any PR whose branch is a few days behind
main, or re-runtest + coverage (latest ruby/rails)on a loaded runner.Expected
A PR whose diff touches no
lib/file, and in two cases notest/file at all, gets a green suite.Actual
Four of the five PRs open on 2026-09-03 went red on
test + coverage, and none of the failures was caused by the PR's diff. Six distinct timing-sensitive methods across five files failed on a loaded runner:SwarmSupervisionTest#test_crash_loop_backoff_grows_and_term_drains_while_pendingtest/integration/swarm_supervision_test.rbTimeoutHangTest#test_a_genuinely_sleeping_job_past_its_deadline_is_abandoned_and_booked_expiredtest/integration/timeout_hang_test.rbApiMutationsTest#test_bulk_deduplicates_repeated_keystest/engine/api_mutations_test.rbWebSearchTest#test_search_bounds_round_trips_on_a_huge_no_match_queuetest/unit/web_search_test.rbLimiterStressTest#test_1000_concurrent_acquires_never_exceed_the_limittest/integration/limiter_stress_test.rbWebExtensionsTest#test_search_kinds_filtertest/engine/web_extensions_test.rbThree fixes landed on
mainon 2026-09-03/04. The bottom three have no landed fix, and they are the subject of this issue.Observed failure text, for identification:
WebSearchTest—Expected 104 to be <= 102(a bounds assertion that hard-codes ONE queue; see the corrected diagnosis below)LimiterStressTest—17 acquires timed out with a 15s waitWebExtensionsTest— expected["retry"], got[]Version
Filed against
mainatb29d659010e56f6b5cf0605cd306a3ad421b308c.Why this is worth its own issue
The three open CI issues do not cover this class, and each was checked:
lint (rubocop)passed on all five PRs. Not firing.So the failures are a real, uncovered class: timing-sensitive integration tests that fail on a loaded runner. The cost is not theoretical — it is why four PRs sat red for days while their diffs were fine, and why a rebase is currently the standard remedy for a test result that had nothing to do with the change.
The three root causes are DIFFERENT — do not apply one remedy to all three
An earlier version of this issue folded all three under "make the assertion follow the clock or the
schedule rather than a fixed wall-clock margin". That fits two of them and actively misleads on the
third, which has no clock at all.
WebSearchTest— the bound hard-codes one queue; the driver is how many queues EXIST. Theoriginal gloss ("assumes the queue did not grow under load") is wrong and the cited code says so:
queue_scan_stopcaps at[SCAN_LIMIT_PER_QUEUE, SCAN_BUDGET - @scanned].min(
lib/wurk/web/search.rb), so the seeded queue's SIZE cannot move the counter — the test's owncomment already notes the count is "never proportional to the queue's real size". What moves it is
search_queuesiteratingQueue.all, which isSMEMBERSof the sharedKeys::QUEUES_SET(
lib/wurk/queue.rb:33-36): one SMEMBERS + one LLEN + 100 LRANGE = exactly 102 for a single 10kqueue, which is
max_round_tripswith ZERO margin. Every additional member of that set adds atleast one more round trip.
WHO adds the extra members is NOT established — do not invent an answer. An earlier revision
of this issue blamed concurrent sibling methods under
parallelize_me!. That is false:Wurk::Test::UnitCaseoverridesdef self.parallelize_me!with an EMPTY body(
test/test_helper.rb:199-201),minitest-parallel_forkre-extends any genuinely parallel suitewith
Minitest::Unparallelizeanyway, andtest/integration/swarm_boot_test.rb:92-97states bothin prose. Two hypotheses remain consistent with the repo and neither is confirmed: a leaked
scheduled poller re-enqueuing due entries and SADDing their queues (the mechanism test: seed the API mutation sorted-set fixtures not-yet-due #520's own
comment records at
test/engine/api_mutations_test.rb:297-309), or two suite runs sharing oneRedis instance on a loaded runner.
Remedy: because the writer is unidentified, the fix must hold when the set changes during the
measured window, not merely when it is larger at the start. Either derive the bound from the
queues present in a way that tolerates concurrent mutation, or give this test its own Redis
logical DB / capsule (as
DemoWorkloadTestdoes withWurk::Test::DEDICATED_DB) soQueue.allcan only ever see its own queue. Do NOT "isolate
Keys::QUEUES_SET" by editinglib/wurk/keys.rb— that is alib/change, which the diff-scope criterion forbids and whichwould make this slice runtime-affecting. Do NOT widen
SCAN_LIMIT_PER_QUEUEorQUEUE_PAGE—they are a real DoS guard ("a keystroke must never full-walk a multi-million-entry queue") and
this test is the only thing pinning them.
WebExtensionsTest— a due-NOW seed that a scheduled poller drains first.:189-192seedsZADD <name> Time.now.to_f, which is exactly the pattern test: seed the API mutation sorted-set fixtures not-yet-due #520 fixed inapi_mutations_test.rb:296-310by seeding a future score (+ 3600). That landed comment namesthe mechanism: "a due entry in the worker's shared Redis DB is exactly what a scheduled poller
still running from an earlier class pops before the request lands." This is the clock/schedule
remedy, and it is a direct mirror of merged precedent.
LimiterStressTest— genuinely wall-clock dependent. 1000 acquires against limit 5 across 50threads under a fixed
wait_timeout: 15. This is the hard one; pick and state an approach in thePR rather than tuning the number, and show how the replacement is demonstrated red.
Do not simply widen a constant, and do not delete an assertion that is testing something real.
Acceptance criteria
All three tests PASS on a quiet runner today, so
bin/checkgreen proves nothing on its own. Thesecriteria are what go red against current
main:WebSearchTest: a case that mutatesKeys::QUEUES_SETconcurrently with the measuredwindow — a thread SADDing sibling queues while the search runs — still satisfies the bound
(red today, and unlike a population fixed before the counter is installed, this is the shape
that actually flakes; a
Queue.all.sizesnapshot taken at assert time passes the staticversion and still flakes)
WebExtensionsTest: a case where a due-now-seeded retry entry is drained before the request ismade still returns
["retry"]from the kinds filter (red today)LimiterStressTest: the test asserts the ATOMICITY invariant it exists for —max_seen <= limitandlimiter.size == 0— without assertingover_limit == 0, and a case thatdeliberately starves the limiter is added showing that new assertion red
test/integration/limiter_stress_test.rbincreases relative toorigin/main—wait_timeout: 15at:36lives insidetest/**, so the diff-scope criterionbelow does not reach it, and raising it is the exact "buy green by widening a constant" this
issue forbids
test/**—git diff --name-only origin/main...HEADlists no pathoutside
test/. This is what stops "buy green by wideningSCAN_LIMIT_PER_QUEUE"on instead
bin/check(lint + suite + parity)Affected paths
test/unit/web_search_test.rb, test/engine/web_extensions_test.rb, test/integration/limiter_stress_test.rb
AFK / HITL
AFK.
Verified against
42cc4e4