Repository navigation
Pin CI's Rust toolchain and fix three intermittent Windows failures - #162
Merged
Merged
Conversation
Every stable job installed dtolnay/rust-toolchain's floating `stable` while the workflow sets RUSTFLAGS="-D warnings". When the runners moved to Rust 1.99 on 2026-10-02, a new std deprecation failed every CI run until 2026-10-20 without any change to the code. The six stable jobs now install the exact version in RUST_TOOLCHAIN (1.99.1, what main last passed on) through the master ref the msrv job already pins. A Rust release can no longer change CI's verdict on its own; raising the pin is a deliberate PR. CONTRIBUTING documents running the local gate on the same version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
invalid_json_endpoint read each request with a single read() and closed the socket right after answering. On Windows a socket closed with unread input sends RST, which can discard the response before the client reads it: the decode failure under test then surfaces as a transport error, so the batch never bisects (request counts [2, 2] instead of [2, 2, 1, 1]), or the fixture waits forever for a request that never comes. The fixture now reads the whole request head, shuts down its write side and drains until the client hangs up (bounded). Both tests bound server.await at 30 s so a missing request fails instead of hanging CI. Windows GNU, Rust 1.99.1: the bisection test passed 30/30 alone and the batch::tests module 10/10, against 26/30 and 4/10 on main before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test created its one-request server outside retry_transient_transport, so any retried transient error reached a closed listener and failed with an unrelated connection error. It also used a 5 s client timeout, which a busy test machine can exceed. The server is now created per attempt, as the ElevenLabs tests already do, and the client timeout is 60 s; the content-length limit under test does not depend on either. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ndows Resuming a paused job rewrites the worker's control file while the paused worker polls it with fs::read_to_string. atomic_replace installed the new file with MoveFileExW(MOVEFILE_REPLACE_EXISTING), which fails with ERROR_ACCESS_DENIED whenever another process has the destination open, so `bookforge resume` intermittently failed with "Access is denied (os error 5)". std::fs::rename uses POSIX rename semantics on Windows and succeeds in that case; a standalone probe on Rust 1.88 and 1.99.1 showed MoveFileExW failing and fs::rename succeeding over a file held open by a reader. The replace stays atomic and failure-preserving, and std falls back to MoveFileExW where POSIX renames are unsupported. A Windows regression test holds the file open across the replace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four CI-reliability fixes from the 2026-10-20 triage: one prevents a repeat of the Oct 2–20 outage, and three remove intermittent Windows failures, one of which was a real product bug.
ci:pin the Rust toolchain (F10). Every stable job installeddtolnay/rust-toolchain's floatingstablewhile the workflow setsRUSTFLAGS="-D warnings". When Rust 1.99 deprecated a std method, all of CI went red for 18 days without any code change. The six non-MSRV jobs now install the exact version inRUST_TOOLCHAIN(1.99.1) through themasterref themsrvjob already pins, so raising it is a deliberate PR. CONTRIBUTING documents running the local gate on that version.msrvstays on 1.88.0.test(llm): decode-bisection fixture (F7).invalid_json_endpointread each request with oneread()and closed right after answering. On Windows, closing a socket with unread input sends RST, which can discard the reply before the client reads it. The decode error under test then becomes a transport error, so there's no bisection ([2, 2]instead of[2, 2, 1, 1]), or the test hangs forever onserver.await. The fixture now reads the whole request head, half-closes, and drains (bounded); both tests boundserver.awaitat 30 s.test(audio): oversized-response test (F8). Its one-request server was created outsideretry_transient_transport, so every retry hit a closed port, and a 5 s client timeout could expire on a busy machine. The server is now created per attempt, as the ElevenLabs tests already do, with a 60 s client timeout.fix(control): resuming a paused job on Windows. Resume rewrites the worker's control file while the paused worker polls it.atomic_replaceusedMoveFileExW(MOVEFILE_REPLACE_EXISTING), which fails withERROR_ACCESS_DENIEDwhenever another process has the destination open, sobookforge resumeintermittently failed with "Access is denied (os error 5)". This was the lifecycle flakecli_pause_during_inflight_qa_request_parks_before_more_finalize_work.std::fs::renameuses POSIX rename semantics on Windows and succeeds in that case. The replace stays atomic and failure-preserving, and std falls back toMoveFileExWwhere POSIX renames are unsupported. CHANGELOG entry added.Validation
All on Windows (GNU), Rust 1.99.1 with
-D warnings:MoveFileExWfails withAccess is denied. (os error 5);std::fs::renamesucceeds. Same result on Rust 1.88.0 and 1.99.1. A new#[cfg(windows)]regression test does the same throughatomic_replace; the 26control::tests pass.batch::testsmodule 10/10, against 26/30 and 4/10 onmainbefore.bookforge-audiolib passes (123).lifecyclesuite passed 56/56.${{ env.RUST_TOOLCHAIN }}. The diff touches only those steps, andvalidate-release-security.shpasses.During the rebase over #161 I found and fixed a mistake in my own first version of (1): a replace-all had dropped the space after
uses:on four steps, which would have made the workflow invalid. That version was never part of an open PR.🤖 Generated with Claude Code