test: RED executable-spec for #217 — stop-less verb invocation defaults the regime
walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2 pins the decided contract (#217 decision comment, 2026-07-09): a walkforward invocation without --stop-length/--stop-k succeeds, each omitted knob defaulting to the single-sourced regime (length 3, k 2.0), and produces a campaign document content-identical to the explicit --stop-length 3 --stop-k 2.0 spelling (registry dedup: exactly one doc). Currently RED: the verb refuses with the requires-knobs usage error. refs #217
This commit is contained in:
@@ -1859,6 +1859,101 @@ fn walkforward_dissolves_through_the_campaign_path() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Property (#217): on the dissolved `aura walkforward --real` path,
|
||||||
|
/// `--stop-length`/`--stop-k` are OPTIONAL — a stop-less invocation succeeds,
|
||||||
|
/// defaulting each omitted knob to the single-sourced default regime (length 3,
|
||||||
|
/// k 2.0), the very regime the campaign member runner already resolves for an
|
||||||
|
/// unbound regime (`stop_rule_for_regime`). The default is byte-identical to
|
||||||
|
/// spelling the knobs out: running the same blueprint in the same store first
|
||||||
|
/// stop-less, then with an explicit `--stop-length 3 --stop-k 2.0`, leaves
|
||||||
|
/// EXACTLY ONE generated campaign document — the two documents are byte-identical
|
||||||
|
/// so content-addressed storage collapses them onto one content id — while each
|
||||||
|
/// invocation still records its own campaign-run line. This aligns walkforward's
|
||||||
|
/// knob contract with sweep's (which already defaults the stop) without any
|
||||||
|
/// content-id churn on the explicit spelling. The sibling verbs mc/generalize gain
|
||||||
|
/// the same independent per-flag defaulting on the GREEN side. Gated on the shared
|
||||||
|
/// GER40 archive; skips cleanly on a data refusal. (The multi-value stop is still
|
||||||
|
/// refused — `walkforward_dissolved_refuses_a_multi_value_stop` — the stop stays a
|
||||||
|
/// single risk regime; only its *presence* becomes optional.)
|
||||||
|
#[test]
|
||||||
|
fn walkforward_stopless_defaults_the_regime_byte_identically_to_explicit_3_2() {
|
||||||
|
const FROM_MS: &str = "1735689600000";
|
||||||
|
const TO_MS: &str = "1767225599000";
|
||||||
|
let cwd = temp_cwd("walkforward-stopless-defaults");
|
||||||
|
let fixture = format!("{}/examples/r_sma_open.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
|
||||||
|
// Stop-less: the two knobs are omitted entirely. This is the headline — today it
|
||||||
|
// is refused at the argv boundary (exit 2, "requires --stop-length --stop-k");
|
||||||
|
// the feature makes the knobs optional so this run defaults the regime and runs.
|
||||||
|
let stopless = 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",
|
||||||
|
"--from", FROM_MS, "--to", TO_MS,
|
||||||
|
])
|
||||||
|
.current_dir(&cwd)
|
||||||
|
.output()
|
||||||
|
.expect("spawn aura");
|
||||||
|
if stopless.status.code() == Some(1) {
|
||||||
|
let stderr = String::from_utf8_lossy(&stopless.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!(
|
||||||
|
stopless.status.code(),
|
||||||
|
Some(0),
|
||||||
|
"a stop-less walkforward must succeed (the stop knobs are optional, each defaulting \
|
||||||
|
to the single-sourced 3/2.0 regime): {}",
|
||||||
|
String::from_utf8_lossy(&stopless.stderr)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Explicit 3/2.0: names the very defaults the stop-less run resolves. Same
|
||||||
|
// blueprint, same axes, same default `--name` ("walkforward"), same window.
|
||||||
|
let explicit = 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", "--stop-k", "2.0",
|
||||||
|
"--from", FROM_MS, "--to", TO_MS,
|
||||||
|
])
|
||||||
|
.current_dir(&cwd)
|
||||||
|
.output()
|
||||||
|
.expect("spawn aura");
|
||||||
|
assert_eq!(
|
||||||
|
explicit.status.code(),
|
||||||
|
Some(0),
|
||||||
|
"the explicit --stop-length 3 --stop-k 2.0 walkforward must succeed: {}",
|
||||||
|
String::from_utf8_lossy(&explicit.stderr)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Byte-identity: the default regime IS 3/2.0, so both invocations generate the
|
||||||
|
// same campaign document and content-addressed storage collapses them onto one
|
||||||
|
// content id — exactly one campaign document in the store.
|
||||||
|
let count = |sub: &str| {
|
||||||
|
std::fs::read_dir(cwd.join("runs").join(sub))
|
||||||
|
.map(|d| d.count())
|
||||||
|
.unwrap_or(0)
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
count("campaigns"),
|
||||||
|
1,
|
||||||
|
"the stop-less default binds the SAME 3/2.0 regime as the explicit run, so the two \
|
||||||
|
campaign documents are byte-identical and dedup to one content id"
|
||||||
|
);
|
||||||
|
let runs_log = std::fs::read_to_string(cwd.join("runs").join("campaign_runs.jsonl"))
|
||||||
|
.expect("campaign_runs.jsonl exists after two sugar runs");
|
||||||
|
assert_eq!(
|
||||||
|
runs_log.lines().count(),
|
||||||
|
2,
|
||||||
|
"each invocation still records its own campaign run (a run is an event, a document \
|
||||||
|
is content)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Property: `aura generalize` is now thin sugar over the one campaign path — a
|
/// Property: `aura generalize` is now thin sugar over the one campaign path — a
|
||||||
/// successful run durably auto-registers exactly one generated process document,
|
/// successful run durably auto-registers exactly one generated process document,
|
||||||
/// one generated campaign document (carrying the `--name` handle and the stop as
|
/// one generated campaign document (carrying the `--name` handle and the stop as
|
||||||
|
|||||||
Reference in New Issue
Block a user