Skip to content

test: give the vale timeout test a real margin - #327

Merged
thecodedrift merged 2 commits into
mainfrom
fix/timeout-test-race
Sep 10, 2026
Merged

test: give the vale timeout test a real margin#327
thecodedrift merged 2 commits into
mainfrom
fix/timeout-test-race

Conversation

@thecodedrift

Copy link
Copy Markdown
Member

Why

terminates and reports a timeout rather than hanging asserted the winner of a race.

It gave a 1ms budget to a 19-byte document, on the stated grounds that "1ms cannot survive process startup." That is not something the test controls. Vale runs in its own process and does not care whether our event loop is free, so under load the timer's callback is delayed while the child keeps going, and the run completes cleanly where the test demands a timeout.

Its sibling had the identical shape and was measured failing exactly that wayValeRunOutcome.blocking > marks a timeout blocking, twice across 13 full-suite runs, reporting status: "ok" where the test wanted "timeout". That one was given a real margin during the #323 work. This one survived only because its window was narrower, not because it was safe.

What changed

The fixture grows and the budget rises, so the work outlasts the budget by a margin nothing plausible closes. Measured on this fixture, warm:

fixture bytes duration headroom over 100ms
19 (the old one) 362 ~46ms 45ms — this is what flaked
8,000 152KB ~1020ms ~920ms
17,000 (the sibling's) 323KB ~4430ms ~4330ms

8,000 rather than the sibling's 17,000. It is 20x the margin that actually flaked at a quarter of the suite cost, and this test asserts the message while the sibling already covers the blocking flag with the larger fixture.

The suite cost is ~110ms, not ~1020ms: the test kills at the budget, so the duration column is what Vale would take, not what the test pays.

maxFileBytes: Number.POSITIVE_INFINITY raises VALE_MAX_FILE_BYTES for this call only — not a CLI flag, not a config surface, just a seam. Without it a 152KB document is excluded before Vale sees it (#321) and reports status: "ok" with a notice, never exercising the timeout at all.

Verification

  • Mutation-checked: restoring the 19-byte fixture fails with expected 'ok' to be 'timeout' — the exact shape of the original flake. The fixture is load-bearing, not decoration.
  • pnpm typecheck clean, pnpm test 1412 passed.

This is not #262

Worth stating plainly, because the resemblance is misleading. #262 names two subprocess-spawning tests, in error-envelope.test.ts and verify-test-commands.test.ts, that shell out to dist/index.js. Different tests, different failure mode. That issue stays open, and a separate finding about it is posted there.

`terminates and reports a timeout rather than hanging` asserted the winner
of a race. It gave a 1ms budget to a 19-byte document on the stated grounds
that "1ms cannot survive process startup" — not something the test controls,
since Vale runs in its own process and does not care whether our event loop
is free. Under load the timer's callback is delayed while the child keeps
going, and the run completes cleanly where the test demands a timeout.

Its sibling in `ValeRunOutcome.blocking` had the identical shape and was
measured failing exactly that way, twice across 13 full-suite runs, before
it was given a real margin. This one survived only because its window was
narrower, not because it was safe.

Measured on this fixture, warm:

  fixture          bytes   duration   headroom over 100ms
  19 (the old one) 362     ~46ms      45ms  <- this flaked
  8,000            152KB   ~1020ms    ~920ms
  17,000 (sibling) 323KB   ~4430ms    ~4330ms

8,000 rather than the sibling's 17,000: 20x the margin that actually
flaked, at a quarter of the suite cost. The test still runs in ~110ms
because it kills at the budget — the duration is what Vale WOULD take, not
what the test pays.

Confirmed load-bearing by mutation: restoring the 19-byte fixture fails
with `expected 'ok' to be 'timeout'`, the exact shape of the original flake.

Note this is NOT the flake reported in #262, which names two
subprocess-spawning tests in error-envelope.test.ts and
verify-test-commands.test.ts. That one stays open.
@thecodedrift

Copy link
Copy Markdown
Member Author

@claude /review

@claude

claude Bot commented Sep 10, 2026

Copy link
Copy Markdown

Claude finished @thecodedrift's task in 2m 35s —— View job


Review complete

Review mode: incremental — read 0 prior review thread(s) before reviewing. .prior-review.json has no reviewThreads, reviews, or top-level comments besides the trigger, so this is the first review of this PR.

  • Read .prior-review.json
  • Fetch PR diff and metadata (gh pr diff 327, gh pr view 327)
  • Read the full changed file plus packages/cli/src/rules/vale/run.ts and the relevant formats.ts section for context the diff doesn't show
  • Analyze correctness, security, performance, test adequacy, clarity
  • Post findings

Assessment

This is a well-reasoned, well-documented fix for a real race (asymmetric timing budget on a subprocess-based timeout test). I traced through runVale/spawnVale in run.ts and findOversizedFiles in formats.ts to confirm the mechanics: maxFileBytes: Number.POSITIVE_INFINITY correctly bypasses the 128KB oversized-file guard for this call only (stats.size > Infinity is always false, no crash risk), the new ~156KB fixture is consistent with the documented quadratic-cost table, and the chosen 100ms budget against a ~1020ms-warm run leaves the kind of absolute margin the PR argues for. The mutation-check claim (reverting to the 19-byte fixture fails with expected 'ok' to be 'timeout') is consistent with the race described. No correctness, security, or test-adequacy problems with the mechanism itself.

Two [New] findings, both posted inline, both about the prose in the new comment block rather than the test logic:

  1. vale-run.test.ts:262 — The new comment says this test's race was looked at "until taskless/cli#262 work looked at it," but the PR's own "This is not Flaky: two subprocess-spawning tests fail intermittently on a full-suite run #262" section states Flaky: two subprocess-spawning tests fail intermittently on a full-suite run #262 names two different, subprocess-spawning tests and is explicitly a different failure mode from this one. Citing Flaky: two subprocess-spawning tests fail intermittently on a full-suite run #262 here looks like a wrong issue reference for the work this PR itself is doing — worth confirming and correcting before it becomes a permanent, misleading pointer in the codebase.
  2. vale-run.test.ts:270 — The new comment cites the sibling test failing "twice across 13 full-suite runs," while the sibling's own existing comment (further down, unchanged) says "once across four concurrent full-suite runs." Same historical event, two different counts — likely two separate measurement passes, but worth a clarifying word if so.

Nothing else surfaced: no removed invariant, no broken call site (this is test-only, no non-test code changed), no reuse/simplification issue beyond the two sibling tests sharing a lot of near-identical structure and commentary by necessity (each needs its own fixture size), and no efficiency concern — the PR body already accounts for actual suite cost (~110ms, since the process is killed at the budget).

Comment thread packages/cli/test/vale-run.test.ts Outdated
Comment thread packages/cli/test/vale-run.test.ts Outdated
Review findings on #327, both about the comment rather than the test.

The comment said this race was asserted "until #262 work
looked at it", which cites the wrong issue for the work this PR is doing.
#262 reports two subprocess-spawning tests in error-envelope.test.ts and
verify-test-commands.test.ts, a different failure mode, and never names
this one — the PR body says as much, so the comment contradicted it. A
future reader would have opened #262 and found nothing connecting it to
this fixture. The citation is kept rather than dropped, because the causal
chain is real: #262 is where the search started, not what it found.

The comment also said the sibling failed "twice across 13 full-suite runs"
while the sibling's own comment, unchanged, says "once across four
concurrent full-suite runs". Both are true and neither was wrong: they are
two measurement passes, the first while #323 was open and the second
counting captured logs recovered later. Nothing said so, which left two
comments appearing to disagree about the same event.
@thecodedrift

Copy link
Copy Markdown
Member Author

Re: @claude[bot] — "Claude finished @thecodedrift's task in 2m 35s — Review complete…"
#327 (comment)

Both findings were real and both are fixed in ff88a5f. Thanks for tracing runVale/spawnVale and findOversizedFiles to confirm the mechanism rather than taking the PR body's word for it — stats.size > Infinity always being false is exactly the property the seam relies on.

Finding 1 (wrong issue citation). Correct, and my own PR body was the evidence against my comment. The comment claimed the race was asserted "until taskless/cli#262 work looked at it" while the body says "This is not #262". A reader following that pointer would open #262, find two subprocess-spawning tests, and be unable to connect them to this fixture. The citation is kept rather than dropped, because the causal chain is real — but it now says so precisely: no issue ever flagged this test, it was found while investigating #262, and #262 remains open and unrelated.

Finding 2 (conflicting counts). Also correct, and your guess at the cause was right. Two measurement passes on the same test: once across four concurrent full-suite runs, observed while #323 was open; twice across 13, counted later from captured logs recovered from an abandoned worktree. Neither number was wrong and nothing said they were separate campaigns, which is exactly why it read as two comments disagreeing. Both passes are now named.

Both were prose defects in a comment whose entire job is explaining a non-obvious choice to a future reader, so they mattered more than their size suggests. 1412 tests pass; typecheck clean.

— AI Coding Agent

@thecodedrift
thecodedrift merged commit d846a2a into main Sep 10, 2026
6 checks passed
@thecodedrift
thecodedrift deleted the fix/timeout-test-race branch September 10, 2026 03:39
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.

1 participant