Repository navigation
test(cli): stabilize issue 8656 stall test on Windows - #14152
Conversation
The stall fixture mirrored its state to disk with an awaited write-temp then rename, running inside the simulated provider fetch. On Windows the rename can fail transiently while the test's 200ms state poll or Defender holds the destination open. A single failure rejected the simulated response, so the agent turn ended before the stall and the until() assertion could never succeed, which raising the budget could not fix. Retry transient replace failures, treat the state file as diagnostics that must not reject the response, and tolerate a transient empty read.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Incremental review of Files Reviewed (2 files)
Previous Review Summary (commit ce18a2f)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit ce18a2f)Status: No Issues Found | Recommendation: Merge Test-only stabilization of the issue #8656 stall fixture. Verified that the bounded rename retry still propagates non-transient errors, the Files Reviewed (1 file)
Reviewed by deepseek-v4.1-flash · Input: 34.5K · Output: 5K · Cached: 240.9K Review guidance: REVIEW.md from base branch |
The simulated provider response no longer waits on the state mirror, so fixture disk latency can never race the 2s request timeout. The bounded test waits for the asynchronous recovery write before asserting on the state file.
What was flaky
packages/opencode/test/kilocode/issue-8656-stall.test.ts, case "recovers instead of freezing once the stall is bounded", failed only on the Windows shardunit (windows, 4/6). Theuntilpoll waited 90s forstalls > 0 && parts.includes("step-finish:tool-calls")and never saw it. The test-runner retry failed identically in a fresh process.Root cause
In both failing runs the agent turn closed about half a second after the model catalog fetch, long before the 2s stall guard could fire, so the turn never reached the stall and the poll condition was permanently unsatisfiable. That is why raising the Windows budget from 60s to 90s in e958d44 changed nothing.
The fixture mirrored its state to disk with an awaited write-temp-then-rename inside the simulated provider fetch. That coupled the simulated provider response to disk IO in two ways:
stalls: 0;Forcing one state write to reject locally reproduces the exact CI shape: state
calls:1, stalls:0, timeline only the user text, poll exhausts the budget. The Windows-side trigger is inferred from that shape and from the runner log (Defender exclusions fail on the Windows runner); the CI log itself does not print the fixture error.Change
Test code only, no product change:
fixture/stall-transport.ts: the response no longer waits on the state mirror. Writes stay serialized, retry transient Windows file errors (EPERM/EACCES/EBUSY), and a failed write is logged instead of propagated. Every write stores the full current state, so a later write still lands anything an earlier failed one dropped.readStallStatetolerates a transient empty read.issue-8656-stall.test.ts: after the turn goes idle, wait for the asynchronous recovery write before asserting on the state file.The provider first-byte guard behavior is unchanged and still exercised.
Validation
bun test ./test/kilocode/issue-8656-stall.test.ts: 7 consecutive passes, timelinetext | step-start | tool:bash:completed | step-finish:tool-calls | step-start | text | step-finish:stop, state{calls:4, stalls:1, recovered:1}.bun run typecheckfrompackages/opencode: pass.bun run script/check-opencode-annotations.ts --worktree: no shared upstream files changed.No changeset: test-only change.