fix(events): settle a finished run whose last resource expired while it ran
All checks were successful
PR Checks / bot-tests (pull_request) Successful in 31s
PR Checks / client-build (pull_request) Successful in 31s
PR Checks / server-tests (pull_request) Successful in 20m48s

Found walking Rust PLAN_FIXES step 2 (run 46 on the walk core): a zone expired
while its run was still going, so expireResource left the run's cleanup status
to its terminal path. When the run was cancelled the sweep never selected it,
because runsNeedingCleanup joins on an unresolved row and it had none, so
cleanup_status sat at `pending` for ever over an empty ledger.

The sweep now settles finished runs that are `pending` with no unresolved row
as `complete` (never over `incomplete`, which is a human's to clear). Checked
against the walk database: the query returns run 46 and nothing else.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-09-26 22:19:17 -05:00
parent cc1f49af29
commit 224e2b4dbc
3 changed files with 50 additions and 0 deletions

View File

@@ -320,6 +320,16 @@ async function sweep() {
// cleanup, whose counters were spent deliberately.
const candidates = await resourcesDb.runsNeedingCleanup(CLEANUP_RUN_BATCH, MAX_REVERT_ATTEMPTS)
let swept = 0
// **A finished run with nothing left to give back is complete** (MODULE_API
// 1.11.0). The scan above joins on an unresolved row, and until `expired` no
// run could end `pending` without one. Now a zone can expire while its run is
// still going — `expireResource` leaves a live run's status to its terminal
// path — and when that run ends, nothing in the scan would ever select it:
// `pending` for ever. Found by the step-2 walk, run 46.
for (const runId of await resourcesDb.finishedRunsWithNothingLeft(CLEANUP_RUN_BATCH)) {
if (await runsDb.setCleanupStatus(runId, 'complete', ['pending'])) swept += 1
}
for (const candidate of candidates) {
if (!runsDb.TERMINAL.includes(candidate.status)) continue
try {