Skip to content

fix: gate worker bootstrap on explicit flag instead of process.send - #176

Merged
merencia merged 2 commits into
masterfrom
fix/worker-bootstrap-sentinel
Jun 18, 2026
Merged

fix: gate worker bootstrap on explicit flag instead of process.send#176
merencia merged 2 commits into
masterfrom
fix/worker-bootstrap-sentinel

Conversation

@merencia

Copy link
Copy Markdown
Contributor

Problem

Closes #175.

Importing Sidequest (even without calling start()) inside a Vitest test running with pool: 'forks' makes Vitest throw.

The main worker module (packages/engine/src/workers/main.ts) ran its bootstrap block as a top-level import side effect, gated on !!process.send. That heuristic is meant to answer "am I the worker the engine forked?", but process.send is defined in any process forked over IPC, including a Vitest pool: 'forks' test worker. So when such a worker transitively imports the engine, the bootstrap ran and:

  1. registered a process.on("message") listener,
  2. registered a process.on("disconnect") handler that calls process.exit(),
  3. called process.send("ready"), which the host IPC layer can't handle.

Fix

Pass an explicit --sidequest-worker argv flag when the engine forks the worker, and gate the bootstrap on process.argv.includes(WORKER_PROCESS_FLAG) instead of !!process.send. The whole block (all three side effects) now only runs when the engine actually forks the worker.

argv is used rather than an env var on purpose: piscina worker threads inherit process.env, so an env var would leak into the job execution environment; the fork's argv does not.

The readystart handshake is unchanged, so the normal start path is unaffected.

Tests

  • New integration test forks the built main.js and asserts: no "ready" without the flag (reproduces the issue), "ready" with the flag.
  • Existing engine tests (44) still pass, including the ones that fork and complete the handshake.

The main worker module self-initialized whenever process.send was defined,
which is true for any process forked over IPC. A Vitest pool:'forks' test
worker that transitively imports the engine therefore ran the bootstrap block
and emitted a "ready" message the host IPC layer can't handle, breaking the
test run.

Pass an explicit --sidequest-worker argv flag when the engine forks the worker
and gate the bootstrap on it, so the module stays inert when merely imported.

Closes #175
@merencia
merencia requested a review from GiovaniGuizzo as a code owner June 18, 2026 17:18
The setup action ran `yarn set version berry`, which downloads the latest Yarn
(4.17.0) and ignores the `packageManager: [email protected]` pin. The newer Yarn
migrates the lockfile metadata from version 9 to 10, so `yarn install
--frozen-lockfile` fails with YN0028 (lockfile would be modified).

Corepack is already enabled and provisions the pinned 4.14.1 from
package.json, so the explicit version step is redundant and harmful. Drop it
to keep installs deterministic.
@merencia
merencia merged commit 3a8dee2 into master Jun 18, 2026
6 checks passed
@merencia
merencia deleted the fix/worker-bootstrap-sentinel branch June 18, 2026 17:36
sidequest-release Bot pushed a commit that referenced this pull request Jun 18, 2026
## [1.15.1](v1.15.0...v1.15.1) (2026-06-18)

### Bug Fixes

* gate worker bootstrap on explicit flag instead of process.send ([#176](#176)) ([3a8dee2](3a8dee2)), closes [#175](#175)
@sidequest-release

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.15.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Worker calls to process.send(...) break Vitest tests when using --pool=forks

2 participants