feat(runner, cli): runner-side marker literals + the zero-trade walk-forward notice

Iteration stderr-markers-2, tasks 3-4 of the stderr-class-markers plan.

aura-runner's four continuing-run diagnostics join the class grammar as
hand-written literals (the #295 boundary keeps the macro owner in the
presentation crate): the stale-dylib warning gains the `aura: ` namespace,
and the three tap/no-nominee notices become `aura: note: ` — the latter
two extracted into pure note-builder fns with unit pins. The two e2e
pins on the cost-model tap string moved with the retag
(research_docs.rs). A new project_nodes e2e exercises the real stale-
dylib load path: fresh build emits nothing, a source newer than the
dylib emits exactly one class-marked warning and still succeeds.

The #313 notice ships on both walk-forward paths: `aura: note: all N
walk-forward windows recorded zero trades` before the summary JSON,
exit 0 untouched (a null result is a valid research result, #198).
Wording and condition live in one shared diag helper taking the
per-window trade counts (orchestrator tidy of the reviewer's
duplicated-predicate residue); the call sites keep only the
path-specific field extraction. Three e2es pin it: the equal-length
degenerate on the synthetic path (exactly one note, no warnings, exit
0, summary unchanged), a traded negative twin, and the real/sugar-path
degenerate gated on the GER40 archive.

refs #278, refs #313
This commit is contained in:
2026-07-23 23:35:23 +02:00
parent 34987be389
commit 1102d776df
8 changed files with 221 additions and 7 deletions
+95
View File
@@ -4782,6 +4782,101 @@ fn aura_walkforward_over_a_blueprint_persists_a_family() {
let _ = std::fs::remove_dir_all(&cwd);
}
/// E2E (#313, Path 2 — synthetic): a walk-forward whose every window records
/// zero trades emits exactly one `aura: note: all N walk-forward windows
/// recorded zero trades` on stderr; exit stays 0 and the summary JSON is
/// unchanged (a null result is a valid research result, #198). Equal
/// fast/slow lengths make the SMA difference constantly zero, so the bias
/// never leaves 0 and no window trades.
#[test]
fn aura_walkforward_all_zero_trade_windows_emit_one_note() {
let cwd = temp_cwd("blueprint-walkforward-zero-trade-note");
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=8",
"--axis", "sma_signal.slow.length=8", "--name", "wfzero"])
.current_dir(&cwd)
.output()
.expect("spawn aura walkforward (degenerate)");
let stderr = String::from_utf8_lossy(&out.stderr).to_string();
let stdout = String::from_utf8_lossy(&out.stdout).to_string();
assert_eq!(out.status.code(), Some(0), "exit stays 0: {stderr}");
assert!(stdout.contains("\"walkforward\""), "summary line unchanged: {stdout}");
assert_eq!(
stderr.matches("walk-forward windows recorded zero trades").count(),
1,
"exactly one zero-trade note: {stderr}"
);
assert!(
stderr.contains("aura: note: all "),
"the note carries the class marker: {stderr}"
);
assert!(
!stderr.contains("aura: warning: "),
"a benign degenerate run emits no warning lines: {stderr}"
);
let _ = std::fs::remove_dir_all(&cwd);
}
/// E2E (#313 negative twin): a walk-forward with at least one traded window
/// emits no zero-trade note.
#[test]
fn aura_walkforward_with_trades_emits_no_zero_trade_note() {
let cwd = temp_cwd("blueprint-walkforward-traded-no-note");
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=2,3",
"--axis", "sma_signal.slow.length=4,6", "--name", "wftrades"])
.current_dir(&cwd)
.output()
.expect("spawn aura walkforward (trading)");
let stderr = String::from_utf8_lossy(&out.stderr).to_string();
assert_eq!(out.status.code(), Some(0), "exit 0: {stderr}");
assert!(
!stderr.contains("recorded zero trades"),
"a traded run emits no zero-trade note: {stderr}"
);
let _ = std::fs::remove_dir_all(&cwd);
}
/// E2E (#313, Path 1 — the real/sugar walk-forward): the degenerate
/// equal-length config over the shared GER40 archive emits the same
/// zero-trade note. Gated on the archive; skips cleanly on a data refusal.
#[test]
fn walkforward_real_all_zero_trade_windows_emit_the_note() {
const FROM_MS: &str = "1735689600000";
const TO_MS: &str = "1767225599000";
let (cwd, _g) = fresh_project();
let fixture = format!("{}/examples/r_sma.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=5", "--axis", "sma_signal.slow.length=5",
"--stop-length", "14", "--stop-k", "2.0",
"--from", FROM_MS, "--to", TO_MS,
])
.current_dir(&cwd)
.output()
.expect("spawn aura walkforward (real degenerate)");
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 stderr = String::from_utf8_lossy(&out.stderr).to_string();
assert_eq!(
stderr.matches("walk-forward windows recorded zero trades").count(),
1,
"exactly one zero-trade note on the real path: {stderr}"
);
assert!(stderr.contains("aura: note: all "), "class marker: {stderr}");
}
/// E2E (#173): a CLOSED blueprint with no --axis (nothing to re-fit) is a usage
/// error (exit 2), naming that >= 1 --axis is required.
#[test]