Skip to content

feat(core): recover incremental results after a crash - #6212

Draft
davidshq wants to merge 9 commits into
stryker-mutator:masterfrom
davidshq:feat/incremental-crash-recovery
Draft

feat(core): recover incremental results after a crash#6212
davidshq wants to merge 9 commits into
stryker-mutator:masterfrom
davidshq:feat/incremental-crash-recovery

Conversation

@davidshq

@davidshq davidshq commented Sep 4, 2026

Copy link
Copy Markdown

Summary

  • Main: Recover --incremental results after a crash. Persist completed mutants in a new pending journal next to incrementalFile so the next run can reuse them after SIGKILL, OOM, or an uncaught exception—not only after a handled signal (extends feat(core): save partial incremental report on unexpected exit #5986). On master, interrupted runs only flush a partial report to incrementalFile on Ctrl+C-style interrupts; there was no pending journal.
  • Wire the journal through project reading, mutation testing, and the report helper so recovered results participate in the normal incremental path.
  • Document the pending directory, ignore it from the sandbox, and have stryker init gitignore reports/stryker-incremental.* (report + pending artifacts).
  • Also restore --inPlace file backups on unexpected process exit (including process.exit) without dropping async signal handlers used for partial incremental reports.

Replaces #6211 (that PR was based on the 9.6.x-era incremental-recovery branch). The 9.6.x work remains available on the fork as incremental-recovery for anyone who wants it.

Test plan

  • Unit tests for IncrementalJournal, unexpected-exit handler, sandbox restore, and mutation-test report helper
  • e2e incremental-interrupt (crash mid-run, then recover on next incremental run)
  • e2e incremental still passes
  • Manual: run with --incremental, kill the process mid-run, confirm pending journal exists and next run reuses results
  • Manual: --inPlace run interrupted via process.exit restores original files

Persist completed mutants in a pending journal next to incrementalFile so the next --incremental run can reuse them after SIGKILL, OOM, or an uncaught exception, not only after a signal.
Freeze the WAL before compacting so in-flight results cannot vanish,
skip plan-time appends, and document hard-kill / concurrent-run limits.
- Re-add a synchronous process.on('exit') path so backups are restored when
termination is not a handled signal (e.g. process.exit), without dropping
async signal handlers used for partial incremental reports.
If pending promotion fails, begin() staged by wiping .prev/.next and could
delete the only on-disk journal. Stage in the other directory until the new
pending pair is durable, and cover both failure paths in unit tests.
@davidshq
davidshq marked this pull request as draft September 7, 2026 14:27
-  `pnpm run clean` recreates `dist`, breaking the hard links pnpm's injected deps rely on, so the e2e tests silently run the old build.
- Check before running, and point at `pnpm install --frozen-lockfile`.
- `sync-dependencies-meta-injected` has no matching dependency, and  `local-release` was added by accident earlier on this branch.
… file

- Incremental runs build the report twice, so a missing source or test file warned twice.
- Track what has been warned. Also share the    extension-to-language map with the incremental journal instead of keeping a second copy.
- copyDir now calls mkdirSync(recursive) unconditionally instead of  checking existsSync first.
- Append `.pending` rather than replacing the extension: `incrementalFile: "x.pending"` used to map onto the report itself, and `a.json`/`a.html` shared one WAL.
- Drop promoteToPending. load() now only records which of `.prev`/`.next` holds the WAL, and begin() stages into the other, closing the window where the only copy could be deleted before the rename landed.
- Also normalize path options to posix before matching them as ignore globs, so `reports\foo.json` is ignored on Windows.
…as stale

- Only set syncHandlersRan when all sync handlers succeed so --inPlace restore can retry on exit.
- Treat injected e2e copies as stale when a copied file has no readable source counterpart.
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