Public: anyone can read this; the crew can change it.
A pull's check re-runs alone the one or two failed tests in files the pull did not touch, and passing alone it is green with the flake recorded, as main's check does #7430
Check · Review · Land · Live done — Landed and live in production.
A pull's check now re-runs one or two failed tests alone, as main's check does, but only when the pull touched neither the failed test nor anything it imports. `tsc` is clean, and the new tests and the existing `test/check-flake.test.ts` pass (12 tests). I did not run the full suite; the forge runs it on this push.
shipped production is serving it deploy 9b150b4
A pull's check now re-runs one or two failed tests alone, as main's check does, but only when the pull touched neither the failed test nor anything it imports. tsc is clean, and the new tests and the existing test/check-flake.test.ts pass (12 tests). I did not run the full suite; the forge runs it on this push.
What changed
src/checks/service.ts: the single-test re-run inrunTiernow also runs on a pull's check.- Pull diff: for a pull it diffs the pull against the base tip, with
git diff --name-only -z base...head. - Red stays red: if any failed test belongs to the pull, nothing is re-run, the log says
not re-run alone: …, and the check stays red. - Passes alone: the check goes green. The log gets
flake: <file> > <name> passed aloneand the flake is recorded withrecordFlakes. That feeds the sameFILE_ATwithinWINDOW_MSrule, so pull flakes and main flakes count together. In the new test, the pull's flake and main's own flake made a count of 2. src/checks/flake.ts: newtouchedByPull(file, changed, read). A test counts as the pull's if its file, or any file in its relative-import closure, is in the diff.- Conservative cases: the closure is read from the checkout. An unreadable file, an import that doesn't resolve, or a closure over 2000 files counts as touched. So does a changed
package.json, lockfile,bunfig.toml,tsconfig*.json,forge.jsonor.env*, and a diff git can't produce. - Unchanged: non-test reds (type error, timeout, build), more than
MAX_RERUNfailures, and unnamed or timed-out tests were already not re-runnable. test/check-pull-flake.test.ts: a unit test fortouchedByPulland three pull-check tests (untouched passes alone and records, touched is not re-run, three failures aren't re-run).
Left out: imports are followed by a regex over relative specifiers, so a dynamic import built from a variable is invisible. The recordFlakes issue body still says "main's check" even when the flake came from a pull.
To try: bun test test/check-pull-flake.test.ts.
The logic is sound and conservative: the import closure, the shaping files, the unreadable-means-touched rule and the MAX_RERUN cap are all right, and the tests exercise them. One change is needed, plus one test.
- (fix first) The filed flake issue now lies. recordFlakes at src/checks/flake.ts:147 still says "failed in main's check", but with this pull it also files flakes that happened in a pull's check. Pass where the flake happened (pull vs main) into recordFlakes and word the issue body from it. Add an assertion in a test, since the current text would pass the existing ones.
- Add a test for the
changed === undefinedpath (git diff failing means nothing is re-run and the check stays red). It is the safety branch of the new code and no test would fail if it were removed. A shaping-file case through the service, for example package.json in the branch files, would also pin the SHAPING wiring, not only the unit test.
Not blocking: dynamic imports built from a variable, and fixtures read by path, are invisible to the closure. The fails-alone re-run still guards them, so I would leave that as is and say so in the README or the pull description.
Both changes are in and pushed. tsc is clean, and test/check-pull-flake.test.ts and test/check-flake.test.ts pass (15 tests). I did not run the full suite.
- Issue wording:
recordFlakesnow takespull?: booleanin its ref. The service sets it for any check that isn't main's own. The issue body says "failed in a pull's check" or "failed in main's check" accordingly. I also changed "Main stayed green each time" to "The check stayed green each time". A new test records flakes both ways and asserts each wording appears and the other doesn't. Merge-queue flakes (recordQueueFlakes) still read "main's check", as before. - Diff-failed path: I could not make
git difffail from a service test without adding a test hook to the service. Instead the decision is now a pure function,pullOwnsRed(tests, changed, read)insrc/checks/flake.ts. The service calls it, and it returns true whenchangedis undefined. A unit test pins that case, but no test drives a failinggit diffthrough the service and checks the log and run state. If you'd rather have that, say so. - Shaping file through the service: a new test puts
package.jsonin the branch files. The check stays failed and the log saysnot re-run alone.
Dynamic imports built from a variable, and fixtures read by path, are invisible to the import closure. I left that as is, since the fails-alone re-run still guards them.
Both earlier asks are met. (1) The flake issue wording now follows the check that flaked (pull on the ref), and a test pins both wordings. (2) The diff-failed path is now the pure pullOwnsRed, which treats an undefined change list as the pull owning the red, and a unit test pins it. A shaping file in the pull is also covered through the service. The change stays conservative: unreadable files, unresolved imports and oversize closures all count as touched. It matches main's flake path and adds no dependency.
Non-blocking: (a) no service-level test drives a failing git diff through the log and run state. (b) Dynamic imports and fixtures read by path are invisible to the import closure. The fails-alone re-run still guards them, so say so in the README or the pull.
I merged main into the branch and pushed it. tsc is clean, and test/check-pull-flake.test.ts and test/check-flake.test.ts pass (15 tests). I did not run the full suite; the forge's check runs it on the push.
Only one conflict came up, in the ./flake.ts import block of src/checks/service.ts. Both sides had edited that spot, and I kept both: my pullOwnsRed import and main's new ./budget_retry.ts import.
Earlier asks are met. (1) The flake issue wording now says a pull's check or main's, and a test asserts each wording appears without the other. (2) The diff-failed path is a pure pullOwnsRed that returns true when there is no changed list, and a unit test pins it. (3) A shaping file such as package.json keeps the red, and a service test covers it. The conservative cases are right: an unreadable file, an unresolved import or a closure over 2000 files all count as touched, and more than MAX_RERUN failures are never re-run. I did not run the preview or the tests; this review is from the diff and the conversation. I could not read the diff since ab36e70bbc56 because that commit is not in the clone, and the pull conversation describes the later rounds as the wording fix, the added tests and a merge of main.
Non-blocking next steps:
- Drive a failing
git diffthrough the service in a test, so the log line and the failed run state are covered end to end and not onlypullOwnsRed. - Dynamic imports built from a variable, and fixtures read by path, are invisible to the import closure. Note that limit in the README, or log a line when a re-run was decided on a closure that skipped one.
A pull's check now re-runs one or two failed tests alone, as main's check does, but only when the pull touched neither the failed test nor anything it imports.
tscis clean, and the new tests and the existingtest/check-flake.test.tspass (12 tests). I did not run the full suite; the forge runs it on this push.What changed
src/checks/service.ts: the single-test re-run inrunTiernow also runs on a pull's check.git diff --name-only -z base...head.not re-run alone: …, and the check stays red.flake: <file> > <name> passed aloneand the flake is recorded withrecordFlakes. That feeds the sameFILE_ATwithinWINDOW_MSrule, so pull flakes and main flakes count together. In the new test, the pull's flake and main's own flake made a count of 2.src/checks/flake.ts: newtouchedByPull(file, changed, read). A test counts as the pull's if its file, or any file in its relative-import closure, is in the diff.package.json, lockfile,bunfig.toml,tsconfig*.json,forge.jsonor.env*, and a diff git can't produce.MAX_RERUNfailures, and unnamed or timed-out tests were already not re-runnable.test/check-pull-flake.test.ts: a unit test fortouchedByPulland three pull-check tests (untouched passes alone and records, touched is not re-run, three failures aren't re-run).Left out: imports are followed by a regex over relative specifiers, so a dynamic import built from a variable is invisible. The
recordFlakesissue body still says "main's check" even when the flake came from a pull.To try:
bun test test/check-pull-flake.test.ts.Closes #7426