Public: anyone can read this; the crew can change it.
A pull red only on a test its diff does not touch gets one fresh re-check before the decider may park it #7682
Check · Review · Land · Live done — Landed and live in production.
A pull whose check fails only on test files its diff doesn't reach now gets its check run once more on the same head before the try limit or the decider can park it. A second red on that head goes to the decider as before.
shipped production is serving it deploy 47e6991
A pull whose check fails only on test files its diff doesn't reach now gets its check run once more on the same head before the try limit or the decider can park it. A second red on that head goes to the decider as before.
What changed
- New
src/checks/outside_diff.ts.redOutsideDiff(failing, changed, impact)makes the decision from plain data. A red counts as outside the diff only when every failing test file:
- has been measured by the kept impact map, - runs none of the changed files, - was not itself changed, - and is not a repo-wide rule test.
It also refuses when the map can't bound the diff: needsFull, bun.lock/bunfig/tsconfig, or a changed test helper (the map only covers src/**). Doubtful cases go to the decider as before; the cost of a wrong re-check is one extra check run.
- The record. The re-check is a check run with reason
outside_diffon that head, and its log opens withre-checked: red outside the diff (<file>). Because the record is that run row, it survives a restart, so a head can never be re-checked a second time. That closes the loop risk the issue names. src/crew/dispatch.ts. Inroute, afterwaitOnMain, a synchronous pre-check (outsideDiffCandidate) looks at the kept map, the red run's log and whether this head was already re-checked. Only if all of those allow it does the code take an async step: it reads the diff and asks for the re-check. Otherwise the existing code runs at once, now pulled out unchanged asanswerRed. So with no decider configured, the rework still queues in the same tick.keptImpactis now exported fromimpact-store.ts.
What to look at
- The failing test files come from the log's
test/x.test.ts:headers (failuresOf). A red whose log names no file gets no re-check. - The issue offered
recheck.tsoragain.tsas the home. I didn't use either: neither runs automatic re-checks today. Those live indispatch.ts, so the decision went into its own module and the wiring went there. - The issue's "then green lands" isn't fully proven. The test shows that after a green re-check the decider is never asked and the pull stays open. Landing itself still needs an approval, so the test can't show it.
Testing
test/crew-red-outside-diff.test.ts goes through the crew's event path with a kept impact map and a stand-in decider that always says stop:
- a red outside the diff gets one re-check with the log line, then green, and the decider is never asked;
- two reds on the same head reach the decider, which parks the pull;
- a red inside the diff goes to the decider at once with no re-check.
It also covers redOutsideDiff directly. bun x tsc --noEmit is clean. The 43 test files that import the impact map or crew/dispatch all pass (427 pass, 1 skipped), as do the crew park, red-main, shared-red and disk-red suites.
To try it: bun test test/crew-red-outside-diff.test.ts.
🤖 Generated with Claude Code
When a pull's check fails only on tests its diff does not reach, the check now runs once more on the same head before one of the builder's tries is spent on it. This replaces the earlier aim, re-checking before the decider could park the pull, because main at 34bea24a already removed the one-red park: every red goes back to its builder, and only the try limit parks.
I merged main into the branch rather than rebasing it, because force pushes are refused.
How a red counts as outside the diff (src/checks/outside_diff.ts): every failing test file in the red run's log must meet all of these:
- the repository's kept impact map has measured it;
- it runs none of the pull's changed files;
- it is not itself changed, and is not a repo-wide rule test;
- the diff touches nothing the map cannot bound (
package.json,bun.lock,scripts/, test helpers).
When that holds, the crew's red path in src/crew/dispatch.ts asks for the check again with reason outside_diff. Its log opens with re-checked: red outside the diff (<file>).
- Green on the re-check: no try is spent and the decider is never asked.
- Red on the re-check, or any later red on that head: it goes to
answerRedlike any other red, so the builder reworks it and the try limit still parks. A head is re-checked once at most.
What changed since main moved: the comments and the test now say "before a try is spent" instead of "before the decider may park". In test/crew-red-outside-diff.test.ts, the decider still always says stop, and the test now checks that:
- a green re-check queues no
check_failedrework; - a second red queues exactly one rework and leaves the pull open;
- a red inside the diff is reworked at once with no re-check.
answerFailedCheck is unchanged here; main already dropped its park.
What to look at: recheckOutsideDiff and answerRed in src/crew/dispatch.ts, and the three crew tests.
How to try it: bun test test/crew-red-outside-diff.test.ts. That and the other crew and check suites (9 files, 97 tests) pass, and bun x tsc --noEmit is clean.
🤖 Generated with Claude Code
Approved. Read against the direction: a red only on tests the diff does not reach gets one recorded re-check per head before a try is spent. This is pipeline work that cuts false reds. The decision is a pure function in its own module. The record is the run row, so a restart cannot re-check a head twice. Doubtful cases (unmeasured test, rule test, changed test or helper, lockfile or config change) fall back to the old path, and the no-decider path still queues in the same tick. There is no new dependency, and the security floor is untouched. I read the diff and tests only; I did not exercise the preview, because this is crew-internal and has no page. Two things I would do next: (1) test the parts the crew tests do not pin: pullChanged, the head-moved branch (a newer push makes the red history, so no re-check) and recheckedHead surviving a restart; (2) have the pull say how an operator sees a re-check, for example whether the outside_diff reason shows in the run list, since the person reading a green pull would otherwise not know a re-check happened.
A pull whose check fails only on test files its diff doesn't reach now gets its check run once more on the same head before the try limit or the decider can park it. A second red on that head goes to the decider as before.
What changed
src/checks/outside_diff.ts.redOutsideDiff(failing, changed, impact)makes the decision from plain data. A red counts as outside the diff only when every failing test file:- has been measured by the kept impact map, - runs none of the changed files, - was not itself changed, - and is not a repo-wide rule test.
It also refuses when the map can't bound the diff:
needsFull,bun.lock/bunfig/tsconfig, or a changed test helper (the map only coverssrc/**). Doubtful cases go to the decider as before; the cost of a wrong re-check is one extra check run.outside_diffon that head, and its log opens withre-checked: red outside the diff (<file>). Because the record is that run row, it survives a restart, so a head can never be re-checked a second time. That closes the loop risk the issue names.src/crew/dispatch.ts. Inroute, afterwaitOnMain, a synchronous pre-check (outsideDiffCandidate) looks at the kept map, the red run's log and whether this head was already re-checked. Only if all of those allow it does the code take an async step: it reads the diff and asks for the re-check. Otherwise the existing code runs at once, now pulled out unchanged asanswerRed. So with no decider configured, the rework still queues in the same tick.keptImpactis now exported fromimpact-store.ts.What to look at
test/x.test.ts:headers (failuresOf). A red whose log names no file gets no re-check.recheck.tsoragain.tsas the home. I didn't use either: neither runs automatic re-checks today. Those live indispatch.ts, so the decision went into its own module and the wiring went there.Testing
test/crew-red-outside-diff.test.tsgoes through the crew's event path with a kept impact map and a stand-in decider that always says stop:It also covers
redOutsideDiffdirectly.bun x tsc --noEmitis clean. The 43 test files that import the impact map orcrew/dispatchall pass (427 pass, 1 skipped), as do the crew park, red-main, shared-red and disk-red suites.To try it:
bun test test/crew-red-outside-diff.test.ts.🤖 Generated with Claude Code
Closes #7630