Skip to content

fix: stop every process a run started on Windows, whatever its parents - #492

Merged
Sequela02 merged 1 commit into
devfrom
fix/490-job
Oct 18, 2026
Merged

Sequela02 merged 1 commit into
devfrom
fix/490-job

Conversation

@Sequela02

Copy link
Copy Markdown
Contributor

Closes #490 (closed by hand on merge).

Cause (from a diagnostic on Windows CI, draft #491)

On CI, npm is a bash script, and MSYS's bash runs it through a fork that exits, leaving npm and everything below it with a parent that no longer exists:

bun <- bash (Git's wrapper) <- bash -c "npm run test"
node <- cmd <- node (npm) <- bash <- bash <- 6536 (gone)

taskkill /T, which runProcess used to stop a tree, follows parents, so a stop or a time limit ended the bash processes and left npm, the check and what it started running. On the owner's machine npm runs through sh with no such fork, so it never showed there.

Change (src/process.ts)

  • On Windows under Bun, which runs Tesota, its engine tests and the evaluation, each process runProcess starts goes into a Windows Job Object as it starts; what it starts joins the job however its parents come and go. A run a stop or its time limit ended terminates the job (TerminateJobObject), through kernel32 with bun:ffi, as the session lock already does.
  • Under Node, which only runs the root tests, and on Linux and macOS, the tree is stopped as before (taskkill /T, the process group). bun:ffi is imported by a name Node never resolves, so the verification layer still loads under Node.
  • A run that ended by itself only closes its job handle: what outlived it, a server a check left, keeps running as before.
  • The tree is now also stopped when a stop or time limit ended a run whose first process had already exited, which runProcess skipped.

Checks

  • New packages/core/tests/process-tree.test.ts reproduces On Windows, stopping a check can leave its processes running #490 on any machine: a process starts a shell (cmd /c start /b or sh -c "… &"), which starts the last process in the background and exits, so the last one has no living parent; the test checks it is alive then, stops the run, and checks it is gone. It fails on dev's process.ts on Windows (the process survives) and passes with the job. The shell is not a Node process because Bun and Node end their own children when they exit, which hid the case in a first version of the test.
  • gate.test.ts's check that a stopped check's processes are gone now runs on Windows too, as On Windows, stopping a check can leave its processes running #490's acceptance asks.
  • bun run check locally (Windows): exit 0; root 264 passed; engine 331 passed, 3 skipped; budgets within limits.

Risk, to watch in CI: the process is put in its job just after it starts, so a process it started in that instant would escape; Node cannot start one suspended. The gate test on Windows CI is the check.

On Windows CI, MSYS's bash runs npm's script through a fork that exits, which breaks the parent chain taskkill /T follows, so a stopped or timed-out check left npm and its processes running (#490). Under Bun each process runProcess starts goes into a Windows Job Object, which what it starts joins however its parents come and go, and a stop or time limit terminates the job. Under Node and elsewhere the tree is stopped as before. A test reproduces the case on any machine with a shell that starts a process in the background and exits.
@Sequela02
Sequela02 merged commit d922fe5 into dev Oct 18, 2026
12 of 13 checks passed
@Sequela02
Sequela02 deleted the fix/490-job branch October 18, 2026 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant