Skip to content

Judge matchers must not block the vitest worker - #196

Merged
gaojude merged 1 commit into
mainfrom
async-judge-matchers
Aug 25, 2026
Merged

Judge matchers must not block the vitest worker#196
gaojude merged 1 commit into
mainfrom
async-judge-matchers

Conversation

@gaojude

@gaojude gaojude commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

A judge run slower than 60 seconds made vitest fail the file even when every assertion passed — the worker's RPC heartbeat shares the event loop the sync matchers were blocking, and its timeout is hardcoded. The matchers now run the judge async, so the eval's own duration no longer decides its result.

toSatisfyCriterion and toScoreAtLeast ran the in-sandbox judge with spawnSync,
freezing the worker's event loop for the length of a model run. The worker
answers the main process over an RPC channel whose per-call timeout is birpc's
default 60 seconds, hardcoded — no vitest option or env var reaches it. Any
judge slower than that left an in-flight call like onTaskUpdate unanswered, and
when the loop finally unblocked, the expired timer fired first: an unhandled
"[vitest-worker]: Timeout calling" error and a non-zero exit for a file whose
every assertion had passed. A green eval recorded as red, more often the slower
the judge model, and invisible on genuinely failing runs since those were
already red.

The judge now runs via async spawn and the matchers are async, so the loop
keeps servicing the RPC channel however long the judge takes; the sandbox
timeout remains the real bound. Judge calls must be awaited, which every
published example already shows — the await is load-bearing now, since vitest
does not track a custom matcher's promise and an un-awaited call floats free of
its test. toContainText is deterministic and stays sync.

Verified three ways: unit tests with a fake runner, including an event-loop
liveness test that fails against the old implementation; and a 70-second judge
under vitest 3 in the harness's generated-config shape, where the old helper
exits 1 with the RPC timeout despite its test passing and the new one exits 0.
@gaojude
gaojude merged commit af0ef7a into main Aug 25, 2026
4 checks passed
@gaojude
gaojude deleted the async-judge-matchers branch August 25, 2026 16:21
@github-actions github-actions Bot mentioned this pull request Aug 25, 2026
gaojude added a commit to vercel/next-evals-oss that referenced this pull request Aug 25, 2026
Picks up the async judge matchers (vercel-labs/agent-eval#196) and the
vitest reinstall guard (vercel-labs/agent-eval#195), moving the harness
onto the 2.x line.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants