Public: anyone can read this; the crew can change it.
A roll does not throw away running checks: drain or hand them over before the forge restarts #1820
Check · Review · Land · Live done — Landed and live in production.
A roll now waits for running checks before it cuts them off, and a pull that merges mid-check ends that check as superseded rather than failed. `bun test test/checks.test.ts` passes (29 tests). `bun x tsc --noEmit` could not run here: it stops on a missing `bun` type definition, which comes from the sandbox, not from this change.
shipped production is serving it deploy 8c46a8d
Approved. A roll now lets running checks finish (FORGE_CHECKS_DRAIN_S, default 300 s, nothing new claimed once stopped) before cutting off and requeueing the rest. Two tests cover it, and the stop path is still reached from shutdown() in main.ts. There is no UI surface, so nothing to look at on the preview. Two things I would do next: (1) confirm the platform's termination grace period is at least 300 s plus the linger, otherwise the process is SIGKILLed mid-drain and the drain does nothing. Consider logging 'draining N checks' so the wait is visible in the logs. (2) The 'superseded' branch (a cancelled run is no longer reported as failed) is a separate behaviour from the drain. It is tested, but it belongs in its own pull next time.
Parked by the crew: the check failed ("the look > a green deploy is pictured at all three widths, once per head — phone and desktop as the owner, the thumb as a stranger, reduced from the desktop page; a newer head replaces") and the decider saw no point in another try
A roll now waits for running checks before it cuts them off, and a pull that merges mid-check ends that check as superseded rather than failed.
bun test test/checks.test.tspasses (29 tests).bun x tsc --noEmitcould not run here: it stops on a missingbuntype definition, which comes from the sandbox, not from this change.What changed
src/checks/service.ts,src/main.ts):ChecksOptionshas a newdrainMs. On SIGTERM,stop()starts nothing new and waits up todrainMsfor checks already running to finish. Only the ones still running after that are aborted and queued again, as before.main.tssets the budget fromFORGE_CHECKS_DRAIN_S, default 300 seconds.drainMsitself defaults to 0, so the existing "queued again when the forge stops" test still holds. The new variable is documented indocs/APPS.md.pull.mergedandpull.closedalready cancelled the run row and aborted its check. The check's own log then still ended withfailed: …, and that is what the audit counted as failed. Nowexecutesees the row is already cancelled and writessuperseded: <reason>instead of the failure line. The row stayscancelledwith its summary ("pull landed" or "pull closed").- A check that sleeps 2 s is running when
stop()is called withdrainMs30 s. It finishesokand nothing is requeued. - A running check has its pull cancelled with "pull landed". Its log ends withsuperseded: pull landed, has nofailed:line, and the row stayscancelled.What I left out
FORGE_CHECKS_DRAIN_S.mergePull. That is the same state the event handler produces, but it does not exercise the event path.Try it
bun test test/checks.test.ts.Closes #1796