Skip to content

fix(forge): preserve mutation workspace paths#15630

Open
mablr wants to merge 8 commits into
masterfrom
mablr/mutation-workspace-paths
Open

fix(forge): preserve mutation workspace paths#15630
mablr wants to merge 8 commits into
masterfrom
mablr/mutation-workspace-paths

Conversation

@mablr

@mablr mablr commented Jul 7, 2026

Copy link
Copy Markdown
Member

Mutation workspaces now preserve external read-only compiler paths instead of trying to copy them into each temporary project. This lets monorepo-style projects with remappings or allow paths pointing at sibling Solidity directories run mutation testing without every mutant becoming invalid during workspace setup.

The workspace rebasing logic now resolves project-relative paths against the original root, rebases project-local paths into the temp workspace, and keeps external paths external. Project-local fuzz and invariant corpus/frontier directories are also copied and rebased into each mutant workspace so mutation runs do not share those mutable paths with the original project. Paths already covered by handled dependency roots are skipped to avoid interfering with dependency symlinks.

@mablr mablr marked this pull request as ready for review July 7, 2026 18:37
Comment thread crates/forge/src/workspace.rs Outdated
Comment thread crates/forge/src/workspace.rs Outdated
Comment thread crates/forge/tests/cli/test_cmd/mutation.rs
Comment thread crates/forge/src/workspace.rs
@mablr mablr requested a review from stevencartavia July 8, 2026 16:04
return Ok(());
}
let is_external = rel.is_absolute();
let target = if is_external || is_covered_by_handled_root(&rel, handled_roots) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This uses the broader handled-roots set, so a corpus under src/test/script can get copied into .foundry_mutable even though rebase_mutable_project_path still points at the normal temp copy. Correctness looks fine; I'll handle the classifier cleanup separately as a follow-up.

@mattsse mattsse left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One more mutable workspace path looks like it should be isolated before this lands.

failure_persist_dir is mutable too, so preserving external values can still let isolated mutation/brutalize workspaces write into the user's real external failure cache. Fuzz writes counterexamples under test_paths(... failure_persist_dir ...), and invariant writes under invariant_failure_dir / record_invariant_failure, so a config like failure_persist_dir = "../shared-failures" can escape the temp workspace.

I think these two fields should use the same mutable-path helper as corpus/frontier:

if let Some(path) = &config.fuzz.failure_persist_dir {
    temp_config.fuzz.failure_persist_dir =
        Some(rebase_mutable_project_path(config, temp_path, path));
}

if let Some(path) = &config.invariant.failure_persist_dir {
    temp_config.invariant.failure_persist_dir =
        Some(rebase_mutable_project_path(config, temp_path, path));
}

Test suggestion: add a workspace unit test with fuzz.failure_persist_dir = Some(PathBuf::from("../shared-fuzz-failures")) and invariant.failure_persist_dir = Some(PathBuf::from("../shared-invariant-failures")), then assert both rebased paths start with workspace.join(".foundry_mutable"). I would keep this as rebase-only for the minimal fix; project-local failure dirs are not copied today, so this closes the write escape without changing persisted-failure replay semantics more broadly.

@mablr mablr requested a review from mattsse July 9, 2026 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants