Why
A job that reports it has finished moves its run along in the same request, and that request also started the jobs it freed. On 2026-10-20, Deploy #443's core (ts) and core (runner-image) were started that way while no 4-core container was free. The containers library spent seconds retrying, the finished job's container disconnected, and its request was cancelled along with the starts inside it. Both jobs had already been marked as running. The runner's logs show 730 such cancellations in two days. #183 recovers these jobs after 3 minutes; this change stops the cancellation from happening.
What changes
- In a request (
fetch),start_queuednow only notes that jobs want starting (defer_starts,starts_wanted). fetchbuilds its answer, then starts those jobs withctx.wait_until, which the caller leaving can't cancel.- The every-minute sweep and the queue consumers still start jobs inline, as before, because nothing there can disconnect.
- A start that still gets cut short (after
wait_until's 30 s) is retried by #183's 3-minutenever_reportedrule.
The RPC handlers themselves are unchanged. They moved into answer() so fetch can act after the reply. Jobs are still claimed with WHERE status = 'queued', so concurrent requests can't start the same job twice.
Checked
cargo test -p g1t-actions-service (121), and clippy on the native and wasm builds.