fix(repo): repair the vm runner and config subpath after 11.0.0 - #110
Merged
Merged
Conversation
The vm session added a failed `(no test suite)` result for every discovered file that registered no tests, ignoring Vitest's `passWithNoTests`. The flag now travels from the resolved Vitest project config through the schema, defaults and extraction, and the session consults the file's own project before reporting the failure, mirroring Vitest's `!file.tasks.length && !config.passWithNoTests` check Closes #107
The vm runner handed its in-memory vitest only to importers inside the sandbox prefix or to files under node_modules/<served name>. A setup file or helper outside the project root therefore received the real, runner-less vitest: a module-scope registration either failed with "Vitest failed to find the runner" or silently registered nothing, stopping the dry run as init-failed. Track the modules a run loads — any importer inside an active sandbox, any importer carrying a run salt, and any module reached from one of those — and serve the harness vitest to all of them. The served shims now read the current run state when their registration API is called, so an unsalted, shared module registers against the current test file's runner rather than a stale session's Closes #108
The vm runner replaced every textual `import.meta.vitest` in a loaded
module with a global regex, so a string or template literal, a comment,
or a regex literal that mentioned the marker reached the program
corrupted. A suite whose fixtures contain the marker (a lint-rule
RuleTester, for example) failed on the vm runner while passing under
`vitest run`, because Vitest only rewrites modules it discovered through
`includeSource`.
Replace the regex with a scanner that rewrites only real member
expressions and copies string, template, comment and regex text byte for
byte. The scanner tracks template substitutions (an expression inside
`${…}` is still rewritten), escaped quotes and backticks, and picks
between a regex literal and division from the last significant token.
Both the host-transform path (`hostResult`) and the loaded-source path of
the transform plugin use the scanner, so in-source `if (import.meta.vitest)`
tests still register and run
Closes #106
… cwd The vm test runner's Vitest host worker resolved @systemfsoftware/stryker-vm-harness/package.json with a createRequire rooted at process.cwd(). Under pnpm's default isolated node_modules layout the harness is installed beside @systemfsoftware/stryker-js and is not visible from the project root, so a project that installed only stryker-js and vitest failed its dry run with: Cannot find module '@systemfsoftware/stryker-vm-harness/package.json'. The lookup now uses createRequire(import.meta.url), resolving the harness from where the harness itself is installed — the same self-resolution worker-client.ts already uses — while the project's own vitest keeps resolving from the project. The dist-less fallback also pointed at a stale src/shell/vitest-host directory; it now points at src/vitest-host, so a source checkout with no build still starts the host worker Closes #105
The compound-pack refactor moved the config helpers onto a `StrykerConfig`
class, so `@systemfsoftware/stryker-js/config` stopped exporting
`defineConfig` and `mergeConfig` and `StrykerConfig` became a class-instance
type. A `stryker.config.ts` written against the 10.1.1 surface then failed at
load time, and `const config: StrykerConfig = defineConfig({ … })` failed to
typecheck, with nothing in the 11.0.0 changelog explaining why.
`defineConfig` returns the four 10.1.1 overloads (object, promise, and the
`(env: ConfigEnv) => …` factory forms) and `mergeConfig(defaults, overrides)`
composes presets again, both as named exports of `./config`. `StrykerConfig`
names the partial options a config file writes. The schema-consuming runtime
values moved out of `stryker-config.schema.ts` into `config/default-options.ts`
so the schema module only declares schemas and types
Closes #104
A failing dry run reported only counts, so the stage failure, its cause and the machine-mode error envelope named neither the failing tests nor their assertion messages. The dry-run decision now carries every failing test's name and failure message (DryRunFailed.failedTests, fed from the runner's failed TestResults); the stage failure's reason renders them and the dry run logs them at error level, so the default-level log and the error event both name each failure Closes #100
The real-vitest reference arm re-copied the fixture and spawned a cold 'vitest run --update' for every mutant, and both arms ran the full vm mutation engine to list the same mutants. Record the unmutated baseline once per fixture, copy it per mutant, and share one cached engine report between the arms; the mocking mutant case drops from 65s to 32s locally Refs #110
effect-tsgo rejects a Context.Service assigned to a variable; a named shape interface keeps the declaration emit free of the handle's private slot type Refs #110
The selection scenario instrumented the whole 528-mutant corpus eight times and crossed the 30s timeout under CI coverage; it now reads one placement site per mutator and keeps the same checks Refs #110
…ment reads Generating whole mutation reports per draw pushed three laws past the 30s timeout on CI Refs #110
A fixed 300ms window flaked under CI load; each suite now waits for its sibling to start Refs #110
…he bug V8 13.6 corrupts escaped JSON object keys once a lone-backslash key is parsed (nodejs/node#63785); the law domain now draws only unescaped keys and the ineffective seed pin is removed Refs #110
macOS links /tmp to /private/tmp, so results keyed by the real path never matched the sandbox path the test looked up Refs #110
systemfsoftware-maker
added this pull request to stack #112
September 25, 2026 14:15
ryanleecode
approved these changes
Sep 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Six regressions users hit after upgrading to 11.0.0 now work again:
defineConfig/mergeConfigconfig files, strict-pnpm installs of the defaultvmrunner, and four vm runner divergences fromvitest run.import { defineConfig, mergeConfig } from '@systemfsoftware/stryker-js/config'loads and typechecks again, andconst config: StrykerConfig = defineConfig({...})typechecks. The 11.0.0StrykerConfigclass statics are removed; the changeset is a major and gives the migration.stryker-jsandvitestcompletes avmdry run. The harness is now located from its own install instead of from the project's cwd.effectis now a required peer rather than an optional one: optional peers are never installed, so the CLI failed to read its config withouteffect, even though it importseffectat runtime.import.meta.vitestinside a string, template, comment or regex literal is no longer rewritten, so RuleTester-style fixtures pass.passWithNoTests.vitest, so a module-scopebeforeEachno longer fails with "Vitest failed to find the runner".CI repairs
main's CI was already red (typescript-checker#lint:tsgo, instrumenter and test-contribution timeouts), and this branch surfaced three more flakes. All are fixed at the root, with no suppressions and no raised timeouts:TypeScriptCompileris declared as aContext.Serviceclass, which clears thelint:tsgoerror.passWithNoTestsharness test resolves its sandbox throughrealPath, because macOS links/tmpto/private/tmp.Not in this PR
#100's enterprise-fixture parity half. Its hand-written live-export and Vite-id patches were dropped in favour of the vm runner redesign, #111, which is layer 2 of stack #112 on top of this PR. The four enterprise e2e configs stay on the vitest runner until then.
Fixes #104
Fixes #105
Fixes #106
Fixes #107
Fixes #108
Related: #100
Validation
Local:
pnpm format:check,pnpm typecheck,pnpm test,pnpm check:ciand the changeset check pass. CI is green on ubuntu and macOS, including the e2e lane. For #105 I packedstryker-jsand installed it into a fresh pnpm project (default isolated linker) whose only direct dependencies werestryker-jsandvitest. There, avmdry run and a mutation run both exit 0. That strict-pnpm install has no in-repo test: the e2e bake installs every tarball with npm.