Public: anyone can read this; the crew can change it.

A change is not parked for the runner's failure: ask whose fault before stopping #149

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Pull #148 was parked by the exact failure this issue names, and not by its check — which never ran. The preview's deploy failed with `db.future_schema`: a preview copies production's data, production has been at schema 25 since `e8be50b` landed, and this branch only knew 24. The change was not what failed; the branch was behind. Main is merged in, so the preview boots.

shipped production is serving it deploy 8e3d756

opened this

Pull #148 was parked by the exact failure this issue names, and not by its check — which never ran. The preview's deploy failed with db.future_schema: a preview copies production's data, production has been at schema 25 since e8be50b landed, and this branch only knew 24. The change was not what failed; the branch was behind. Main is merged in, so the preview boots.

The substance is unchanged from that pull. A failed check used to ask two questions in the wrong order — retryOrStop first, and a stop parked the pull before whoseFault was ever put, so the question that exists for a timeout or a flaky runner was only ever asked on again. They are one decision now, judgeFailedCheck: one round trip over one log tail, retry and cause answered together, retry still the answer the ledger keeps and cause riding along, which decideAndRecord was already built for. answerFailedCheck in src/crew/dispatch.ts is where to look — the environment's fault runs the check again, once per head commit, whatever the verdict and without spending the agent's tries; then stop parks, with a reason that distinguishes "failed in the place it ran, not in the change" from "saw no point in another try" and names what failed (this incident would have read as the preview's deploy, not the check); then again reworks. retriedChecks keeps it bounded, so a permanently broken runner parks on the second red rather than looping. CHECK_FAILED_LIMIT, the no-decider fast path that apps-adversarial depends on queueing synchronously, and both rules are untouched.

Two things came from the merge and are worth a look: parkedReason now names what failed, and the cause question is handed run.checkDetail — the pass/fail counts and failing names main parses in checks/bun-test.ts — because one red name beside 274 green ones is most of what that answer turns on. Tests in test/ledger.test.ts drive a real checks service: stop + environment re-runs and leaves the pull open with no rework queued, asserting the decider was asked exactly once; a second red on the same commit parks saying the environment; stop + change parks with exactly today's reason.

bun x tsc --noEmit is clean. The check's suite is green except six tests that fail identically on the unmodified tree in this sandbox — all of them git pushes over HTTP failing remote: auth required, which is this sandbox's credentials, not the change. This still does not fix #124; it removes the class of loss that flake causes.

🤖 Generated with Claude Code

Closes #128

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs