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
|
||||
/// successful run durably auto-registers exactly one generated process document,
|
||||
/// one generated campaign document (carrying the `--name` handle and the stop as
|
||||
|
||||
Reference in New Issue
Block a user