Skip to content

chore(mutants): keep the mutation scratch dir out of the OS temp sweep - #500

Merged
ultimatile merged 1 commit into
mainfrom
chore/mutants-safe-scratch
Aug 6, 2026
Merged

chore(mutants): keep the mutation scratch dir out of the OS temp sweep#500
ultimatile merged 1 commit into
mainfrom
chore/mutants-safe-scratch

Conversation

@ultimatile

Copy link
Copy Markdown
Owner

Summary

cargo mutants copies the workspace into $TMPDIR and mutates the copy, and the OS temp cleaner deletes files there by age. On macOS 26.5.2 /System/Library/LaunchDaemons/com.apple.bsd.dirhelper.plist schedules that sweep daily and sets CLEAN_FILES_OLDER_THAN_DAYS to 3. The full pass is 3345 mutants at e08cf9b, each needing its own build and test cycle: a run here reached 31% after 3.3 days, then lost source files out of its own copy and died with does not exist, refusing to create it naming a file that is present in the working tree.

cargo make mutants now points the scratch at target/mutants-scratch, which no cleaner touches, and the two pre-existing scoped tasks mutants-arpack and mutants-hptt do the same. Everything below describes cargo-mutants 27.1.0.

Changes

  • Makefile.tomlMUTANTS_SCRATCH names the scratch directory, a private mutants-scratch task creates it, and all three passes export it as TMPDIR. Without the directory cargo-mutants exits with Error: create temp dir / Caused by: No such file or directory (os error 2), so creating it is a task dependency rather than an assumption.
  • Makefile.toml — a new mutants task runs the full pass, forwarding extra flags through ${@} so it stays a drop-in for a bare cargo mutants. mutants-arpack and mutants-hptt deliberately do not forward: cargo-mutants unions a repeated --file / --re rather than replacing it, so forwarding into their fixed scope would widen it instead of narrowing it.
  • Makefile.toml — the clean task's description records that cleaning also deletes a running pass's scratch copies.
  • .cargo/mutants.tomlcopy_target is pinned to false. With the scratch under target/, copying target/ into each build directory would copy the scratch into itself.
  • .gitignoremutants.out and mutants.out.old, which the full pass writes at the repo root and which main neither tracks nor ignores.

Impact

Only the mutation tasks change. MUTANTS_SCRATCH is defined workspace-wide but nothing outside those tasks reads it, and TMPDIR is set per task rather than globally, so build / test / clippy are unaffected. The repository has no CI workflow, and no script under scripts/ invokes the mutation passes.

Test plan

Against a fresh git worktree carrying this change, with no target/ and no mutants.out:

  • cargo make mutants runs its mutants-scratch dependency and lands its build copy under target/mutants-scratch; cargo make --print-steps shows all three passes carrying that dependency and TMPDIR;
  • cargo make mutants --list lists those 3345 and exits without starting a pass;
  • adding copy_target leaves that list unchanged and still excludes the top-level algorithms-fixtures/ and crates/ariadnetor-tensor/src/test_fixtures/, so the existing exclude_globs still apply;
  • git check-ignore -v matches both new .gitignore entries.

The union behaviour behind the forwarding asymmetry is reproducible at e08cf9b: --file crates/ariadnetor-algorithms/src/krylov/arpack.rs lists 34 mutants, --file crates/ariadnetor-algorithms/src/dmrg/sweep.rs lists 61, and passing both lists 95.

The recursion the copy_target pin prevents was reproduced in a scratch crate outside this repository, with TMPDIR pointed at a directory inside it and copy_target = true: each copy nests one level deeper until the path is too long to create (ENAMETOOLONG). A --copy-target true on the command line still overrides the pinned value, so the pin guards against a change of default rather than a deliberate override.

Notes

cargo clean now reaches the scratch. That reclaims what a killed run leaves behind and destroys a live pass's copies just as readily, which is what the clean task description warns about. Neither is in play as the repository stands — .cargo/config.toml sets no target-dir — but setting CARGO_TARGET_DIR or build.target-dir breaks both halves of that: the scratch stays at the literal repo target/ while cargo clean operates on the relocated directory, so it neither reclaims the leftovers nor endangers a live pass.

cargo-mutants copies the workspace into $TMPDIR and mutates the copy,
and the OS temp cleaner deletes that copy by age — three days on macOS
— so a pass that runs for days loses source files out of its own copy
and dies partway through.

Point the scratch at target/mutants-scratch on every task that invokes
cargo-mutants, and register the shipped pass as `cargo make mutants` so
the redirect is not something to remember. That task forwards extra
flags, keeping it a drop-in for the bare command; the scoped passes do
not, since a repeated --file/--re widens their fixed scope instead of
narrowing it.

Under target/ the scratch needs copy_target pinned false — otherwise
each build directory would copy the scratch into itself — and `cargo
clean` now reaches it, reclaiming a killed run's leftovers and
destroying a live pass's copies alike. Also gitignore mutants.out,
which was excluded only locally.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes long-running cargo mutants passes resilient to OS temp-directory cleanup by redirecting cargo-mutants’ scratch workspace from $TMPDIR to a stable, repository-local location under target/mutants-scratch via cargo make tasks.

Changes:

  • Introduces a workspace-wide MUTANTS_SCRATCH env var and a private mutants-scratch prerequisite task, then runs all mutation passes with TMPDIR set to that directory.
  • Adds a new mutants cargo-make task for the shipped/default mutation pass and updates the existing mutants-arpack / mutants-hptt tasks to use the same scratch redirect.
  • Pins copy_target = false in .cargo/mutants.toml and ignores root-level mutants.out artifacts; updates CONTRIBUTING docs accordingly.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated no comments.

File Description
Makefile.toml Defines MUTANTS_SCRATCH, adds mutants-scratch + mutants tasks, and ensures all mutation tasks export TMPDIR and create the scratch directory first.
CONTRIBUTING.md Switches guidance from bare cargo mutants to cargo make mutants and documents why (OS temp sweeping) and where scratch is stored.
.gitignore Ignores mutants.out / mutants.out.old written at repo root by the shipped pass.
.cargo/mutants.toml Pins copy_target = false to prevent recursive copying when scratch lives under target/.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ultimatile
ultimatile merged commit 05141a4 into main Aug 6, 2026
1 check passed
@ultimatile
ultimatile deleted the chore/mutants-safe-scratch branch August 7, 2026 05:24
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.

2 participants