From 25b8354959b32a6b56eea9cd2e6264c537060abe Mon Sep 17 00:00:00 2001 From: claude Date: Mon, 13 Jul 2026 15:11:21 +0200 Subject: [PATCH] =?UTF-8?q?test(cli):=20per-test=20project=20dirs=20via=20?= =?UTF-8?q?a=20[nodes]=20pointer=20=E2=80=94=20project=5Flock=20retired?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every E2E test that mutated the shared demo-project fixture store now mints its own tempdir project whose 2-line Aura.toml points at the once-built fixture crate by absolute [nodes] pointer; the runs/ store follows the project root, so no two tests contend and libtest's default thread parallelism applies. project_load keeps driving the fixture directly (the suite's only proof of the inline-crate routing arm); its single store-mutating test is that binary's only one, so no lock is needed there either. This also closes the documented cross-process race window (#223): there is no shared mutable store left to race on. Measured on the data-full host: cli_run 211s -> 57.2s (140 tests), research_docs 21.1s -> 2.1s (73 tests), project_load green, clippy clean. refs #250 --- crates/aura-cli/tests/cli_run.rs | 136 ++++----- crates/aura-cli/tests/common/mod.rs | 62 ++-- crates/aura-cli/tests/project_load.rs | 3 +- crates/aura-cli/tests/research_docs.rs | 377 +++++++++++-------------- 4 files changed, 275 insertions(+), 303 deletions(-) diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index cc4aaee..f12342e 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -1,12 +1,11 @@ //! Integration test: drive the built `aura` binary as a downstream user would, //! asserting the `run` subcommand's stdout/exit contract and the bad-args path. -use std::path::{Path, PathBuf}; +use std::path::Path; use std::process::Command; -use std::sync::MutexGuard; mod common; -use common::{built_project, project_lock}; +use common::fresh_project; /// Path to the freshly-built `aura` binary (Cargo sets this env var for the test /// crate; the binary is named `aura` in `Cargo.toml`). @@ -22,29 +21,6 @@ fn temp_cwd(name: &str) -> std::path::PathBuf { dir } -/// Removes `/runs` on drop (including mid-panic) so a migrated test never -/// leaks its content-addressed store into the git-tracked fixture tree. -struct RunsCleanup(PathBuf); -impl Drop for RunsCleanup { - fn drop(&mut self) { - std::fs::remove_dir_all(&self.0).ok(); - } -} - -/// Enter the shared built project fixture with a FRESH `runs/` store, serialized. -/// Returns the fixture dir (has an Aura.toml, so `provenance()` is `Some`) and the -/// guards; bind the guards for the whole test body: `let (dir, _g) = fresh_project();`. -/// The guard tuple orders `RunsCleanup` before the `MutexGuard` deliberately: tuple -/// fields drop in index order, so the `runs/` removal (index 0) runs while the lock -/// (index 1) is still held — the serialization the lock exists to enforce. -fn fresh_project() -> (&'static PathBuf, (RunsCleanup, MutexGuard<'static, ()>)) { - let lock = project_lock(); - let dir = built_project(); - let runs = dir.join("runs"); - std::fs::remove_dir_all(&runs).ok(); - (dir, (RunsCleanup(runs), lock)) -} - /// #218: a well-formed dissolved verb run outside a project refuses up front /// (exit 1, "needs a project") and leaves no store — parity with `campaign run`. /// The refusal names the INVOKED verb, not a copy-pasted generic string — a @@ -1215,7 +1191,7 @@ fn sweep_real_no_geometry_symbol_refuses_with_exit_1() { let fixture = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR")); let out = std::process::Command::new(BIN) .args(["sweep", &fixture, "--axis", "sma_signal.fast.length=2,4", "--real", "NONEXISTENT"]) - .current_dir(dir).output().unwrap(); + .current_dir(&dir).output().unwrap(); assert_eq!(out.status.code(), Some(1)); assert!(String::from_utf8_lossy(&out.stderr).contains("no recorded geometry")); } @@ -1301,7 +1277,7 @@ fn sweep_real_blueprint_member_manifest_stamps_the_default_stop() { "--axis", "sma_signal.slow.length=8", "--name", "brp_nostop", ]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr)); @@ -1348,7 +1324,7 @@ fn sweep_real_blueprint_member_manifest_stamps_project_provenance() { "--axis", "sma_signal.slow.length=8", "--name", "brp_provenance", ]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr)); @@ -1434,7 +1410,7 @@ fn sweep_real_ohlc_channel_members_run_and_reproduce() { let fixture = format!("{}/tests/fixtures/r_channel_open.json", env!("CARGO_MANIFEST_DIR")); let axes = std::process::Command::new(BIN) .args(["sweep", &fixture, "--list-axes"]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); assert_eq!( @@ -1455,7 +1431,7 @@ fn sweep_real_ohlc_channel_members_run_and_reproduce() { "--axis", "hl_channel.channel_length=3,5", "--name", "chan", ]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr)); @@ -1476,7 +1452,7 @@ fn sweep_real_ohlc_channel_members_run_and_reproduce() { .to_string(); let rep = std::process::Command::new(BIN) .args(["reproduce", &family_id]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); assert!( @@ -1523,7 +1499,7 @@ fn sweep_real_blueprint_member_lines_pin_the_dissolved_contract() { "--axis", "sma_signal.slow.length=8,16", "--name", "brp", ]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr)); @@ -1571,7 +1547,7 @@ fn sweep_real_blueprint_member_lines_pin_the_dissolved_contract() { // exactly one new Sweep family persisted, with the grid's four members. let fams = std::process::Command::new(BIN) .args(["runs", "families"]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); assert!(fams.status.success(), "families exit: {:?}", fams.status); @@ -1674,7 +1650,7 @@ fn sweep_real_blueprint_member_lines_pin_the_dissolved_contract() { "--axis", "sma_signal.slow.length=8,16", "--name", "brp", ]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); assert!(out2.status.success(), "second run stderr: {}", String::from_utf8_lossy(&out2.stderr)); @@ -1761,7 +1737,7 @@ fn sweep_real_with_trace_writes_tap_series_to_disk() { "--axis", "sma_signal.slow.length=8", "--trace", "brp", ]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); // (1) the #168 refusal is lifted: the advertised flag runs instead of exit 2. @@ -1829,13 +1805,13 @@ fn reproduce_real_sweep_family_re_derives_bit_identically() { "--axis", "sma_signal.slow.length=8", "--name", "repro_sweep", ]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); assert!(mint.status.success(), "sweep mint stderr: {}", String::from_utf8_lossy(&mint.stderr)); - let id = family_id_of_kind(dir, "Sweep"); - let out = Command::new(BIN).args(["reproduce", &id]).current_dir(dir).output().unwrap(); + let id = family_id_of_kind(&dir, "Sweep"); + let out = Command::new(BIN).args(["reproduce", &id]).current_dir(&dir).output().unwrap(); let stdout = String::from_utf8_lossy(&out.stdout).into_owned(); assert!( out.status.success(), @@ -1984,13 +1960,13 @@ fn reproduce_real_walkforward_family_does_not_panic() { "--axis", "sma_signal.slow.length=8", "--name", "repro_wf", ]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); assert!(mint.status.success(), "walkforward mint stderr: {}", String::from_utf8_lossy(&mint.stderr)); - let id = family_id_of_kind(dir, "WalkForward"); - let out = Command::new(BIN).args(["reproduce", &id]).current_dir(dir).output().unwrap(); + let id = family_id_of_kind(&dir, "WalkForward"); + let out = Command::new(BIN).args(["reproduce", &id]).current_dir(&dir).output().unwrap(); let stdout = String::from_utf8_lossy(&out.stdout).into_owned(); let stderr = String::from_utf8_lossy(&out.stderr).into_owned(); assert_ne!( @@ -2147,7 +2123,7 @@ fn sweep_real_multi_column_blueprint_opens_high_and_low_columns() { "--axis", "hl_signal.fast.length=2,4", "--axis", "hl_signal.slow.length=8", ]) - .current_dir(dir) + .current_dir(&dir) .output() .unwrap(); assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr)); @@ -2232,7 +2208,7 @@ fn aura_sweep_real_blueprint_subset_axes_refuses_without_store_litter() { "--axis", "sma_signal.fast.length=2,4", "--name", "c0110-subset", ]) - .current_dir(dir) + .current_dir(&dir) .output() .expect("spawn aura sweep (subset axes)"); assert_ne!( @@ -2288,7 +2264,7 @@ fn sweep_dissolved_accepts_an_axis_over_a_bound_param() { "--axis", "sma_signal.fast.length=2,3", "--name", "bound-override", ]) - .current_dir(dir) + .current_dir(&dir) .output() .expect("spawn aura sweep over a bound-param axis"); assert_eq!( @@ -2298,7 +2274,7 @@ fn sweep_dissolved_accepts_an_axis_over_a_bound_param() { path too, stderr: {}", String::from_utf8_lossy(&out.stderr) ); - let fams = Command::new(BIN).args(["runs", "families"]).current_dir(dir).output().unwrap(); + let fams = Command::new(BIN).args(["runs", "families"]).current_dir(&dir).output().unwrap(); assert!(fams.status.success(), "families exit: {:?}", fams.status); let fams_out = String::from_utf8_lossy(&fams.stdout).into_owned(); assert_eq!(fams_out.lines().count(), 1, "exactly one family persisted: {fams_out}"); @@ -2731,7 +2707,7 @@ fn generalize_grades_a_candidate_across_two_instruments() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -2777,7 +2753,7 @@ fn generalize_real_e2e_pins_the_exact_current_grade() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -2834,7 +2810,7 @@ fn generalize_grades_a_non_r_sma_blueprint() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -2882,7 +2858,7 @@ fn walkforward_real_e2e_pins_the_exact_current_grade() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -2944,7 +2920,7 @@ fn walkforward_dissolves_a_non_r_sma_blueprint() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -3009,7 +2985,7 @@ fn walkforward_dissolved_accepts_select_plateau() { "--select", "plateau:worst", "--from", "1735689600000", "--to", "1747353600000", ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -3054,7 +3030,7 @@ fn walkforward_real_e2e_pins_the_exact_current_plateau_grade() { "--from", FROM_MS, "--to", TO_MS, "--select", "plateau:worst", ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -3109,7 +3085,7 @@ fn walkforward_dissolved_defaults_a_single_omitted_knob() { "--stop-length", "3", "--from", "1735689600000", "--to", "1747353600000", ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -3257,7 +3233,7 @@ fn walkforward_dissolves_through_the_campaign_path() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -3303,7 +3279,7 @@ fn walkforward_dissolves_through_the_campaign_path() { let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(["runs", "families"]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn families"); let fams_out = String::from_utf8_lossy(&fams.stdout).into_owned(); @@ -3351,7 +3327,7 @@ fn walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2() { "--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if stopless.status.code() == Some(1) { @@ -3380,7 +3356,7 @@ fn walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2() { "--stop-length", "3", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); assert_eq!( @@ -3434,7 +3410,7 @@ fn generalize_dissolves_through_the_campaign_path() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -3485,7 +3461,7 @@ fn generalize_dissolves_through_the_campaign_path() { // per-instrument Sweep family per cell (persisted by the executor). let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(["runs", "families"]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn families"); let fams_out = String::from_utf8_lossy(&fams.stdout).into_owned(); @@ -3526,7 +3502,7 @@ fn generalize_repeated_identical_invocation_does_not_litter_the_store() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura") }; @@ -3582,7 +3558,7 @@ fn generalize_distinct_invocations_persist_distinct_campaign_documents() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura") }; @@ -3629,7 +3605,7 @@ fn generalize_without_explicit_window_resolves_a_shared_window_and_completes() { "--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12", "--stop-length", "14", "--stop-k", "2.0", ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -3707,7 +3683,7 @@ fn generalize_without_explicit_window_resolves_the_intersection_of_all_symbols() "--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12", "--name", name, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura sweep"); assert_eq!( @@ -3739,7 +3715,7 @@ fn generalize_without_explicit_window_resolves_the_intersection_of_all_symbols() "--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12", "--name", "gen-both", ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura generalize"); assert_eq!( @@ -3919,7 +3895,7 @@ fn generalize_persists_a_discoverable_cross_instrument_family() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -3936,7 +3912,7 @@ fn generalize_persists_a_discoverable_cross_instrument_family() { // the run persisted the per-instrument members as a discoverable CrossInstrument family. let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(["runs", "families"]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn families"); assert!(fams.status.success(), "families exit: {:?}", fams.status); @@ -3981,7 +3957,7 @@ fn generalize_family_members_are_attributed_to_the_right_instrument() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -3997,7 +3973,7 @@ fn generalize_family_members_are_attributed_to_the_right_instrument() { let fam = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(["runs", "family", "generalize-0"]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn runs family"); assert!(fam.status.success(), "runs family exit: {:?}", fam.status); @@ -4048,7 +4024,7 @@ fn generalize_family_members_stamp_project_provenance() { "--stop-length", "14", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -4064,7 +4040,7 @@ fn generalize_family_members_stamp_project_provenance() { let fam = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(["runs", "family", "generalize-0"]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn runs family"); assert!(fam.status.success(), "runs family exit: {:?}", fam.status); @@ -5083,7 +5059,7 @@ fn mc_r_bootstrap_real_e2e_pins_the_exact_current_grade() { "--block-len", "5", "--resamples", "1000", "--seed", "42", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -5140,7 +5116,7 @@ fn mc_dissolves_a_non_r_sma_blueprint() { "--block-len", "5", "--resamples", "1000", "--seed", "42", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -5195,7 +5171,7 @@ fn mc_dissolves_through_the_campaign_path() { "--block-len", "5", "--resamples", "1000", "--seed", "42", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -5249,7 +5225,7 @@ fn mc_dissolves_through_the_campaign_path() { let fams = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) .args(["runs", "families"]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn families"); let fams_out = String::from_utf8_lossy(&fams.stdout).into_owned(); @@ -5305,7 +5281,7 @@ fn mc_real_fits_the_injected_roller_to_a_short_window() { "--block-len", "5", "--resamples", "100", "--seed", "42", "--from", SUB_FROM_MS, "--to", SUB_TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura mc"); @@ -5414,7 +5390,7 @@ fn mc_dissolved_seed_changes_the_bootstrap_draw_not_the_pooled_series() { "--block-len", "5", "--resamples", "1000", "--seed", seed, "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura") }; @@ -5475,7 +5451,7 @@ fn walkforward_campaign_runs_an_arbitrary_blueprint() { "--stop-length", "3", "--stop-k", "2.0", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -5527,7 +5503,7 @@ fn mc_campaign_runs_an_arbitrary_blueprint() { "--block-len", "5", "--resamples", "100", "--seed", "7", "--from", FROM_MS, "--to", TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { @@ -5576,7 +5552,7 @@ fn generalize_campaign_runs_an_arbitrary_blueprint() { "--stop-length", "3", "--stop-k", "2.0", "--from", GER40_SEPT2024_FROM_MS, "--to", GER40_SEPT2024_TO_MS, ]) - .current_dir(cwd) + .current_dir(&cwd) .output() .expect("spawn aura"); if out.status.code() == Some(1) { diff --git a/crates/aura-cli/tests/common/mod.rs b/crates/aura-cli/tests/common/mod.rs index c3ba3f9..972eeec 100644 --- a/crates/aura-cli/tests/common/mod.rs +++ b/crates/aura-cli/tests/common/mod.rs @@ -3,14 +3,11 @@ //! and drive the `tests/fixtures/demo-project` fixture through the real //! `aura` binary (#223). //! -//! **Process-local serialization only.** [`project_lock`]'s `Mutex` is a -//! per-process `static`: it serializes test THREADS within one test-binary -//! process, but grants no cross-process exclusion. A process-parallel test -//! runner (e.g. `cargo-nextest`, which forks one process per test binary) — -//! or simply two concurrent `cargo test` invocations over the same working -//! tree — still race on the shared `tests/fixtures/demo-project/runs` store. -//! That boundary is a known, documented limitation (#223), not something -//! this module claims to close. +//! **Per-test project directories, no shared store.** [`fresh_project`] mints +//! a unique tempdir per test, wired to the shared, once-built fixture crate +//! via an absolute `[nodes]` pointer (`project.rs`'s pointer-arm routing), so +//! no two tests ever race on the same `runs/` store — libtest's default +//! thread parallelism applies without a serializing lock (#250). //! //! Each of the three test binaries compiles this file as its own `mod //! common`, so an item unused by one binary trips that binary's `-D @@ -18,7 +15,8 @@ //! rather than deleting a helper just because one binary doesn't need it. use std::path::{Path, PathBuf}; -use std::sync::{Mutex, MutexGuard, OnceLock}; +use std::sync::OnceLock; +use std::sync::atomic::{AtomicUsize, Ordering}; /// The demo-project fixture (`Aura.toml` present), built once per test-binary /// process. Each test binary has its own `OnceLock` (this module is compiled @@ -42,13 +40,45 @@ pub fn built_project() -> &'static PathBuf { }) } -/// Serializes every test that touches the shared demo-project fixture store -/// (they remove/re-seed `/runs`, so parallel test threads would race -/// on it). A poisoned lock is taken over: one failed test must not cascade -/// into unrelated lock panics. -pub fn project_lock() -> MutexGuard<'static, ()> { - static LOCK: Mutex<()> = Mutex::new(()); - LOCK.lock().unwrap_or_else(|e| e.into_inner()) +/// A per-test project directory, removed on drop (including mid-test panic). +#[allow(dead_code)] +pub struct FreshProject { + root: PathBuf, +} + +impl Drop for FreshProject { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.root); + } +} + +/// Mint a unique tempdir holding a 2-line data-level project (`Aura.toml` +/// with an absolute `[nodes]` pointer at the shared, once-built fixture +/// crate) plus a copy of `demo_signal.json`. Every test gets its own `runs/` +/// store under this root, so no two tests ever contend for the same store — +/// this is what lets `project_lock` disappear (#250). Bind the guard for the +/// whole test body: `let (dir, _g) = fresh_project();`. +#[allow(dead_code)] +pub fn fresh_project() -> (PathBuf, FreshProject) { + static COUNTER: AtomicUsize = AtomicUsize::new(0); + let root = std::env::temp_dir().join(format!( + "aura-e2e-{}-{}", + std::process::id(), + COUNTER.fetch_add(1, Ordering::Relaxed) + )); + std::fs::create_dir_all(&root).expect("create fresh project tempdir"); + let fixture = built_project(); + std::fs::write( + root.join("Aura.toml"), + format!( + "[paths]\nruns = \"runs\"\n\n[nodes]\ncrates = [\"{}\"]\n", + fixture.display() + ), + ) + .expect("write fresh project Aura.toml"); + std::fs::copy(fixture.join("demo_signal.json"), root.join("demo_signal.json")) + .expect("copy demo_signal.json into the fresh project"); + (root.clone(), FreshProject { root }) } /// A scratch filesystem entry a test writes under the git-tracked diff --git a/crates/aura-cli/tests/project_load.rs b/crates/aura-cli/tests/project_load.rs index b08013b..f979325 100644 --- a/crates/aura-cli/tests/project_load.rs +++ b/crates/aura-cli/tests/project_load.rs @@ -9,7 +9,7 @@ use std::process::{Command, Output}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; mod common; -use common::{built_project, project_lock}; +use common::built_project; fn fixture_dir() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/demo-project") @@ -98,7 +98,6 @@ fn outside_a_project_the_demo_blueprint_is_unknown() { /// rather than shell-relative (C17). #[test] fn project_registry_anchors_at_discovered_root_not_invocation_cwd() { - let _fixture = project_lock(); let dir = built_project(); let sub = dir.join("src"); let runs_dir = dir.join("runs"); diff --git a/crates/aura-cli/tests/research_docs.rs b/crates/aura-cli/tests/research_docs.rs index 5fd5c9a..4318fb4 100644 --- a/crates/aura-cli/tests/research_docs.rs +++ b/crates/aura-cli/tests/research_docs.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; mod common; -use common::{ScratchGuard, ScratchPath, built_project, project_lock}; +use common::{ScratchGuard, ScratchPath, fresh_project}; /// A fresh, unique working directory for a process test that persists /// content-addressed documents under `./runs/` (so `aura process register` @@ -304,8 +304,7 @@ fn process_register_stores_content_addressed_under_runs_root() { /// them together end to end. #[test] fn campaign_validate_in_project_reports_referential_tier_end_to_end() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -321,7 +320,7 @@ fn campaign_validate_in_project_reports_referential_tier_end_to_end() { // test). let closed_bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR")); let (sweep_out, sweep_code) = run_code_in( - dir, + &dir, &[ "sweep", &closed_bp, @@ -360,8 +359,8 @@ fn campaign_validate_in_project_reports_referential_tier_end_to_end() { // overfit_probability (not per-member) flip to a per-member scalar. .replacen("\"overfit_probability\", \"cmp\": \"lt\", \"value\": 0.1", "\"win_rate\", \"cmp\": \"lt\", \"value\": 1.1", 1); assert_ne!(exec_process, PROCESS_DOC, "replacen must match the sweep stage"); - write_doc(dir, "seed.process.json", &exec_process); - let (proc_out, proc_code) = run_code_in(dir, &["process", "register", "seed.process.json"]); + write_doc(&dir, "seed.process.json", &exec_process); + let (proc_out, proc_code) = run_code_in(&dir, &["process", "register", "seed.process.json"]); assert_eq!(proc_code, Some(0), "seed process register failed: {proc_out}"); let proc_id = proc_out .lines() @@ -394,8 +393,8 @@ fn campaign_validate_in_project_reports_referential_tier_end_to_end() { bp = bp_id, proc = proc_id ); - write_doc(dir, "ref-ok.campaign.json", &campaign); - let (out, code) = run_code_in(dir, &["campaign", "validate", "ref-ok.campaign.json"]); + write_doc(&dir, "ref-ok.campaign.json", &campaign); + let (out, code) = run_code_in(&dir, &["campaign", "validate", "ref-ok.campaign.json"]); assert_eq!(code, Some(0), "stdout/stderr: {out}"); assert!(out.contains( "campaign document valid (referential): all references resolve, axes are in the param space" @@ -404,8 +403,8 @@ fn campaign_validate_in_project_reports_referential_tier_end_to_end() { // An unresolved process reference: the fault block + `ref_fault_prose` // mapping, prose exit 1, at the same CLI seam. let bad = campaign.replace(&proc_id, "0000000000000000000000000000000000000000000000000000000000000000"); - write_doc(dir, "ref-bad.campaign.json", &bad); - let (out2, code2) = run_code_in(dir, &["campaign", "validate", "ref-bad.campaign.json"]); + write_doc(&dir, "ref-bad.campaign.json", &bad); + let (out2, code2) = run_code_in(&dir, &["campaign", "validate", "ref-bad.campaign.json"]); assert_eq!(code2, Some(1)); assert!(out2.contains("campaign references do not resolve:"), "stdout/stderr: {out2}"); assert!(out2.contains("not found in the project store"), "stdout/stderr: {out2}"); @@ -1129,9 +1128,8 @@ fn campaign_run_outside_project_refuses() { /// both readings (inside the project, past the project gate). #[test] fn campaign_run_bogus_target_refuses() { - let _fixture = project_lock(); - let dir = built_project(); - let (out, code) = run_code_in(dir, &["campaign", "run", "no-such-target"]); + let (dir, _fixture) = fresh_project(); + let (out, code) = run_code_in(&dir, &["campaign", "run", "no-such-target"]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); assert!( out.contains("'no-such-target' is neither a readable .json file nor a 64-hex content id"), @@ -1142,10 +1140,9 @@ fn campaign_run_bogus_target_refuses() { /// A well-formed but unknown content id refuses with the store-miss prose. #[test] fn campaign_run_unknown_id_refuses() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let id = "0".repeat(64); - let (out, code) = run_code_in(dir, &["campaign", "run", &id]); + let (out, code) = run_code_in(&dir, &["campaign", "run", &id]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); assert!( out.contains(&format!("no campaign {id} in the project store")), @@ -1169,8 +1166,7 @@ fn campaign_run_unknown_id_refuses() { /// bare-store-line contract exactly. #[test] fn campaign_runs_lists_and_dumps_the_bare_stored_record() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); std::fs::create_dir_all(&runs_dir).expect("create runs dir"); @@ -1185,7 +1181,7 @@ fn campaign_runs_lists_and_dumps_the_bare_stored_record() { // LIST mode: one scannable line per stored record, carrying the campaign // id (bare 64-hex or a prefix), exit 0. - let (list_out, list_code) = run_code_in(dir, &["campaign", "runs"]); + let (list_out, list_code) = run_code_in(&dir, &["campaign", "runs"]); assert_eq!(list_code, Some(0), "list exits 0: {list_out}"); assert!( list_out.contains(&campaign_id[..8]), @@ -1195,7 +1191,7 @@ fn campaign_runs_lists_and_dumps_the_bare_stored_record() { // DUMP mode: addressed by the campaign content id, prints the BARE stored // record line — the store form, NOT the `{{"campaign_run": …}}` run-emit // wrapper (F10) — byte-identical to the seeded line, exit 0. - let (dump_out, dump_code) = run_code_in(dir, &["campaign", "runs", &campaign_id]); + let (dump_out, dump_code) = run_code_in(&dir, &["campaign", "runs", &campaign_id]); assert_eq!(dump_code, Some(0), "dump exits 0: {dump_out}"); assert!( !dump_out.contains("\"campaign_run\""), @@ -1221,8 +1217,7 @@ fn campaign_runs_lists_and_dumps_the_bare_stored_record() { /// depends on. #[test] fn campaign_runs_dump_round_trips_a_populated_trace_name() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); std::fs::create_dir_all(&runs_dir).expect("create runs dir"); @@ -1235,7 +1230,7 @@ fn campaign_runs_dump_round_trips_a_populated_trace_name() { std::fs::write(runs_dir.join("campaign_runs.jsonl"), format!("{record_line}\n")) .expect("seed the campaign-run store"); - let (dump_out, dump_code) = run_code_in(dir, &["campaign", "runs", &campaign_id]); + let (dump_out, dump_code) = run_code_in(&dir, &["campaign", "runs", &campaign_id]); assert_eq!(dump_code, Some(0), "dump exits 0: {dump_out}"); assert_eq!( dump_out.trim(), @@ -1254,8 +1249,7 @@ fn campaign_runs_dump_round_trips_a_populated_trace_name() { /// runs, with Debug-free PipelineShape prose. #[test] fn campaign_run_refuses_mc_before_walk_forward() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1263,10 +1257,10 @@ fn campaign_run_refuses_mc_before_walk_forward() { ScratchPath::File(dir.join("mcwf.process.json")), ScratchPath::File(dir.join("mcwf.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-mcwf-seed"); - let proc_id = register_process_doc(dir, "mcwf.process.json", MC_BEFORE_WF_PROCESS_DOC); - write_doc(dir, "mcwf.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); - let (out, code) = run_code_in(dir, &["campaign", "run", "mcwf.campaign.json"]); + let bp_id = seed_blueprint(&dir, "campaign-run-mcwf-seed"); + let proc_id = register_process_doc(&dir, "mcwf.process.json", MC_BEFORE_WF_PROCESS_DOC); + write_doc(&dir, "mcwf.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); + let (out, code) = run_code_in(&dir, &["campaign", "run", "mcwf.campaign.json"]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); assert!(out.contains("process pipeline is not executable:"), "stdout/stderr: {out}"); assert!( @@ -1304,8 +1298,7 @@ const SELECTION_FREE_SWEEP_THEN_GATE_PROCESS_DOC: &str = r#"{ /// nothing for a following stage to consume). #[test] fn campaign_run_refuses_a_non_terminal_selection_free_sweep() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1313,11 +1306,11 @@ fn campaign_run_refuses_a_non_terminal_selection_free_sweep() { ScratchPath::File(dir.join("selfree.process.json")), ScratchPath::File(dir.join("selfree.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-selfree-seed"); + let bp_id = seed_blueprint(&dir, "campaign-run-selfree-seed"); let proc_id = - register_process_doc(dir, "selfree.process.json", SELECTION_FREE_SWEEP_THEN_GATE_PROCESS_DOC); - write_doc(dir, "selfree.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); - let (out, code) = run_code_in(dir, &["campaign", "run", "selfree.campaign.json"]); + register_process_doc(&dir, "selfree.process.json", SELECTION_FREE_SWEEP_THEN_GATE_PROCESS_DOC); + write_doc(&dir, "selfree.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); + let (out, code) = run_code_in(&dir, &["campaign", "run", "selfree.campaign.json"]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); assert!( out.contains( @@ -1344,8 +1337,7 @@ const SELECTION_FREE_SWEEP_ONLY_PROCESS_DOC: &str = r#"{ #[test] fn campaign_validate_accepts_a_terminal_selection_free_sweep() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1353,15 +1345,15 @@ fn campaign_validate_accepts_a_terminal_selection_free_sweep() { ScratchPath::File(dir.join("selfreeonly.process.json")), ScratchPath::File(dir.join("selfreeonly.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-validate-selfreeonly-seed"); + let bp_id = seed_blueprint(&dir, "campaign-validate-selfreeonly-seed"); let proc_id = - register_process_doc(dir, "selfreeonly.process.json", SELECTION_FREE_SWEEP_ONLY_PROCESS_DOC); + register_process_doc(&dir, "selfreeonly.process.json", SELECTION_FREE_SWEEP_ONLY_PROCESS_DOC); write_doc( - dir, + &dir, "selfreeonly.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", ""), ); - let (out, code) = run_code_in(dir, &["campaign", "validate", "selfreeonly.campaign.json"]); + let (out, code) = run_code_in(&dir, &["campaign", "validate", "selfreeonly.campaign.json"]); assert_eq!(code, Some(0), "a terminal selection-free sweep validates as executable: {out}"); assert!( out.contains("campaign document valid (executable): pipeline shape and static guards pass"), @@ -1378,8 +1370,7 @@ fn campaign_validate_accepts_a_terminal_selection_free_sweep() { /// single-instrument, and this refusal is data-free by construction). #[test] fn campaign_run_refuses_single_instrument_generalize() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1387,10 +1378,10 @@ fn campaign_run_refuses_single_instrument_generalize() { ScratchPath::File(dir.join("gen1.process.json")), ScratchPath::File(dir.join("gen1.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-gen1-seed"); - let proc_id = register_process_doc(dir, "gen1.process.json", GENERALIZE_PROCESS_DOC); - write_doc(dir, "gen1.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); - let (out, code) = run_code_in(dir, &["campaign", "run", "gen1.campaign.json"]); + let bp_id = seed_blueprint(&dir, "campaign-run-gen1-seed"); + let proc_id = register_process_doc(&dir, "gen1.process.json", GENERALIZE_PROCESS_DOC); + write_doc(&dir, "gen1.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); + let (out, code) = run_code_in(&dir, &["campaign", "run", "gen1.campaign.json"]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); assert!( out.contains("std::generalize needs at least 2"), @@ -1423,8 +1414,7 @@ const GENERALIZE_NON_R_PROCESS_DOC: &str = r#"{ /// would silently accept a pip-denominated `std::generalize`. #[test] fn campaign_run_refuses_generalize_non_r_metric() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1432,8 +1422,8 @@ fn campaign_run_refuses_generalize_non_r_metric() { ScratchPath::File(dir.join("genpip.process.json")), ScratchPath::File(dir.join("genpip.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-genpip-seed"); - let proc_id = register_process_doc(dir, "genpip.process.json", GENERALIZE_NON_R_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "campaign-run-genpip-seed"); + let proc_id = register_process_doc(&dir, "genpip.process.json", GENERALIZE_NON_R_PROCESS_DOC); // Two instruments (arity passes), so the metric guard is the one that // actually fires. The [1, 2] 1970 window never matters: this refusal is // data-free, before any member runs. @@ -1451,8 +1441,8 @@ fn campaign_run_refuses_generalize_non_r_metric() { "presentation": {{ "persist_taps": [], "emit": [] }} }}"# ); - write_doc(dir, "genpip.campaign.json", &campaign_doc); - let (out, code) = run_code_in(dir, &["campaign", "run", "genpip.campaign.json"]); + write_doc(&dir, "genpip.campaign.json", &campaign_doc); + let (out, code) = run_code_in(&dir, &["campaign", "run", "genpip.campaign.json"]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); // #207 (fieldtest 0108 F8): the refusal names the REAL rule (the rankable // R-expectancy family), never the mislabel "pip metric" — max_r_drawdown is @@ -1505,8 +1495,7 @@ fn process_validate_permits_wf_after_mc_but_campaign_run_refuses_it() { ); // executor tier: campaign run's v2 preflight refuses the same shape. - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1514,10 +1503,10 @@ fn process_validate_permits_wf_after_mc_but_campaign_run_refuses_it() { ScratchPath::File(dir.join("wfmc.process.json")), ScratchPath::File(dir.join("wfmc.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-wfmc-seed"); - let proc_id = register_process_doc(dir, "wfmc.process.json", WF_AFTER_MC_PROCESS_DOC); - write_doc(dir, "wfmc.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); - let (out, code) = run_code_in(dir, &["campaign", "run", "wfmc.campaign.json"]); + let bp_id = seed_blueprint(&dir, "campaign-run-wfmc-seed"); + let proc_id = register_process_doc(&dir, "wfmc.process.json", WF_AFTER_MC_PROCESS_DOC); + write_doc(&dir, "wfmc.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); + let (out, code) = run_code_in(&dir, &["campaign", "run", "wfmc.campaign.json"]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); assert!( out.contains("std::walk_forward cannot follow std::monte_carlo"), @@ -1545,8 +1534,7 @@ fn process_validate_permits_wf_after_mc_but_campaign_run_refuses_it() { /// shape validate clean (exit 0), the F6 friction. #[test] fn campaign_validate_runs_the_executor_preflight_as_a_third_tier() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1556,15 +1544,15 @@ fn campaign_validate_runs_the_executor_preflight_as_a_third_tier() { ScratchPath::File(dir.join("wfmc.process.json")), ScratchPath::File(dir.join("wfmc.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-validate-3tier-seed"); + let bp_id = seed_blueprint(&dir, "campaign-validate-3tier-seed"); // Face 1 — the happy path: an executable pipeline shape (a bare // std::sweep). Intrinsic + referential pass today (exit 0), but the // executor tier is invisible; after #205 validate gains the third // "valid (executable)" line while staying exit 0. - let ok_proc = register_process_doc(dir, "runnable.process.json", SWEEP_ONLY_PROCESS_DOC); - write_doc(dir, "runnable.campaign.json", &campaign_doc_json(&bp_id, &ok_proc, (1, 2), "", "")); - let (ok_out, ok_code) = run_code_in(dir, &["campaign", "validate", "runnable.campaign.json"]); + let ok_proc = register_process_doc(&dir, "runnable.process.json", SWEEP_ONLY_PROCESS_DOC); + write_doc(&dir, "runnable.campaign.json", &campaign_doc_json(&bp_id, &ok_proc, (1, 2), "", "")); + let (ok_out, ok_code) = run_code_in(&dir, &["campaign", "validate", "runnable.campaign.json"]); assert_eq!(ok_code, Some(0), "an executable campaign validates clean: {ok_out}"); assert!( ok_out.contains("campaign document valid (referential): all references resolve"), @@ -1583,9 +1571,9 @@ fn campaign_validate_runs_the_executor_preflight_as_a_third_tier() { // reaches the third tier — which refuses the non-executable shape at // validate time (data-free: the [1, 2] 1970-epoch-ms window is never // reached), exit 1, with the executor fault header + the shape prose. - let bad_proc = register_process_doc(dir, "wfmc.process.json", WF_AFTER_MC_PROCESS_DOC); - write_doc(dir, "wfmc.campaign.json", &campaign_doc_json(&bp_id, &bad_proc, (1, 2), "", "")); - let (bad_out, bad_code) = run_code_in(dir, &["campaign", "validate", "wfmc.campaign.json"]); + let bad_proc = register_process_doc(&dir, "wfmc.process.json", WF_AFTER_MC_PROCESS_DOC); + write_doc(&dir, "wfmc.campaign.json", &campaign_doc_json(&bp_id, &bad_proc, (1, 2), "", "")); + let (bad_out, bad_code) = run_code_in(&dir, &["campaign", "validate", "wfmc.campaign.json"]); assert_eq!(bad_code, Some(1), "an executor-unrunnable shape refuses at validate: {bad_out}"); assert!( bad_out.contains("campaign is not executable:"), @@ -1639,8 +1627,7 @@ const WF_PLATEAU_GATED_PROCESS_DOC: &str = r#"{ /// exit 0 — either direction goes red here. #[test] fn campaign_validate_narrows_plateau_in_wf_refusal_to_gate_preceded_stages() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1650,19 +1637,19 @@ fn campaign_validate_narrows_plateau_in_wf_refusal_to_gate_preceded_stages() { ScratchPath::File(dir.join("wf-plateau-gated.process.json")), ScratchPath::File(dir.join("wf-plateau-gated.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-validate-wf-plateau-seed"); + let bp_id = seed_blueprint(&dir, "campaign-validate-wf-plateau-seed"); // Face 1 — gate-free: sweep -> walk_forward(plateau:worst). The lattice // is intact, so the executor tier accepts it (exit 0). let free_id = - register_process_doc(dir, "wf-plateau-free.process.json", WF_PLATEAU_GATE_FREE_PROCESS_DOC); + register_process_doc(&dir, "wf-plateau-free.process.json", WF_PLATEAU_GATE_FREE_PROCESS_DOC); write_doc( - dir, + &dir, "wf-plateau-free.campaign.json", &campaign_doc_json(&bp_id, &free_id, (1, 2), "", ""), ); let (free_out, free_code) = - run_code_in(dir, &["campaign", "validate", "wf-plateau-free.campaign.json"]); + run_code_in(&dir, &["campaign", "validate", "wf-plateau-free.campaign.json"]); assert_eq!(free_code, Some(0), "gate-free plateau walk_forward validates clean: {free_out}"); assert!( free_out.contains( @@ -1675,14 +1662,14 @@ fn campaign_validate_narrows_plateau_in_wf_refusal_to_gate_preceded_stages() { // The gate can break the lattice, so the executor tier still refuses it // (exit 1), naming the offending stage. let gated_id = - register_process_doc(dir, "wf-plateau-gated.process.json", WF_PLATEAU_GATED_PROCESS_DOC); + register_process_doc(&dir, "wf-plateau-gated.process.json", WF_PLATEAU_GATED_PROCESS_DOC); write_doc( - dir, + &dir, "wf-plateau-gated.campaign.json", &campaign_doc_json(&bp_id, &gated_id, (1, 2), "", ""), ); let (gated_out, gated_code) = - run_code_in(dir, &["campaign", "validate", "wf-plateau-gated.campaign.json"]); + run_code_in(&dir, &["campaign", "validate", "wf-plateau-gated.campaign.json"]); assert_eq!(gated_code, Some(1), "gate-preceded plateau walk_forward still refuses: {gated_out}"); assert!( gated_out.contains("campaign is not executable:"), @@ -1717,8 +1704,7 @@ const ZERO_RESAMPLES_MC_PROCESS_DOC: &str = r#"{ /// would let this refusal disappear or drift to a different message. #[test] fn campaign_run_refuses_zero_resamples_monte_carlo_before_any_member_runs() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1726,10 +1712,10 @@ fn campaign_run_refuses_zero_resamples_monte_carlo_before_any_member_runs() { ScratchPath::File(dir.join("zeromc.process.json")), ScratchPath::File(dir.join("zeromc.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-zeromc-seed"); - let proc_id = register_process_doc(dir, "zeromc.process.json", ZERO_RESAMPLES_MC_PROCESS_DOC); - write_doc(dir, "zeromc.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); - let (out, code) = run_code_in(dir, &["campaign", "run", "zeromc.campaign.json"]); + let bp_id = seed_blueprint(&dir, "campaign-run-zeromc-seed"); + let proc_id = register_process_doc(&dir, "zeromc.process.json", ZERO_RESAMPLES_MC_PROCESS_DOC); + write_doc(&dir, "zeromc.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); + let (out, code) = run_code_in(&dir, &["campaign", "run", "zeromc.campaign.json"]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); assert!( out.contains("process stage 1: monte_carlo resamples must be > 0"), @@ -1749,8 +1735,7 @@ fn campaign_run_refuses_zero_resamples_monte_carlo_before_any_member_runs() { /// pinned here. #[test] fn campaign_run_refuses_unknown_tap_at_validate() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1758,14 +1743,14 @@ fn campaign_run_refuses_unknown_tap_at_validate() { ScratchPath::File(dir.join("badtap.process.json")), ScratchPath::File(dir.join("badtap.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-badtap-seed"); - let proc_id = register_process_doc(dir, "badtap.process.json", SWEEP_ONLY_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "campaign-run-badtap-seed"); + let proc_id = register_process_doc(&dir, "badtap.process.json", SWEEP_ONLY_PROCESS_DOC); write_doc( - dir, + &dir, "badtap.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "\"r_record\"", ""), ); - let (out, code) = run_code_in(dir, &["campaign", "run", "badtap.campaign.json"]); + let (out, code) = run_code_in(&dir, &["campaign", "run", "badtap.campaign.json"]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); assert!(out.contains("campaign document invalid:"), "stdout/stderr: {out}"); assert!( @@ -1794,8 +1779,7 @@ fn campaign_run_refuses_unknown_tap_at_validate() { /// tightens this test to pin its absence. #[test] fn campaign_run_valid_tap_reaches_the_member_data_seam() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1803,14 +1787,14 @@ fn campaign_run_valid_tap_reaches_the_member_data_seam() { ScratchPath::File(dir.join("taps.process.json")), ScratchPath::File(dir.join("taps.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-taps-seed"); - let proc_id = register_process_doc(dir, "taps.process.json", SWEEP_ONLY_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "campaign-run-taps-seed"); + let proc_id = register_process_doc(&dir, "taps.process.json", SWEEP_ONLY_PROCESS_DOC); write_doc( - dir, + &dir, "taps.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "\"equity\"", ""), ); - let (out, code) = run_code_in(dir, &["campaign", "run", "taps.campaign.json"]); + let (out, code) = run_code_in(&dir, &["campaign", "run", "taps.campaign.json"]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); assert!( !out.contains("unknown tap"), @@ -1849,8 +1833,7 @@ fn campaign_run_valid_tap_reaches_the_member_data_seam() { /// stays green on a data-less machine. #[test] fn campaign_run_real_e2e_sweep_gate_walkforward() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -1858,12 +1841,12 @@ fn campaign_run_real_e2e_sweep_gate_walkforward() { ScratchPath::File(dir.join("wf.process.json")), ScratchPath::File(dir.join("wf.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-e2e-seed"); - let proc_id = register_process_doc(dir, "wf.process.json", WF_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "campaign-run-e2e-seed"); + let proc_id = register_process_doc(&dir, "wf.process.json", WF_PROCESS_DOC); // The GER40 Sept-2024 window (inclusive Unix-ms) — the same gated window // cli_run.rs drives; ~30 days, so the (14d, 7d, 7d) roller tiles it. write_doc( - dir, + &dir, "wf.campaign.json", &campaign_doc_json( &bp_id, @@ -1873,7 +1856,7 @@ fn campaign_run_real_e2e_sweep_gate_walkforward() { "\"family_table\", \"selection_report\"", ), ); - let (out, code) = run_code_in(dir, &["campaign", "run", "wf.campaign.json"]); + let (out, code) = run_code_in(&dir, &["campaign", "run", "wf.campaign.json"]); // Skip on a data-less machine: the member-data refusal, never a panic. if code == Some(1) @@ -1999,7 +1982,7 @@ fn campaign_run_real_e2e_sweep_gate_walkforward() { // The shipped viewer reads the member back (the cli_run.rs chart // idiom): exit 0 and an HTML page carrying both persisted tap series. let (chart_out, chart_code) = - run_code_in(dir, &["chart", &format!("{trace_name}/{cell_key}")]); + run_code_in(&dir, &["chart", &format!("{trace_name}/{cell_key}")]); assert_eq!(chart_code, Some(0), "aura chart reads the persisted member: {chart_out}"); assert!( chart_out.contains("\"equity\"") && chart_out.contains("\"r_equity\""), @@ -2015,8 +1998,7 @@ fn campaign_run_real_e2e_sweep_gate_walkforward() { /// Gated on the local GER40 archive; skips cleanly when absent. #[test] fn campaign_run_real_e2e_bindings_override_rebinds_price_to_open() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -2025,8 +2007,8 @@ fn campaign_run_real_e2e_bindings_override_rebinds_price_to_open() { ScratchPath::File(dir.join("close.campaign.json")), ScratchPath::File(dir.join("open.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "binding-override-seed"); - let proc_id = register_process_doc(dir, "sweep.process.json", SWEEP_ONLY_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "binding-override-seed"); + let proc_id = register_process_doc(&dir, "sweep.process.json", SWEEP_ONLY_PROCESS_DOC); let base = campaign_doc_json( &bp_id, &proc_id, @@ -2039,10 +2021,10 @@ fn campaign_run_real_e2e_bindings_override_rebinds_price_to_open() { r#""data": { "bindings": { "price": "open" }, "instruments": ["GER40"],"#, ); assert_ne!(rebound, base, "the override splice must hit the data section"); - write_doc(dir, "close.campaign.json", &base); - write_doc(dir, "open.campaign.json", &rebound); + write_doc(&dir, "close.campaign.json", &base); + write_doc(&dir, "open.campaign.json", &rebound); - let (out_close, code_close) = run_code_in(dir, &["campaign", "run", "close.campaign.json"]); + let (out_close, code_close) = run_code_in(&dir, &["campaign", "run", "close.campaign.json"]); if code_close == Some(1) && (out_close.contains("no recorded geometry") || out_close.contains("no data for instrument")) { @@ -2050,7 +2032,7 @@ fn campaign_run_real_e2e_bindings_override_rebinds_price_to_open() { return; } assert_eq!(code_close, Some(0), "stdout/stderr: {out_close}"); - let (out_open, code_open) = run_code_in(dir, &["campaign", "run", "open.campaign.json"]); + let (out_open, code_open) = run_code_in(&dir, &["campaign", "run", "open.campaign.json"]); assert_eq!(code_open, Some(0), "stdout/stderr: {out_open}"); let first_member = |out: &str| -> serde_json::Value { @@ -2081,8 +2063,7 @@ fn campaign_run_real_e2e_bindings_override_rebinds_price_to_open() { /// above; skips on a data-less host. #[test] fn campaign_run_real_e2e_non_default_regime_stamps_its_own_stop() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -2090,8 +2071,8 @@ fn campaign_run_real_e2e_non_default_regime_stamps_its_own_stop() { ScratchPath::File(dir.join("regime.process.json")), ScratchPath::File(dir.join("regime.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-regime-seed"); - let proc_id = register_process_doc(dir, "regime.process.json", SWEEP_ONLY_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "campaign-run-regime-seed"); + let proc_id = register_process_doc(&dir, "regime.process.json", SWEEP_ONLY_PROCESS_DOC); let base = campaign_doc_json( &bp_id, &proc_id, @@ -2105,8 +2086,8 @@ fn campaign_run_real_e2e_non_default_regime_stamps_its_own_stop() { 1, ); assert_ne!(with_regime, base, "replacen must actually match the fixture's seed field"); - write_doc(dir, "regime.campaign.json", &with_regime); - let (out, code) = run_code_in(dir, &["campaign", "run", "regime.campaign.json"]); + write_doc(&dir, "regime.campaign.json", &with_regime); + let (out, code) = run_code_in(&dir, &["campaign", "run", "regime.campaign.json"]); if code == Some(1) && (out.contains("no recorded geometry") || out.contains("no data for instrument")) @@ -2151,8 +2132,7 @@ fn campaign_run_real_e2e_non_default_regime_stamps_its_own_stop() { /// on the local GER40 archive like its siblings; skips on a data-less host. #[test] fn campaign_run_real_e2e_two_regimes_each_stamp_their_own_stop() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -2160,8 +2140,8 @@ fn campaign_run_real_e2e_two_regimes_each_stamp_their_own_stop() { ScratchPath::File(dir.join("tworegimes.process.json")), ScratchPath::File(dir.join("tworegimes.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-two-regimes-seed"); - let proc_id = register_process_doc(dir, "tworegimes.process.json", SWEEP_ONLY_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "campaign-run-two-regimes-seed"); + let proc_id = register_process_doc(&dir, "tworegimes.process.json", SWEEP_ONLY_PROCESS_DOC); let base = campaign_doc_json( &bp_id, &proc_id, @@ -2176,8 +2156,8 @@ fn campaign_run_real_e2e_two_regimes_each_stamp_their_own_stop() { 1, ); assert_ne!(with_regimes, base, "replacen must actually match the fixture's seed field"); - write_doc(dir, "tworegimes.campaign.json", &with_regimes); - let (out, code) = run_code_in(dir, &["campaign", "run", "tworegimes.campaign.json"]); + write_doc(&dir, "tworegimes.campaign.json", &with_regimes); + let (out, code) = run_code_in(&dir, &["campaign", "run", "tworegimes.campaign.json"]); if code == Some(1) && (out.contains("no recorded geometry") || out.contains("no data for instrument")) @@ -2236,8 +2216,7 @@ fn campaign_run_real_e2e_two_regimes_each_stamp_their_own_stop() { /// archive like its siblings; skips on a data-less host. #[test] fn campaign_persist_non_default_regime_does_not_false_fail_c1() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -2245,9 +2224,9 @@ fn campaign_persist_non_default_regime_does_not_false_fail_c1() { ScratchPath::File(dir.join("regime-persist.process.json")), ScratchPath::File(dir.join("regime-persist.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-persist-regime-seed"); + let bp_id = seed_blueprint(&dir, "campaign-persist-regime-seed"); let proc_id = - register_process_doc(dir, "regime-persist.process.json", SWEEP_ONLY_PROCESS_DOC); + register_process_doc(&dir, "regime-persist.process.json", SWEEP_ONLY_PROCESS_DOC); let base = campaign_doc_json( &bp_id, &proc_id, @@ -2264,8 +2243,8 @@ fn campaign_persist_non_default_regime_does_not_false_fail_c1() { 1, ); assert_ne!(with_regime, base, "replacen must actually match the fixture's seed field"); - write_doc(dir, "regime-persist.campaign.json", &with_regime); - let (out, code) = run_code_in(dir, &["campaign", "run", "regime-persist.campaign.json"]); + write_doc(&dir, "regime-persist.campaign.json", &with_regime); + let (out, code) = run_code_in(&dir, &["campaign", "run", "regime-persist.campaign.json"]); // Skip on a data-less machine: the member-data refusal, never a panic. if code == Some(1) @@ -2321,8 +2300,7 @@ fn campaign_persist_non_default_regime_does_not_false_fail_c1() { /// the local GER40 archive; skips on a data-less host. #[test] fn campaign_run_real_e2e_costed_campaign_persists_without_drift_alarm() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -2330,8 +2308,8 @@ fn campaign_run_real_e2e_costed_campaign_persists_without_drift_alarm() { ScratchPath::File(dir.join("cost-persist.process.json")), ScratchPath::File(dir.join("cost-persist.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-persist-cost-seed"); - let proc_id = register_process_doc(dir, "cost-persist.process.json", SWEEP_ONLY_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "campaign-persist-cost-seed"); + let proc_id = register_process_doc(&dir, "cost-persist.process.json", SWEEP_ONLY_PROCESS_DOC); let base = campaign_doc_json( &bp_id, &proc_id, @@ -2345,8 +2323,8 @@ fn campaign_run_real_e2e_costed_campaign_persists_without_drift_alarm() { 1, ); assert_ne!(with_cost, base, "replacen must actually match the fixture's seed field"); - write_doc(dir, "cost-persist.campaign.json", &with_cost); - let (out, code) = run_code_in(dir, &["campaign", "run", "cost-persist.campaign.json"]); + write_doc(&dir, "cost-persist.campaign.json", &with_cost); + let (out, code) = run_code_in(&dir, &["campaign", "run", "cost-persist.campaign.json"]); // Skip on a data-less machine: the member-data refusal, never a panic. if code == Some(1) @@ -2417,8 +2395,7 @@ fn campaign_run_real_e2e_costed_campaign_persists_without_drift_alarm() { /// archive; skips on a data-less host. #[test] fn campaign_run_real_e2e_net_r_equity_tap_persists_the_cost_adjusted_curve() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -2426,8 +2403,8 @@ fn campaign_run_real_e2e_net_r_equity_tap_persists_the_cost_adjusted_curve() { ScratchPath::File(dir.join("net-persist.process.json")), ScratchPath::File(dir.join("net-persist.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-persist-net-seed"); - let proc_id = register_process_doc(dir, "net-persist.process.json", SWEEP_ONLY_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "campaign-persist-net-seed"); + let proc_id = register_process_doc(&dir, "net-persist.process.json", SWEEP_ONLY_PROCESS_DOC); let base = campaign_doc_json( &bp_id, &proc_id, @@ -2441,8 +2418,8 @@ fn campaign_run_real_e2e_net_r_equity_tap_persists_the_cost_adjusted_curve() { 1, ); assert_ne!(with_cost, base, "replacen must actually match the fixture's seed field"); - write_doc(dir, "net-persist.campaign.json", &with_cost); - let (out, code) = run_code_in(dir, &["campaign", "run", "net-persist.campaign.json"]); + write_doc(&dir, "net-persist.campaign.json", &with_cost); + let (out, code) = run_code_in(&dir, &["campaign", "run", "net-persist.campaign.json"]); // Skip on a data-less machine: the member-data refusal, never a panic. if code == Some(1) @@ -2531,8 +2508,7 @@ fn campaign_run_real_e2e_net_r_equity_tap_persists_the_cost_adjusted_curve() { /// wired). Gated on the local GER40 archive; skips on a data-less host. #[test] fn campaign_run_real_e2e_vol_slippage_cost_persists_without_drift_alarm() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -2540,9 +2516,9 @@ fn campaign_run_real_e2e_vol_slippage_cost_persists_without_drift_alarm() { ScratchPath::File(dir.join("volcost-persist.process.json")), ScratchPath::File(dir.join("volcost-persist.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-persist-volcost-seed"); + let bp_id = seed_blueprint(&dir, "campaign-persist-volcost-seed"); let proc_id = - register_process_doc(dir, "volcost-persist.process.json", SWEEP_ONLY_PROCESS_DOC); + register_process_doc(&dir, "volcost-persist.process.json", SWEEP_ONLY_PROCESS_DOC); let base = campaign_doc_json( &bp_id, &proc_id, @@ -2556,8 +2532,8 @@ fn campaign_run_real_e2e_vol_slippage_cost_persists_without_drift_alarm() { 1, ); assert_ne!(with_cost, base, "replacen must actually match the fixture's seed field"); - write_doc(dir, "volcost-persist.campaign.json", &with_cost); - let (out, code) = run_code_in(dir, &["campaign", "run", "volcost-persist.campaign.json"]); + write_doc(&dir, "volcost-persist.campaign.json", &with_cost); + let (out, code) = run_code_in(&dir, &["campaign", "run", "volcost-persist.campaign.json"]); // Skip on a data-less machine: the member-data refusal, never a panic. if code == Some(1) @@ -2617,8 +2593,7 @@ fn campaign_run_real_e2e_vol_slippage_cost_persists_without_drift_alarm() { /// into one dir. Gated on the local GER40 archive; skips on a data-less host. #[test] fn campaign_persist_two_regimes_land_in_distinct_trace_dirs() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -2626,9 +2601,9 @@ fn campaign_persist_two_regimes_land_in_distinct_trace_dirs() { ScratchPath::File(dir.join("tworegimes-persist.process.json")), ScratchPath::File(dir.join("tworegimes-persist.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-persist-two-regimes-seed"); + let bp_id = seed_blueprint(&dir, "campaign-persist-two-regimes-seed"); let proc_id = register_process_doc( - dir, + &dir, "tworegimes-persist.process.json", SWEEP_ONLY_PROCESS_DOC, ); @@ -2646,8 +2621,8 @@ fn campaign_persist_two_regimes_land_in_distinct_trace_dirs() { 1, ); assert_ne!(with_regimes, base, "replacen must actually match the fixture's seed field"); - write_doc(dir, "tworegimes-persist.campaign.json", &with_regimes); - let (out, code) = run_code_in(dir, &["campaign", "run", "tworegimes-persist.campaign.json"]); + write_doc(&dir, "tworegimes-persist.campaign.json", &with_regimes); + let (out, code) = run_code_in(&dir, &["campaign", "run", "tworegimes-persist.campaign.json"]); // Skip on a data-less machine: the member-data refusal, never a panic. if code == Some(1) @@ -2704,8 +2679,7 @@ fn campaign_persist_two_regimes_land_in_distinct_trace_dirs() { /// host like its siblings. #[test] fn campaign_run_real_e2e_skips_unproducible_tap_gracefully() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -2713,10 +2687,10 @@ fn campaign_run_real_e2e_skips_unproducible_tap_gracefully() { ScratchPath::File(dir.join("mixedtap.process.json")), ScratchPath::File(dir.join("mixedtap.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-mixedtap-seed"); - let proc_id = register_process_doc(dir, "mixedtap.process.json", SWEEP_ONLY_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "campaign-run-mixedtap-seed"); + let proc_id = register_process_doc(&dir, "mixedtap.process.json", SWEEP_ONLY_PROCESS_DOC); write_doc( - dir, + &dir, "mixedtap.campaign.json", &campaign_doc_json( &bp_id, @@ -2726,7 +2700,7 @@ fn campaign_run_real_e2e_skips_unproducible_tap_gracefully() { "", ), ); - let (out, code) = run_code_in(dir, &["campaign", "run", "mixedtap.campaign.json"]); + let (out, code) = run_code_in(&dir, &["campaign", "run", "mixedtap.campaign.json"]); // Skip on a data-less machine: the member-data refusal, never a panic. if code == Some(1) @@ -2781,8 +2755,7 @@ fn campaign_run_real_e2e_skips_unproducible_tap_gracefully() { /// manifests). Skips on a data-less host like its siblings. #[test] fn campaign_run_real_e2e_cost_block_nets_members_and_persists_net_r_equity() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -2791,8 +2764,8 @@ fn campaign_run_real_e2e_cost_block_nets_members_and_persists_net_r_equity() { ScratchPath::File(dir.join("costless.campaign.json")), ScratchPath::File(dir.join("costnet.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-costnet-seed"); - let proc_id = register_process_doc(dir, "costnet.process.json", SWEEP_ONLY_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "campaign-run-costnet-seed"); + let proc_id = register_process_doc(&dir, "costnet.process.json", SWEEP_ONLY_PROCESS_DOC); let base = campaign_doc_json( &bp_id, &proc_id, @@ -2809,8 +2782,8 @@ fn campaign_run_real_e2e_cost_block_nets_members_and_persists_net_r_equity() { 1, ); assert_ne!(with_cost, base, "replacen must actually match the fixture's seed field"); - write_doc(dir, "costless.campaign.json", &base); - write_doc(dir, "costnet.campaign.json", &with_cost); + write_doc(&dir, "costless.campaign.json", &base); + write_doc(&dir, "costnet.campaign.json", &with_cost); // Per-member (axis-params key -> (expectancy_r, net_expectancy_r)), // parsed from the emitted family_table member lines. Cost stamps are @@ -2843,7 +2816,7 @@ fn campaign_run_real_e2e_cost_block_nets_members_and_persists_net_r_equity() { // (a) the cost-less baseline: net == gross, and net_r_equity skips with // the remedy notice while equity still persists. - let (out_gross, code_gross) = run_code_in(dir, &["campaign", "run", "costless.campaign.json"]); + let (out_gross, code_gross) = run_code_in(&dir, &["campaign", "run", "costless.campaign.json"]); if code_gross == Some(1) && (out_gross.contains("no recorded geometry") || out_gross.contains("no data for instrument")) { @@ -2865,7 +2838,7 @@ fn campaign_run_real_e2e_cost_block_nets_members_and_persists_net_r_equity() { // (b) the costed run: gross identical member-for-member, net strictly // below gross, no skip notice, net_r_equity persisted on disk. - let (out_net, code_net) = run_code_in(dir, &["campaign", "run", "costnet.campaign.json"]); + let (out_net, code_net) = run_code_in(&dir, &["campaign", "run", "costnet.campaign.json"]); assert_eq!(code_net, Some(0), "costed run: {out_net}"); assert!( !out_net.contains("skipped"), @@ -2916,7 +2889,7 @@ fn campaign_run_real_e2e_cost_block_nets_members_and_persists_net_r_equity() { .to_string() }) .expect("a costed member line carries the family id"); - let (rep_out, rep_code) = run_code_in(dir, &["reproduce", &family_id]); + let (rep_out, rep_code) = run_code_in(&dir, &["reproduce", &family_id]); assert_eq!(rep_code, Some(0), "reproduce: {rep_out}"); assert!( rep_out.contains("reproduced 4/4 members bit-identically"), @@ -2939,8 +2912,7 @@ fn campaign_run_real_e2e_cost_block_nets_members_and_persists_net_r_equity() { /// local GER40 archive; skips on a data-less host. #[test] fn campaign_run_real_e2e_carry_cost_persists_without_drift_alarm() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -2948,9 +2920,9 @@ fn campaign_run_real_e2e_carry_cost_persists_without_drift_alarm() { ScratchPath::File(dir.join("carrycost-persist.process.json")), ScratchPath::File(dir.join("carrycost-persist.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-persist-carrycost-seed"); + let bp_id = seed_blueprint(&dir, "campaign-persist-carrycost-seed"); let proc_id = - register_process_doc(dir, "carrycost-persist.process.json", SWEEP_ONLY_PROCESS_DOC); + register_process_doc(&dir, "carrycost-persist.process.json", SWEEP_ONLY_PROCESS_DOC); let base = campaign_doc_json( &bp_id, &proc_id, @@ -2964,8 +2936,8 @@ fn campaign_run_real_e2e_carry_cost_persists_without_drift_alarm() { 1, ); assert_ne!(with_cost, base, "replacen must actually match the fixture's seed field"); - write_doc(dir, "carrycost-persist.campaign.json", &with_cost); - let (out, code) = run_code_in(dir, &["campaign", "run", "carrycost-persist.campaign.json"]); + write_doc(&dir, "carrycost-persist.campaign.json", &with_cost); + let (out, code) = run_code_in(&dir, &["campaign", "run", "carrycost-persist.campaign.json"]); // Skip on a data-less machine: the member-data refusal, never a panic. if code == Some(1) @@ -3021,8 +2993,7 @@ fn campaign_run_real_e2e_carry_cost_persists_without_drift_alarm() { /// data-free refusal fixture). #[test] fn campaign_run_real_e2e_sweep_monte_carlo_per_survivor() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -3030,15 +3001,15 @@ fn campaign_run_real_e2e_sweep_monte_carlo_per_survivor() { ScratchPath::File(dir.join("mc.process.json")), ScratchPath::File(dir.join("mc.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-mc-persurvivor-seed"); - let proc_id = register_process_doc(dir, "mc.process.json", MC_PROCESS_DOC); + let bp_id = seed_blueprint(&dir, "campaign-run-mc-persurvivor-seed"); + let proc_id = register_process_doc(&dir, "mc.process.json", MC_PROCESS_DOC); // Same GER40 Sept-2024 window as the gated wf e2e above. write_doc( - dir, + &dir, "mc.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1725148800000, 1727740799999), "", ""), ); - let (out, code) = run_code_in(dir, &["campaign", "run", "mc.campaign.json"]); + let (out, code) = run_code_in(&dir, &["campaign", "run", "mc.campaign.json"]); // Skip on a data-less machine: the member-data refusal, never a panic. if code == Some(1) @@ -3095,8 +3066,7 @@ fn campaign_run_real_e2e_sweep_monte_carlo_per_survivor() { /// prose either way (the message depends only on env/instrument/window). #[test] fn campaign_run_by_content_id_matches_file_sugar_refusal() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -3104,14 +3074,14 @@ fn campaign_run_by_content_id_matches_file_sugar_refusal() { ScratchPath::File(dir.join("mc2.process.json")), ScratchPath::File(dir.join("mc2.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-id-seed"); - let proc_id = register_process_doc(dir, "mc2.process.json", MC_PROCESS_DOC); - write_doc(dir, "mc2.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); + let bp_id = seed_blueprint(&dir, "campaign-run-id-seed"); + let proc_id = register_process_doc(&dir, "mc2.process.json", MC_PROCESS_DOC); + write_doc(&dir, "mc2.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); - let (file_out, file_code) = run_code_in(dir, &["campaign", "run", "mc2.campaign.json"]); + let (file_out, file_code) = run_code_in(&dir, &["campaign", "run", "mc2.campaign.json"]); assert_eq!(file_code, Some(1), "stdout/stderr: {file_out}"); - let (reg_out, reg_code) = run_code_in(dir, &["campaign", "register", "mc2.campaign.json"]); + let (reg_out, reg_code) = run_code_in(&dir, &["campaign", "register", "mc2.campaign.json"]); assert_eq!(reg_code, Some(0), "register failed: {reg_out}"); let id = reg_out .lines() @@ -3123,7 +3093,7 @@ fn campaign_run_by_content_id_matches_file_sugar_refusal() { .expect("id") .trim_start_matches("content:") .to_string(); - let (id_out, id_code) = run_code_in(dir, &["campaign", "run", &id]); + let (id_out, id_code) = run_code_in(&dir, &["campaign", "run", &id]); assert_eq!(id_code, Some(1), "stdout/stderr: {id_out}"); let file_line = file_out.lines().find(|l| l.starts_with("aura: ")).expect("file refusal line"); @@ -3146,8 +3116,7 @@ fn campaign_run_by_content_id_matches_file_sugar_refusal() { /// needed. #[test] fn campaign_run_tolerates_content_prefix_on_target() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -3155,11 +3124,11 @@ fn campaign_run_tolerates_content_prefix_on_target() { ScratchPath::File(dir.join("prefix.process.json")), ScratchPath::File(dir.join("prefix.campaign.json")), ]); - let bp_id = seed_blueprint(dir, "campaign-run-prefix-seed"); - let proc_id = register_process_doc(dir, "prefix.process.json", MC_PROCESS_DOC); - write_doc(dir, "prefix.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); + let bp_id = seed_blueprint(&dir, "campaign-run-prefix-seed"); + let proc_id = register_process_doc(&dir, "prefix.process.json", MC_PROCESS_DOC); + write_doc(&dir, "prefix.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); - let (reg_out, reg_code) = run_code_in(dir, &["campaign", "register", "prefix.campaign.json"]); + let (reg_out, reg_code) = run_code_in(&dir, &["campaign", "register", "prefix.campaign.json"]); assert_eq!(reg_code, Some(0), "register failed: {reg_out}"); let id = reg_out .lines() @@ -3172,8 +3141,8 @@ fn campaign_run_tolerates_content_prefix_on_target() { .trim_start_matches("content:") .to_string(); - let (bare_out, _) = run_code_in(dir, &["campaign", "run", &id]); - let (pfx_out, _) = run_code_in(dir, &["campaign", "run", &format!("content:{id}")]); + let (bare_out, _) = run_code_in(&dir, &["campaign", "run", &id]); + let (pfx_out, _) = run_code_in(&dir, &["campaign", "run", &format!("content:{id}")]); let pfx_line = pfx_out.lines().find(|l| l.starts_with("aura: ")).expect("prefixed refusal line"); assert!( @@ -3201,8 +3170,7 @@ fn campaign_run_tolerates_content_prefix_on_target() { /// data-free refusal) rather than failing earlier at referencing or binding. #[test] fn campaign_run_accepts_a_graph_register_seeded_strategy() { - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let _cleanup = ScratchGuard(vec![ @@ -3211,7 +3179,7 @@ fn campaign_run_accepts_a_graph_register_seeded_strategy() { ScratchPath::File(dir.join("onramp.campaign.json")), ]); let closed_bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR")); - let (reg_out, reg_code) = run_code_in(dir, &["graph", "register", &closed_bp]); + let (reg_out, reg_code) = run_code_in(&dir, &["graph", "register", &closed_bp]); assert_eq!(reg_code, Some(0), "graph register failed: {reg_out}"); let bp_id = reg_out .lines() @@ -3224,9 +3192,9 @@ fn campaign_run_accepts_a_graph_register_seeded_strategy() { .trim_start_matches("content:") .to_string(); - let proc_id = register_process_doc(dir, "onramp.process.json", SWEEP_ONLY_PROCESS_DOC); - write_doc(dir, "onramp.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); - let (out, code) = run_code_in(dir, &["campaign", "run", "onramp.campaign.json"]); + let proc_id = register_process_doc(&dir, "onramp.process.json", SWEEP_ONLY_PROCESS_DOC); + write_doc(&dir, "onramp.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", "")); + let (out, code) = run_code_in(&dir, &["campaign", "run", "onramp.campaign.json"]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); assert!( out.contains("no recorded geometry") || out.contains("no data for instrument"), @@ -3248,15 +3216,14 @@ fn campaign_run_invalid_file_refuses_before_touching_store() { // Run inside the built project (campaign run needs one, per the project // gate); the document fails intrinsic validation before any store or // referential check is reached, so the project's own store is untouched. - let _fixture = project_lock(); - let dir = built_project(); + let (dir, _fixture) = fresh_project(); let runs_dir = dir.join("runs"); std::fs::remove_dir_all(&runs_dir).ok(); let bad = CAMPAIGN_DOC.replacen(r#""values": [8]"#, r#""values": []"#, 1); - let bad_path = write_doc(dir, "bad.campaign.json", &bad); + let bad_path = write_doc(&dir, "bad.campaign.json", &bad); let _cleanup = ScratchGuard(vec![ScratchPath::Dir(runs_dir.clone()), ScratchPath::File(bad_path)]); - let (out, code) = run_code_in(dir, &["campaign", "run", "bad.campaign.json"]); + let (out, code) = run_code_in(&dir, &["campaign", "run", "bad.campaign.json"]); assert_eq!(code, Some(1), "stdout/stderr: {out}"); assert!( out.contains("aura: campaign document invalid:"),