Skip to content

fix(oracle-gaps): retire a batch's copies from Vitest's state - #61

Draft
KyleAMathews wants to merge 3 commits into
mainfrom
fix/gap-cli-retention
Draft

KyleAMathews wants to merge 3 commits into
mainfrom
fix/gap-cli-retention

Conversation

@KyleAMathews

@KyleAMathews KyleAMathews commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The gap CLI kept every test of every mutant copy alive for the whole check. Each mutant runs in a new copy of its test file, and Vitest's state holds each copy's file and test tasks (filesMap by path, idMap by task id) and never releases them. On the klona four-file suite (295 mutants, two contexts), a 2 GB heap runs out and the check fails. The default 4 GB heap is close to the limit (3.66 GB peak in the child).

This PR retires a batch's copies from Vitest's state once their units are plain data. It removes their stale idMap entries behind a shape guard, then calls Vitest's state.clearFiles. clearFiles exists with the same semantics in Vitest 4.1.12 and 5.1.0. On its own it leaves the test tasks in idMap, so the explicit delete is needed.

Decisions

  • P10 (one diagnostic per failure): unchanged for this PR. The first line, the baseline line, and the single done line per stream stay fixed, and the exit code is unchanged. Stream-line order is not a contract, per the coordinator ruling on P10 and P11 (register C entry, 2026-11-13). The one reordered gap line between the baseline and GREEN runs is therefore not a P10 contract break.
  • P11 (a report says what it could not judge): unchanged. Results are compared by stable key; results.json is the stable record (the same C ruling).
  • P9 (framework changes are oracle-first): this is a CLI change with no oracle-observable gap. Per the coordinator ruling on P9 (register C entry, 2026-11-13), CLI-level RED, GREEN, and hostile mutants on a real suite meet P9. The evidence below is that measurement.
  • Fail-loud guard on Vitest internals: retireCopies checks that state.filesMap and state.idMap are Maps and throws otherwise. It calls clearFiles, which exists in Vitest 4.1.12 and 5.1.0.

Evidence

  • Law: A check's memory holds what it must keep (the per-run records it classifies), not every task Vitest has run. Authority: the CLI's own rule that memory scales with what the run must keep (retention law of this change; no contract document covers it). This is not an oracle-owned behaviour; no coverage-map entry covers retention, so no oracle is extended. The focused probe below is the evidence.
  • Reach: Not an oracle change. The probe covers every batch of the klona four-file suite (295 mutants, two contexts).
  • RED: origin/main affeb1a, node --max-old-space-size=2048 on the klona copy, same seed and mutants. Result: FATAL ERROR: Ineffective mark-compacts near heap limit ... JavaScript heap out of memory, then oracle-gaps: done error, the run's process exited with SIGABRT: it ran out of memory; give Node more (NODE_OPTIONS=--max-old-space-size=<MB>) or run fewer --contexts.
  • Baseline (default heap, before this change): completed, 43 gaps (provisional), child peak heap 3659 MB.
  • GREEN: same command on this branch (3924eb6): completed, 43 gaps (provisional), the same done line, child peak heap 1862 MB (3659 MB before the change at the default heap). Log /tmp/oom/green.log.
  • Outcome identity: results.json from the baseline run and the GREEN run are identical once timing fields and the output path are dropped (392 entries each). The run id changes, because it hashes the CLI's own check.mjs. One streamed gap line moved position in stdout order; the results do not change.
  • Mutant: (A) retirement call removed, and (B) only the idMap step removed (clearFiles kept). Both run at --max-old-space-size=2048 on the same inputs, and both end FATAL ERROR: ... JavaScript heap out of memory with oracle-gaps: done error ... ran out of memory. The oracle run rejects each. Logs /tmp/oom/mutant-noretire.log and /tmp/oom/mutant-noidmap.log.
  • Limits: No change to the store, the baseline rows, or results, which are not the dominant term in the snapshot. The fast suites run in CI. The Vitest state fields are internal (filesMap, idMap); a shape change throws instead of leaking.

This pull request and its description were written by Isaac.

A check runs each mutant in a new copy of its test file. Vitest's state kept
every file and test task it ever ran (filesMap by path, idMap by task id), so
a check held every test of every copy: about 377k test cases on a four-file
suite, which exhausted a 2 GB heap. Once a batch's units are plain data, its
copies leave the state: their stale idMap entries go first (shape-guarded),
then clearFiles replaces each path's file entry.

Co-authored-by: Isaac <no-reply@databricks.com>
@coderabbitai

coderabbitai Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@changeset-bot

changeset-bot Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 84a1085

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Nov 13, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/TanStack/oracles/@tanstack/eslint-plugin-oracles@61
npm i https://pkg.pr.new/TanStack/oracles/@tanstack/oracles@61

commit: 3924eb6

@KyleAMathews
KyleAMathews marked this pull request as ready for review November 13, 2026 18:11
@KyleAMathews
KyleAMathews marked this pull request as draft November 13, 2026 18:21
KyleAMathews and others added 2 commits November 13, 2026 11:31
The gap CLI's e2e driver sets ORACLE_GAPS_TEST_RETENTION on each run and
records one line per batch: the count of Vitest state entries (filesMap,
pathsSet, idMap) and Vite module nodes that still name the batch's retired
copies. The law: each count is 0 after retirement. This commit adds the hook
and the law, not the fix, so CI records the RED on the parent code.

Co-authored-by: Isaac <no-reply@databricks.com>
… file entry

Vitest's clearFiles replaces a copy's file entry, and leaves its path in
pathsSet, one placeholder task in idMap, and Vite's module node and transform
for the copy. The retention law (bounded retention, after each batch) fails on
46dfd3e in CI: each count is nonzero.

retireCopies now removes the copy's filesMap key, pathsSet entry, and idMap
tasks. retireModules invalidates each copy's module through Vite's API and
removes it from every environment's graph maps. Both guard the shapes they
touch and throw if one changes, so a Vitest or Vite that changes them fails
loudly rather than leaking.

Co-authored-by: Isaac <no-reply@databricks.com>
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