feat(cli): walkforward + mc go blueprint-generic (#220 slice 2)

Tasks 3-4 of the verb-axis-generalization plan:

- walkforward: the welded r-sma campaign branch moves out of the None
  arm into the blueprint mode, split on --real (campaign sugar) vs the
  unchanged synthetic family path; WalkforwardCmd drops
  --strategy/--fast/--slow; the full-window clip block, wf_ms_sizes and
  WINNER_SELECTION_METRIC move unchanged; the --select plateau #215
  refusal survives. walkforward_summary_json_from_reports is de-welded:
  the axis-name list is passed in from the invocation instead of the
  const AXES r-sma quadruple.
- mc: McCmd drops the welded flags and gains --axis; the blueprint arm
  splits on --real (campaign pipeline: requires --axis, refuses
  --seeds) vs the unchanged synthetic seed family (--seeds); block-len/
  resamples/seed defaults and the --name/--trace refusal stay
  byte-identical.
- translate_walkforward/run_walkforward_sugar and translate_mc/
  run_mc_sugar adopt SugarInvocation (+ WfWindows/McKnobs); ZERO
  #[allow(clippy::too_many_arguments)] remain in verb_sugar.rs (#214
  complete).
- Tests: wf/mc e2e groups migrate argv-only (grade anchors keep all
  pinned floats verbatim); collateral usage/retired-token tests assert
  clap's structural rejection of the deleted flags; the synthetic
  family control groups pass unmodified.

Necessary deviation from the plan's literal step text: re-added
#[allow(clippy::type_complexity)] on walkforward_args_from (clippy -D
warnings flags the 6-tuple; mirrors its siblings). Held quality nits
(validate/strip block now quadruplicated across the four dispatch fns;
inline exit-mapping recurrence) are deliberate plan-holds — dedup
follows as the rule-of-three cleanup at cycle end.

Verification: full workspace suite green in the loop's independent
verify; build + clippy -D warnings clean.

refs #220, refs #214
This commit is contained in:
2026-07-09 14:48:59 +02:00
parent 4f629ee2ab
commit e7efe1f765
3 changed files with 660 additions and 533 deletions
+276 -50
View File
@@ -1048,12 +1048,9 @@ fn mc_rejects_real_flag_with_usage_exit_2() {
.current_dir(&dir)
.output()
.expect("spawn aura mc --real");
assert_eq!(out.status.code(), Some(2), "mc --real must exit 2; status: {:?}", out.status);
assert!(out.stdout.is_empty(), "mc --real must not emit a run on stdout: {:?}", out.stdout);
assert_eq!(out.status.code(), Some(2), "mc --real without a blueprint is a usage error");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(stderr.contains("Usage"), "mc --real stderr must carry usage: {stderr:?}");
// the exclusion is total: no `runs/` registry write either.
assert!(!dir.join("runs").exists(), "mc --real must not start a real run");
assert!(stderr.contains("Usage: aura mc"), "usage refusal: {stderr}");
let _ = std::fs::remove_dir_all(&dir);
}
@@ -1099,11 +1096,11 @@ fn family_window_flag_without_real_refuses_with_usage_exit_2() {
/// regression that drops the prefix again would NOT be caught by the older
/// `family_window_flag_without_real_refuses_with_usage_exit_2` pin above, which
/// only checks for the verb token and `--real` — not the `Usage: aura` lead-in.
/// An unknown `--strategy` name reaches this same closure on both commands, so
/// it is the smallest trigger for each.
/// An unknown `--strategy` name reaches sweep's closure; the bare verb reaches
/// walkforward's.
#[test]
fn builtin_branch_usage_lines_lead_with_the_house_style_prefix() {
for (args, verb) in [(&["sweep", "--strategy", "bogus"][..], "sweep"), (&["walkforward", "--strategy", "bogus"][..], "walkforward")] {
for (args, verb) in [(&["sweep", "--strategy", "bogus"][..], "sweep"), (&["walkforward"][..], "walkforward")] {
let dir = temp_cwd("builtin_usage_prefix");
let out = Command::new(BIN)
.args(args)
@@ -1123,8 +1120,9 @@ fn builtin_branch_usage_lines_lead_with_the_house_style_prefix() {
/// not just the first. This is the one lockstep-pinned site
/// (`mc_rejects_real_flag_with_usage_exit_2` above), but that pin only checks
/// for a single `"Usage"` occurrence — a regression that fixed only the first
/// alternative (leaving `… | mc --strategy r-sma …` unprefixed) would still
/// pass it. `--seeds` with no blueprint file is the built-in branch's own
/// alternative (leaving the second `| aura mc <blueprint.json> --real …`
/// alternative unprefixed) would still pass it. `--seeds` with no blueprint
/// file is the built-in branch's own
/// grammar violation, reaching this exact literal. (#159 cut 4: the first
/// alternative's literal is now the blueprint-mode grammar, not a bare
/// `[--name` — `mc` is blueprint-first.)
@@ -1143,7 +1141,7 @@ fn mc_builtin_usage_names_the_program_in_both_alternatives() {
"first alternative must name the program: {stderr:?}"
);
assert!(
stderr.contains("| aura mc --strategy r-sma"),
stderr.contains("| aura mc <blueprint.json> --real"),
"second alternative must ALSO name the program: {stderr:?}"
);
let _ = std::fs::remove_dir_all(&dir);
@@ -1220,12 +1218,19 @@ fn sweep_and_walkforward_reject_retired_r_breakout_token_as_unrecognized() {
.unwrap_or_else(|e| panic!("spawn aura {args:?}: {e}"));
assert_eq!(out.status.code(), Some(2), "`aura {args:?}` must exit 2; status: {:?}", out.status);
let stderr = String::from_utf8_lossy(&out.stderr);
let want = format!("aura: Usage: aura {verb} ");
assert!(
stderr.starts_with(&want),
"`aura {args:?}` must fall into the generic usage error, not a special-cased \
message: {stderr:?}"
);
if verb == "sweep" {
let want = format!("aura: Usage: aura {verb} ");
assert!(
stderr.starts_with(&want),
"`aura {args:?}` must fall into the generic usage error, not a special-cased \
message: {stderr:?}"
);
} else {
assert!(
stderr.contains("unexpected argument '--strategy'"),
"`aura {args:?}` must be a clap unknown-argument rejection: {stderr:?}"
);
}
assert!(out.stdout.is_empty(), "no family summary on the rejected path: {:?}", out.stdout);
let _ = std::fs::remove_dir_all(&cwd);
}
@@ -1244,8 +1249,15 @@ fn sweep_and_walkforward_reject_retired_r_meanrev_token_as_unrecognized() {
.unwrap_or_else(|e| panic!("spawn aura {args:?}: {e}"));
assert_eq!(out.status.code(), Some(2), "`aura {args:?}` must exit 2; status: {:?}", out.status);
let stderr = String::from_utf8_lossy(&out.stderr);
let want = format!("aura: Usage: aura {verb} ");
assert!(stderr.starts_with(&want), "generic usage, not special-cased: {stderr:?}");
if verb == "sweep" {
let want = format!("aura: Usage: aura {verb} ");
assert!(stderr.starts_with(&want), "generic usage, not special-cased: {stderr:?}");
} else {
assert!(
stderr.contains("unexpected argument '--strategy'"),
"`aura {args:?}` must be a clap unknown-argument rejection: {stderr:?}"
);
}
assert!(out.stdout.is_empty(), "no summary on the rejected path: {:?}", out.stdout);
let _ = std::fs::remove_dir_all(&cwd);
}
@@ -1272,12 +1284,19 @@ fn sweep_and_walkforward_reject_retired_pip_tokens_as_unrecognized() {
out.status
);
let stderr = String::from_utf8_lossy(&out.stderr);
let want = format!("aura: Usage: aura {verb} ");
assert!(
stderr.starts_with(&want),
"`aura {args:?}` must fall into the generic usage error, not a \
special-cased message: {stderr:?}"
);
if verb == "sweep" {
let want = format!("aura: Usage: aura {verb} ");
assert!(
stderr.starts_with(&want),
"`aura {args:?}` must fall into the generic usage error, not a \
special-cased message: {stderr:?}"
);
} else {
assert!(
stderr.contains("unexpected argument '--strategy'"),
"`aura {args:?}` must be a clap unknown-argument rejection: {stderr:?}"
);
}
assert!(out.stdout.is_empty(), "no summary on the rejected path: {:?}", out.stdout);
let _ = std::fs::remove_dir_all(&cwd);
}
@@ -1495,10 +1514,12 @@ fn walkforward_real_e2e_pins_the_exact_current_grade() {
const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("walkforward-exact-grade");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"walkforward", "--strategy", "r-sma", "--real", "GER40",
"--fast", "3,5", "--slow", "12,20", "--stop-length", "14", "--stop-k", "2.0",
"walkforward", &fixture, "--real", "GER40",
"--axis", "sma_signal.fast.length=3,5", "--axis", "sma_signal.slow.length=12,20",
"--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS,
])
.current_dir(&cwd)
@@ -1535,15 +1556,72 @@ fn walkforward_real_e2e_pins_the_exact_current_grade() {
assert_eq!(ps[1]["mean"].as_f64(), Some(17.333333333333332), "slow-MA refit mean: {grade_line}");
}
/// Property (#220 walkforward vertical): `aura walkforward --real` IS-refits ANY
/// sweepable blueprint through the campaign path, not just the historical r-sma
/// candidate — the whole point of dropping `--strategy r-sma`/`--fast`/`--slow`
/// for a positional blueprint + generic `--axis`. Every other walkforward E2E
/// test here still exercises `r_sma_open.json`; a regression that silently
/// special-cased the refit's axis lookup back to a hardcoded `sma_signal.*`
/// namespace (e.g. in `walkforward_summary_json_from_reports`'s per-axis
/// `raw_matches_wrapped` search) would ship green there while breaking every
/// other blueprint. This runs the unrelated r-breakout candidate
/// (`channel_hi`/`channel_lo` axes, no `sma_signal` node in sight) end-to-end
/// and asserts a well-formed multi-window summary whose `param_stability` has
/// exactly one entry per bound axis (2 blueprint axes + the 2 stop columns).
/// The window count (9) is roller/data-derived, not strategy-derived, so it is
/// pinned exactly against the same GER40 2025 span the r-sma anchor uses.
/// Gated on the shared GER40 archive; skips cleanly on a data refusal.
#[test]
fn walkforward_dissolves_a_non_r_sma_blueprint() {
const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("walkforward-non-r-sma");
let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"walkforward", &fixture, "--real", "GER40",
"--axis", "r_breakout_signal.channel_hi.length=10,20",
"--axis", "r_breakout_signal.channel_lo.length=10,20",
"--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS,
])
.current_dir(&cwd)
.output()
.expect("spawn aura");
if out.status.code() == Some(1) {
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("no local data") || stderr.contains("no recorded geometry"),
"exit 1 must be a data refusal, got: {stderr}"
);
eprintln!("skip: no local GER40 data");
return;
}
assert_eq!(out.status.code(), Some(0), "exit: {:?}", out.status);
let stdout = String::from_utf8(out.stdout).expect("utf-8 stdout");
let grade_line = stdout
.lines()
.find(|l| l.starts_with("{\"walkforward\":"))
.unwrap_or_else(|| panic!("the walkforward summary line: {stdout}"));
let v: serde_json::Value = serde_json::from_str(grade_line).expect("summary line parses as JSON");
let wf = &v["walkforward"];
assert_eq!(wf["windows"].as_u64(), Some(9), "window count (data/roller-derived): {grade_line}");
assert!(wf["oos_r"]["n_trades"].as_u64().is_some(), "pooled OOS trade count present: {grade_line}");
let ps = wf["param_stability"].as_array().expect("param_stability is an array");
assert_eq!(ps.len(), 4, "one entry per bound axis (channel_hi, channel_lo, stop_length, stop_k): {grade_line}");
}
/// Fork A: the dissolved walkforward binds the stop as a single risk regime, so a
/// multi-value --stop-length/--stop-k is refused (exit 2) before any run.
#[test]
fn walkforward_dissolved_refuses_a_multi_value_stop() {
let cwd = temp_cwd("walkforward-multi-stop");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"walkforward", "--strategy", "r-sma", "--real", "GER40",
"--fast", "3", "--slow", "12", "--stop-length", "14,20", "--stop-k", "2.0",
"walkforward", &fixture, "--real", "GER40",
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "14,20", "--stop-k", "2.0",
])
.current_dir(&cwd)
.output()
@@ -1558,10 +1636,12 @@ fn walkforward_dissolved_refuses_a_multi_value_stop() {
#[test]
fn walkforward_dissolved_refuses_select_plateau() {
let cwd = temp_cwd("walkforward-plateau");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"walkforward", "--strategy", "r-sma", "--real", "GER40",
"--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0",
"walkforward", &fixture, "--real", "GER40",
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "14", "--stop-k", "2.0",
"--select", "plateau:worst",
])
.current_dir(&cwd)
@@ -1577,14 +1657,18 @@ fn walkforward_dissolved_refuses_select_plateau() {
#[test]
fn walkforward_dissolved_refuses_missing_knobs() {
let cwd = temp_cwd("walkforward-missing-knobs");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args(["walkforward", "--strategy", "r-sma", "--real", "GER40", "--fast", "3", "--slow", "12"])
.args([
"walkforward", &fixture, "--real", "GER40",
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
])
.current_dir(&cwd)
.output()
.expect("spawn aura");
assert_eq!(out.status.code(), Some(2), "missing --stop-length/--stop-k is a usage refusal");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(stderr.contains("requires --fast --slow --stop-length --stop-k"), "missing-knob refusal: {stderr}");
assert!(stderr.contains("requires --stop-length --stop-k"), "missing-knob refusal: {stderr}");
}
/// An empty `--real ""` still satisfies clap's `Option::is_some()` dispatch guard, so
@@ -1593,10 +1677,12 @@ fn walkforward_dissolved_refuses_missing_knobs() {
#[test]
fn walkforward_dissolved_refuses_an_empty_real_symbol() {
let cwd = temp_cwd("walkforward-empty-real");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"walkforward", "--strategy", "r-sma", "--real", "",
"--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0",
"walkforward", &fixture, "--real", "",
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "14", "--stop-k", "2.0",
])
.current_dir(&cwd)
.output()
@@ -1606,16 +1692,51 @@ fn walkforward_dissolved_refuses_an_empty_real_symbol() {
assert!(stderr.contains("dissolves only over --real"), "empty-real refusal: {stderr}");
}
/// Property: an `--axis` name that is not among the loaded blueprint's sweepable
/// axes is refused before the raw-namespace strip or any archive access (exit 2),
/// echoing exactly the name the user typed — the same WRAPPED-probe preflight the
/// sibling verbs enforce
/// (`aura_sweep_real_blueprint_refuses_a_raw_form_axis_name_before_data_access`,
/// `generalize_refuses_an_unknown_axis_name`). Data-free: no archive/store access.
#[test]
fn walkforward_dissolved_refuses_an_unknown_axis_name() {
let cwd = temp_cwd("walkforward-unknown-axis");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"walkforward", &fixture, "--real", "GER40",
"--axis", "sma_signal.nope=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "14", "--stop-k", "2.0",
])
.current_dir(&cwd)
.output()
.expect("spawn aura");
assert_eq!(out.status.code(), Some(2), "an unknown axis name must exit 2");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("axis \"sma_signal.nope\"") && stderr.contains("sweepable axes"),
"must name the unresolved axis exactly as typed: {stderr}"
);
assert!(
out.stdout.is_empty(),
"the refusal must not leak an aggregate to stdout, got: {:?}",
String::from_utf8_lossy(&out.stdout)
);
assert!(!cwd.join("runs").exists(), "a refused axis name must not start a real run");
}
/// Front-end parity: an unknown `--select` token is refused (exit 2) on the
/// dissolved path exactly as it is on the inline path — it must not be silently
/// swallowed by `select_is_plateau`'s plateau-only check and fall through to argmax.
#[test]
fn walkforward_dissolved_refuses_an_unknown_select_token() {
let cwd = temp_cwd("walkforward-bad-select");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"walkforward", "--strategy", "r-sma", "--real", "GER40",
"--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0",
"walkforward", &fixture, "--real", "GER40",
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "14", "--stop-k", "2.0",
"--select", "bogus",
])
.current_dir(&cwd)
@@ -1633,10 +1754,12 @@ fn walkforward_dissolved_refuses_an_unknown_select_token() {
#[test]
fn walkforward_dissolved_refuses_name_and_trace_together() {
let cwd = temp_cwd("walkforward-name-and-trace");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"walkforward", "--strategy", "r-sma", "--real", "GER40",
"--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0",
"walkforward", &fixture, "--real", "GER40",
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "14", "--stop-k", "2.0",
"--name", "a", "--trace", "b",
])
.current_dir(&cwd)
@@ -1662,10 +1785,12 @@ fn walkforward_dissolves_through_the_campaign_path() {
const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("walkforward-dissolves");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"walkforward", "--strategy", "r-sma", "--real", "GER40",
"--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0",
"walkforward", &fixture, "--real", "GER40",
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS,
])
.current_dir(&cwd)
@@ -3035,7 +3160,7 @@ fn dual_grammar_stray_positional_is_a_usage_error_not_swallowed() {
for argv in [
&["run", "bogus"][..],
&["sweep", "bogus", "--name", "x"][..],
&["walkforward", "bogus", "--strategy", "sma"][..],
&["walkforward", "bogus"][..],
&["mc", "bogus"][..],
] {
let out = Command::new(BIN).args(argv).output().expect("spawn aura <sub> <stray-positional>");
@@ -3128,10 +3253,12 @@ fn mc_r_bootstrap_real_e2e_pins_the_exact_current_grade() {
const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("mc-r-bootstrap-exact-grade");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"mc", "--strategy", "r-sma", "--real", "GER40",
"--fast", "3,5", "--slow", "12,20", "--stop-length", "14", "--stop-k", "2.0",
"mc", &fixture, "--real", "GER40",
"--axis", "sma_signal.fast.length=3,5", "--axis", "sma_signal.slow.length=12,20",
"--stop-length", "14", "--stop-k", "2.0",
"--block-len", "5", "--resamples", "1000", "--seed", "42",
"--from", FROM_MS, "--to", TO_MS,
])
@@ -3164,6 +3291,65 @@ fn mc_r_bootstrap_real_e2e_pins_the_exact_current_grade() {
assert_eq!(mc["prob_le_zero"].as_f64(), Some(0.558), "P(E[R] <= 0): {grade_line}");
}
/// Property (#220 mc vertical): `aura mc --real` R-bootstraps ANY sweepable
/// blueprint through the campaign path, not just the historical r-sma candidate
/// — the whole point of dropping `--strategy r-sma`/`--fast`/`--slow` for a
/// positional blueprint + generic `--axis`. Every other mc E2E test here still
/// exercises `r_sma_open.json`; a regression that silently special-cased the
/// pipeline's axis lookup back to a hardcoded `sma_signal.*` namespace would
/// ship green there while breaking every other blueprint. This runs the
/// unrelated r-breakout candidate (`channel_hi`/`channel_lo` axes, no
/// `sma_signal` node in sight) end-to-end and asserts a well-formed
/// `mc_r_bootstrap` grade: the argv-echoed knobs (`block_len`/`n_resamples`)
/// pin exactly, `n_trades`/`e_r.mean`/`prob_le_zero` are asserted present and
/// well-typed (their exact values are strategy-specific, already the r-sma
/// pin's job above). Gated on the shared GER40 archive; skips cleanly on a
/// data refusal.
#[test]
fn mc_dissolves_a_non_r_sma_blueprint() {
const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("mc-non-r-sma");
let fixture = format!("{}/examples/r_breakout_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"mc", &fixture, "--real", "GER40",
"--axis", "r_breakout_signal.channel_hi.length=10,20",
"--axis", "r_breakout_signal.channel_lo.length=10,20",
"--stop-length", "14", "--stop-k", "2.0",
"--block-len", "5", "--resamples", "1000", "--seed", "42",
"--from", FROM_MS, "--to", TO_MS,
])
.current_dir(&cwd)
.output()
.expect("spawn aura");
if out.status.code() == Some(1) {
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("no local data") || stderr.contains("no recorded geometry"),
"exit 1 must be a data refusal, got: {stderr}"
);
eprintln!("skip: no local GER40 data");
return;
}
assert_eq!(out.status.code(), Some(0), "exit: {:?}", out.status);
let stdout = String::from_utf8(out.stdout).expect("utf-8 stdout");
let grade_line = stdout
.lines()
.find(|l| l.starts_with("{\"mc_r_bootstrap\":"))
.unwrap_or_else(|| panic!("the mc_r_bootstrap grade line: {stdout}"));
let v: serde_json::Value = serde_json::from_str(grade_line).expect("grade line parses as JSON");
let mc = &v["mc_r_bootstrap"];
assert_eq!(mc["block_len"].as_u64(), Some(5), "block_len (argv echo): {grade_line}");
assert_eq!(mc["n_resamples"].as_u64(), Some(1000), "n_resamples (argv echo): {grade_line}");
assert!(mc["n_trades"].as_u64().is_some_and(|n| n > 0), "pooled OOS trade count present: {grade_line}");
assert!(mc["e_r"]["mean"].as_f64().is_some_and(|m| m.is_finite()), "E[R] mean present + finite: {grade_line}");
assert!(
mc["prob_le_zero"].as_f64().is_some_and(|p| (0.0..=1.0).contains(&p)),
"P(E[R] <= 0) present + in [0,1]: {grade_line}"
);
}
/// Property: `aura mc --strategy r-sma --real` is now thin sugar over the one campaign
/// path — a successful run durably auto-registers exactly one generated process
/// document, one generated campaign document (carrying the constant "mc" name and the
@@ -3178,10 +3364,12 @@ fn mc_dissolves_through_the_campaign_path() {
const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000";
let cwd = temp_cwd("mc-dissolves");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"mc", "--strategy", "r-sma", "--real", "GER40",
"--fast", "3,5", "--slow", "12,20", "--stop-length", "14", "--stop-k", "2.0",
"mc", &fixture, "--real", "GER40",
"--axis", "sma_signal.fast.length=3,5", "--axis", "sma_signal.slow.length=12,20",
"--stop-length", "14", "--stop-k", "2.0",
"--block-len", "5", "--resamples", "1000", "--seed", "42",
"--from", FROM_MS, "--to", TO_MS,
])
@@ -3267,10 +3455,12 @@ fn mc_dissolves_through_the_campaign_path() {
#[test]
fn mc_dissolved_refuses_a_multi_value_stop() {
let cwd = temp_cwd("mc-multi-stop");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"mc", "--strategy", "r-sma", "--real", "GER40",
"--fast", "3", "--slow", "12", "--stop-length", "14,20", "--stop-k", "2.0",
"mc", &fixture, "--real", "GER40",
"--axis", "sma_signal.fast.length=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "14,20", "--stop-k", "2.0",
])
.current_dir(&cwd)
.output()
@@ -3280,6 +3470,40 @@ fn mc_dissolved_refuses_a_multi_value_stop() {
assert!(stderr.contains("single risk regime"), "stop-regime refusal: {stderr}");
}
/// Property: an `--axis` name that is not among the loaded blueprint's sweepable
/// axes is refused before the raw-namespace strip or any archive access (exit 2),
/// echoing exactly the name the user typed — mc shares the identical WRAPPED-probe
/// preflight `walkforward_dissolved_refuses_an_unknown_axis_name` pins for
/// walkforward (both dispatchers now call the same `validate_and_register_axes`
/// helper); this test pins mc's own copy so a future edit cannot silently break it
/// while walkforward's stays green. Data-free: no archive/store access.
#[test]
fn mc_dissolved_refuses_an_unknown_axis_name() {
let cwd = temp_cwd("mc-unknown-axis");
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"mc", &fixture, "--real", "GER40",
"--axis", "sma_signal.nope=3", "--axis", "sma_signal.slow.length=12",
"--stop-length", "14", "--stop-k", "2.0",
])
.current_dir(&cwd)
.output()
.expect("spawn aura");
assert_eq!(out.status.code(), Some(2), "an unknown axis name must exit 2");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("axis \"sma_signal.nope\"") && stderr.contains("sweepable axes"),
"must name the unresolved axis exactly as typed: {stderr}"
);
assert!(
out.stdout.is_empty(),
"the refusal must not leak an aggregate to stdout, got: {:?}",
String::from_utf8_lossy(&out.stdout)
);
assert!(!cwd.join("runs").exists(), "a refused axis name must not start a real run");
}
/// Property: the one load-bearing decision new to the mc dissolution — `translate_mc`
/// maps `campaign.seed` to the mc `--seed` (unlike `translate_walkforward`, which
/// hardcodes `seed: 0`) — actually flows through the executor to the emitted grade,
@@ -3297,12 +3521,14 @@ fn mc_dissolved_refuses_a_multi_value_stop() {
fn mc_dissolved_seed_changes_the_bootstrap_draw_not_the_pooled_series() {
const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000";
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
let run = |seed: &str| {
let cwd = temp_cwd(&format!("mc-seed-{seed}"));
std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args([
"mc", "--strategy", "r-sma", "--real", "GER40",
"--fast", "3,5", "--slow", "12,20", "--stop-length", "14", "--stop-k", "2.0",
"mc", &fixture, "--real", "GER40",
"--axis", "sma_signal.fast.length=3,5", "--axis", "sma_signal.slow.length=12,20",
"--stop-length", "14", "--stop-k", "2.0",
"--block-len", "5", "--resamples", "1000", "--seed", seed,
"--from", FROM_MS, "--to", TO_MS,
])