diff --git a/crates/aura-cli/src/main.rs b/crates/aura-cli/src/main.rs index 83c8880..3a3ab48 100644 --- a/crates/aura-cli/src/main.rs +++ b/crates/aura-cli/src/main.rs @@ -447,7 +447,7 @@ fn emit_chart(name: &str, tap: Option<&str>, mode: ChartMode, env: &project::Env NameKind::NotFound => { eprintln!( "aura: no recorded run or family '{name}' under runs/traces \ - (run `aura run --trace {name}` or `aura sweep --trace {name}` first)" + (run `aura run --trace {name}` first)" ); std::process::exit(1); } @@ -2174,7 +2174,7 @@ struct SweepCmd { /// Family name (records to the registry without persisting per-member traces). #[arg(long)] name: Option, - /// Family name that also persists each member's taps (mutually exclusive with --name). + /// Family name (not yet available on this verb; see #224). #[arg(long)] trace: Option, /// Blueprint sweep axis `=` (repeatable; .json mode). @@ -2201,7 +2201,7 @@ struct WalkforwardCmd { /// Family name (records to the registry without persisting per-member traces). #[arg(long)] name: Option, - /// Family name that also persists each OOS window's taps (mutually exclusive with --name). + /// Family name (not yet available on this verb; see #224). #[arg(long)] trace: Option, /// Campaign-path stop length (single value; --real mode). @@ -2795,9 +2795,17 @@ fn dispatch_new(a: NewCmd, _env: &project::Env) { fn dispatch_sweep(a: SweepCmd, env: &project::Env) { // Single-sourced: the blueprint grammar and the no-blueprint usage error must // stay in lockstep, so both arms below read this one closure. - let usage = || "Usage: aura sweep --axis = [--axis …] [--name | --trace ] [--real [--from ] [--to ]]".to_string(); + let usage = || "Usage: aura sweep --axis = [--axis …] [--name ] [--real [--from ] [--to ]]".to_string(); match is_blueprint_file(&a.blueprint) { Some(path) => { + // No blueprint-mode arm persists per-member taps (synthetic drops it via + // `let _ = persist`, `--real` sugar sets `persist_taps: vec![]`) — refuse + // rather than silently accept an advertised-but-unhonoured flag, mirroring + // the `aura run`/`aura mc` --trace refusals. + if a.trace.is_some() { + eprintln!("aura: --trace is not yet available on sweep; see #224"); + std::process::exit(2); + } let doc = std::fs::read_to_string(path).unwrap_or_else(|e| { eprintln!("aura: {path}: {e}"); std::process::exit(2); @@ -2817,7 +2825,6 @@ fn dispatch_sweep(a: SweepCmd, env: &project::Env) { // A query, not a sweep: it must stand alone. if !a.axis.is_empty() || a.name.is_some() - || a.trace.is_some() || a.real.is_some() || a.from.is_some() || a.to.is_some() @@ -2901,9 +2908,16 @@ fn dispatch_sweep(a: SweepCmd, env: &project::Env) { /// #220: arbitrary user blueprint + axes, formerly the welded r-sma branch). fn dispatch_walkforward(a: WalkforwardCmd, env: &project::Env) { // Single-sourced: both arms below read this one closure (house style, #179). - let usage = || format!("Usage: aura walkforward --axis = [--axis …] [--select ] [--name ] | aura walkforward --real --axis = [--axis …] [--stop-length (default {R_SMA_STOP_LENGTH})] [--stop-k (default {R_SMA_STOP_K:.1})] [--from ] [--to ] [--name | --trace ]"); + let usage = || format!("Usage: aura walkforward --axis = [--axis …] [--select ] [--name ] | aura walkforward --real --axis = [--axis …] [--stop-length (default {R_SMA_STOP_LENGTH})] [--stop-k (default {R_SMA_STOP_K:.1})] [--from ] [--to ] [--name ]"); match is_blueprint_file(&a.blueprint) { Some(path) => { + // No blueprint-mode arm persists per-window taps — refuse rather than + // silently accept an advertised-but-unhonoured flag, mirroring the + // `aura run`/`aura mc` --trace refusals. + if a.trace.is_some() { + eprintln!("aura: --trace is not yet available on walkforward; see #224"); + std::process::exit(2); + } let doc = std::fs::read_to_string(path).unwrap_or_else(|e| { eprintln!("aura: {path}: {e}"); std::process::exit(2); @@ -2983,7 +2997,7 @@ fn dispatch_walkforward(a: WalkforwardCmd, env: &project::Env) { return; } // Synthetic in-process family path — unchanged (#220 non-goal). - if a.stop_length.is_some() || a.stop_k.is_some() || a.trace.is_some() { + if a.stop_length.is_some() || a.stop_k.is_some() { eprintln!("aura: {}", usage()); std::process::exit(2); } diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index 733b920..ba593f5 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -1936,10 +1936,10 @@ fn walkforward_dissolved_refuses_an_unknown_select_token() { assert!(stderr.contains("Usage: aura walkforward"), "unknown-select refusal: {stderr}"); } -/// `--trace`'s own help text ("mutually exclusive with --name") is a contract the -/// inline path already enforces via `name_persist`; the dissolved r-sma-real path -/// must refuse the same combination (exit 2) rather than silently letting --name -/// win over --trace. +/// `--trace` is now refused up front on every blueprint-mode arm (#168, before the +/// `--name`/`--trace` mutual-exclusion check even runs) — the dissolved r-sma-real +/// path must surface that #224 forward-pointer refusal (exit 2), not the generic +/// mutual-exclusion message which its early return now pre-empts. #[test] fn walkforward_dissolved_refuses_name_and_trace_together() { let cwd = temp_cwd("walkforward-name-and-trace"); @@ -1954,9 +1954,10 @@ fn walkforward_dissolved_refuses_name_and_trace_together() { .current_dir(&cwd) .output() .expect("spawn aura"); - assert_eq!(out.status.code(), Some(2), "--name and --trace together is a usage refusal"); + assert_eq!(out.status.code(), Some(2), "--trace is refused up front (exit 2)"); let stderr = String::from_utf8_lossy(&out.stderr); - assert!(stderr.contains("mutually exclusive"), "name/trace refusal: {stderr}"); + assert!(stderr.contains("--trace"), "refusal names the flag: {stderr}"); + assert!(stderr.contains("#224"), "refusal points forward to #224: {stderr}"); } /// Property (#210 T3, dispatch split): a successful real-data walkforward @@ -2717,7 +2718,7 @@ fn aura_sweep_loads_a_blueprint_and_persists_a_sweep_family() { "sweep", &fixture, "--axis", "sma_signal.fast.length=2,4", "--axis", "sma_signal.slow.length=8,16", - "--trace", "f", + "--name", "f", ]) .current_dir(&cwd) .output() @@ -2804,7 +2805,7 @@ fn aura_sweep_persists_the_canonical_blueprint_keyed_by_topology_hash() { "sweep", &fixture, "--axis", "sma_signal.fast.length=2,4", "--axis", "sma_signal.slow.length=8,16", - "--trace", "f", + "--name", "f", ]) .current_dir(&cwd) .output() @@ -2881,7 +2882,7 @@ fn aura_reproduce_re_derives_a_persisted_sweep_family() { "sweep", &fixture, "--axis", "sma_signal.fast.length=2,4", "--axis", "sma_signal.slow.length=8,16", - "--trace", "f", + "--name", "f", ]) .current_dir(&cwd) .output() @@ -2922,7 +2923,7 @@ fn aura_reproduce_reports_a_diverged_member_and_exits_one() { "sweep", &fixture, "--axis", "sma_signal.fast.length=2,4", "--axis", "sma_signal.slow.length=8,16", - "--trace", "f", + "--name", "f", ]) .current_dir(&cwd) .output()