Skip to content

Wall-clock-sensitive tests fail intermittently on loaded CI runners #710

Description

@mohanagy

Current disposition — 10 September 2026

OPEN — unresolved asynchronous session failures remain here. The former collection has been separated so clock assertions and worker startup are no longer treated as one cause.

Reported behavior Current owner
Recovery-budget assertion depends on a 1 ms real-time window and 3 ms busy wait #755 — focused deterministic-clock story
Requested test module fails to start or execute #756 — deferred, bounded lifecycle diagnosis
MCP discovery times out while a graph request waits for startup This issue — unresolved
An active stdio session times out while refreshing after a workspace change This issue — unresolved

For the two remaining session cases, the desired outcome is that the public session remains responsive and observes a workspace change. Their causal explanation is still uncertain. Preserve the original failures and existing guards. A same-code retry passing does not prove an infrastructure-only cause or exclude a product race; the historical heading claiming these are not product regressions is not an established conclusion.

Do not close this issue when #755 is fixed. Close it only when the remaining session failures have specific resolved evidence, or when each has a focused successor retaining the evidence and unresolved status. No diagnosis, tests, timeout changes or implementation are launched by this triage.

This issue is independent of #754 except when a concrete failure prevents trustworthy execution. It is no longer an umbrella directing automatic recovery work.

Historical issue body — preserved; current disposition above takes precedence

Current disposition — 5 September 2026

OPEN — independent timing and worker-lifecycle maintenance. Current roadmap: #740. Keep deterministic test-timing failures distinct from worker-start/lifecycle failures; preserve the guarded requested-versus-executed test ledger and do not treat isolated passes as a full-suite pass. This work is independent of #741's research and evaluation-contract preparation, and blocks future evaluation only when trustworthy execution is prevented. This reconciliation does not authorize coding or automatically resume implementation.

The original scope and evidence below, and all existing comments, are preserved as history.


Three consecutive protected matrices during PR #709 each lost exactly one lane,
a different lane each time, always to a wall-clock-sensitive test. Two of the
tests are untouched by that PR. This issue collects the family so it is tracked
rather than absorbed as noise.

Affected tests

Test File Touched by #709
keeps MCP discovery responsive while one graph request waits for slow startup tests/unit/background-auto-refresh.test.ts no — last changed 2026-07-17
refreshes an active stdio session after an agent changes its workspace tests/unit/stdio-server.test.ts yes, read migration only (218dd7c7)
does not relabel a completed final attempt as a time-budget abort tests/unit/context-pack-recovery.test.ts no

Evidence

Run Lane Test Observed
32142031007 windows-latest Node 20 background-auto-refresh timed out at 30,008 ms
32145436260 windows-latest Node 20 same test 4,009 ms — pass
32148986352 windows-latest Node 22 stdio-server watcher timed out at 11,344 ms
32145436260 windows-latest Node 22 same test 4,442 ms — pass
32139276516 windows-latest Node 22 same test 4,719 ms — pass
32150482216 (1st attempt) macos-latest Node 22 context-pack-recovery expected 1 call, got 0
32150482216 (rerun) macos-latest Node 22 same test pass

The background-auto-refresh case was retested on byte-identical production
code
— 71cce3f3 is documentation-only on top of a8a94a8c — and ran in
3,615 ms. Same code, both a timeout and a 3.6 s pass.

Why these are not product regressions

The outcomes are bimodal: roughly 4 s or the full timeout, never a gradual
slope. Two of the three tests have no commits in PR #709 at all. Every failure
was retested on identical code and passed. Nothing was changed to make any of
them pass — no timeout raised, no test skipped, no assertion loosened.

context-pack-recovery is the clearest of the three: it asserts one call under
maxElapsedMs: 1 while the callback it measures busy-waits 3 ms. On a loaded
runner the budget can expire before the first call is made, giving 0 calls.
A 1 ms wall-clock budget is not reliably observable on a shared CI runner.

Why raising Vitest timeouts is not the fix

Measured on vitest 4.1.10 in this repository: a synchronous test body that
busy-waits 3,000 ms under a 1,000 ms timeout runs to completion and is only
then reported as timed out. A per-test timeout is therefore a post-hoc
elapsed-time verdict with no phase attribution and no deadlock protection
for the many synchronous tests here. Raising a budget past a real one hides
contention rather than preventing a hang; real hang protection has to come from
timeout on each execFileSync/spawnSync call, or from moving the work into
a child with a parent-side deadline.

Suggested direction

  • Replace absolute wall-clock budgets with injected clocks or fake timers where
    the test is about ordering rather than duration.
  • Where a real deadline is the contract, assert it in a child process with a
    parent-side timeout so a regression fails fast and attributably.
  • Keep the durations in the record: a test that normally runs in 4 s against a
    30 s budget is not short of budget, and raising it would not have helped.

Refs #705.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions