feat(cli): default the stop regime on walkforward/mc/generalize
GREEN side of the #217 executable-spec: --stop-length/--stop-k are now optional on the three dissolved verbs; each missing flag independently defaults to the single-sourced regime constants (length 3, k 2.0) at the argv-boundary extractors — the same regime the campaign member runner resolves for an unbound regime. The generated campaign document still binds the regime explicitly, so the default spelling is content-identical to an explicit --stop-length 3 --stop-k 2.0 (pinned by registry dedup in the headline test). The multi-value stop refusal survives byte-identically; sweep is untouched. Sanctioned contract migrations (decision on the issue, 2026-07-09): walkforward_dissolved_refuses_missing_knobs now pins the surviving multi-value refusal; mc_args_from_refuses_missing_knobs pins the default path. Usage strings name the defaults. Held quality nit (deferred to cycle close, minimal-slice constraint): the None=>default / Some=>parse stop block recurs 4x across the walkforward/mc extractors. closes #217
This commit is contained in:
@@ -1654,23 +1654,42 @@ fn walkforward_dissolved_refuses_select_plateau() {
|
||||
assert!(stderr.contains("#215"), "forward pointer to the plateau follow-up: {stderr}");
|
||||
}
|
||||
|
||||
/// `walkforward_args_from` requires both stop knobs on the campaign path — a
|
||||
/// missing knob is a usage refusal (exit 2), not a silent default.
|
||||
/// Property (#217): `walkforward_args_from` no longer requires both stop knobs
|
||||
/// together on the campaign path — each defaults independently to the
|
||||
/// single-sourced regime. Omitting only `--stop-k` while keeping `--stop-length`
|
||||
/// explicit still succeeds, proving the defaulting is per-flag, not
|
||||
/// all-or-nothing (the sibling
|
||||
/// `walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2`
|
||||
/// pins the both-omitted case). Gated on the shared GER40 archive; skips
|
||||
/// cleanly on a data refusal.
|
||||
#[test]
|
||||
fn walkforward_dissolved_refuses_missing_knobs() {
|
||||
let cwd = temp_cwd("walkforward-missing-knobs");
|
||||
fn walkforward_dissolved_defaults_a_single_omitted_knob() {
|
||||
let cwd = temp_cwd("walkforward-single-knob-default");
|
||||
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.fast.length=3", "--axis", "sma_signal.slow.length=12",
|
||||
"--stop-length", "3",
|
||||
])
|
||||
.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 --stop-length --stop-k"), "missing-knob refusal: {stderr}");
|
||||
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),
|
||||
"omitting only --stop-k must still succeed (it defaults independently): {}",
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
/// An empty `--real ""` still satisfies clap's `Option::is_some()` dispatch guard, so
|
||||
|
||||
Reference in New Issue
Block a user