diff --git a/crates/aura-cli/src/graph_construct.rs b/crates/aura-cli/src/graph_construct.rs index 148a175..d9f91e4 100644 --- a/crates/aura-cli/src/graph_construct.rs +++ b/crates/aura-cli/src/graph_construct.rs @@ -188,7 +188,7 @@ pub fn introspect_cmd(cmd: crate::GraphIntrospectCmd) { + cmd.content_id as usize; if count != 1 { eprintln!( - "aura: usage: aura graph introspect --vocabulary | --node | --unwired | --content-id" + "aura: Usage: aura graph introspect --vocabulary | --node | --unwired | --content-id" ); std::process::exit(2); } diff --git a/crates/aura-cli/src/main.rs b/crates/aura-cli/src/main.rs index b01b777..0df2ae9 100644 --- a/crates/aura-cli/src/main.rs +++ b/crates/aura-cli/src/main.rs @@ -3988,7 +3988,7 @@ fn is_blueprint_file(arg: &Option) -> Option<&str> { /// mirroring the old `parse_blueprint_run_args` window guard (`--from`/`--to` /// require `--real`; empty symbol rejected). Refuses in place (stderr + exit 2). fn run_data_from(real: Option<&str>, from: Option, to: Option) -> RunData { - let usage = "usage: aura run [--params ] [--seed ] [--real [--from ] [--to ]]"; + let usage = "Usage: aura run [--params ] [--seed ] [--real [--from ] [--to ]]"; match real { Some(s) if !s.is_empty() => RunData::Real { symbol: s.to_string(), from, to }, Some(_) => { @@ -4184,7 +4184,7 @@ fn dispatch_run(a: RunCmd) { || a.slip_vol_mult.is_some() || a.carry_per_cycle.is_some() { - eprintln!("aura: usage: aura run [--params ] [--seed ] [--real [--from ] [--to ]]"); + eprintln!("aura: Usage: aura run [--params ] [--seed ] [--real [--from ] [--to ]]"); std::process::exit(2); } let doc = std::fs::read_to_string(path).unwrap_or_else(|e| { @@ -4269,7 +4269,7 @@ fn dispatch_runs(a: RunsCmd) { (None, None) => runs_family(&id, None), (Some("rank"), Some(m)) => runs_family(&id, Some(&m)), _ => { - eprintln!("aura: usage: aura runs family [rank ]"); + eprintln!("aura: Usage: aura runs family [rank ]"); std::process::exit(2); } }, @@ -4285,7 +4285,7 @@ fn dispatch_reproduce(a: ReproduceCmd) { fn dispatch_sweep(a: SweepCmd) { match is_blueprint_file(&a.blueprint) { Some(path) => { - let usage = || "sweep --axis = [--axis …] [--name | --trace ] [--real [--from ] [--to ]]".to_string(); + let usage = || "Usage: aura sweep --axis = [--axis …] [--name | --trace ] [--real [--from ] [--to ]]".to_string(); let doc = std::fs::read_to_string(path).unwrap_or_else(|e| { eprintln!("aura: {path}: {e}"); std::process::exit(2); @@ -4343,7 +4343,7 @@ fn dispatch_sweep(a: SweepCmd) { run_blueprint_sweep(&doc, &axes, &name, persist, DataSource::from_choice(data)); } None => { - let usage = || "sweep [--strategy ] [--real [--from ] [--to ]] [--name | --trace ] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--channel ] [--window ] [--band-k ]".to_string(); + let usage = || "Usage: aura sweep [--strategy ] [--real [--from ] [--to ]] [--name | --trace ] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--channel ] [--window ] [--band-k ]".to_string(); if a.blueprint.is_some() || !a.axis.is_empty() || a.list_axes { eprintln!("aura: {}", usage()); std::process::exit(2); @@ -4383,7 +4383,7 @@ fn dispatch_sweep(a: SweepCmd) { fn dispatch_walkforward(a: WalkforwardCmd) { match is_blueprint_file(&a.blueprint) { Some(path) => { - let usage = || "walkforward --axis = [--axis …] [--select ] [--name ]".to_string(); + let usage = || "Usage: aura walkforward --axis = [--axis …] [--select ] [--name ]".to_string(); let doc = std::fs::read_to_string(path).unwrap_or_else(|e| { eprintln!("aura: {path}: {e}"); std::process::exit(2); @@ -4421,7 +4421,7 @@ fn dispatch_walkforward(a: WalkforwardCmd) { run_blueprint_walkforward(&doc, &axes, &name, DataSource::Synthetic, select); } None => { - let usage = || "walkforward [--strategy ] [--real [--from ] [--to ]] [--name | --trace ] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--select ]".to_string(); + let usage = || "Usage: aura walkforward [--strategy ] [--real [--from ] [--to ]] [--name | --trace ] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--select ]".to_string(); if a.blueprint.is_some() || !a.axis.is_empty() { eprintln!("aura: {}", usage()); std::process::exit(2); @@ -4462,7 +4462,7 @@ fn dispatch_walkforward(a: WalkforwardCmd) { fn dispatch_mc(a: McCmd) { match is_blueprint_file(&a.blueprint) { Some(path) => { - let usage = "usage: mc --seeds [--name ]"; + let usage = "Usage: aura mc --seeds [--name ]"; let doc = std::fs::read_to_string(path).unwrap_or_else(|e| { eprintln!("aura: {path}: {e}"); std::process::exit(2); @@ -4500,7 +4500,7 @@ fn dispatch_mc(a: McCmd) { run_blueprint_mc(&doc, n_seeds, &name, DataSource::Synthetic); } None => { - let usage = || "usage: mc [--name |--trace ] | mc --strategy r-sma [--real [--from ] [--to ]] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--block-len ] [--resamples ] [--seed ]".to_string(); + let usage = || "Usage: aura mc [--name |--trace ] | aura mc --strategy r-sma [--real [--from ] [--to ]] [--fast ] [--slow ] [--stop-length ] [--stop-k ] [--block-len ] [--resamples ] [--seed ]".to_string(); if a.blueprint.is_some() || a.seeds.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 8cd6711..ef86ae0 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -1406,7 +1406,7 @@ fn mc_rejects_real_flag_with_usage_exit_2() { assert_eq!(out.status.code(), Some(2), "mc --real must exit 2; status: {:?}", out.status); assert!(out.stdout.is_empty(), "mc --real must not emit a run on stdout: {:?}", out.stdout); let stderr = String::from_utf8_lossy(&out.stderr); - assert!(stderr.contains("usage"), "mc --real stderr must carry usage: {stderr:?}"); + assert!(stderr.contains("Usage"), "mc --real stderr must carry usage: {stderr:?}"); // the exclusion is total: no `runs/` registry write either. assert!(!dir.join("runs").exists(), "mc --real must not start a real run"); @@ -1447,6 +1447,85 @@ fn family_window_flag_without_real_refuses_with_usage_exit_2() { } } +/// Property (house style, #179): every hand-rolled usage line reads +/// `Usage: aura …` — capital `Usage:` plus the program name inside the +/// grammar. `sweep`/`walkforward`'s built-in-branch usage closures were bare +/// (`"sweep [--strategy …]"`, no prefix, no program name) before cycle 0101; a +/// regression that drops the prefix again would NOT be caught by the older +/// `family_window_flag_without_real_refuses_with_usage_exit_2` pin above, which +/// only checks for the verb token and `--real` — not the `Usage: aura` lead-in. +/// An unknown `--strategy` name reaches this same closure on both commands, so +/// it is the smallest trigger for each. +#[test] +fn builtin_branch_usage_lines_lead_with_the_house_style_prefix() { + for (args, verb) in [(&["sweep", "--strategy", "bogus"][..], "sweep"), (&["walkforward", "--strategy", "bogus"][..], "walkforward")] { + let dir = temp_cwd("builtin_usage_prefix"); + let out = Command::new(BIN) + .args(args) + .current_dir(&dir) + .output() + .unwrap_or_else(|e| panic!("spawn aura {args:?}: {e}")); + assert_eq!(out.status.code(), Some(2), "`aura {args:?}` must exit 2; status: {:?}", out.status); + let stderr = String::from_utf8_lossy(&out.stderr); + let want = format!("aura: Usage: aura {verb} "); + assert!(stderr.starts_with(&want), "`aura {args:?}` stderr must lead with {want:?}: {stderr:?}"); + let _ = std::fs::remove_dir_all(&dir); + } +} + +/// Property (house style, #179): the built-in `mc` usage line carries TWO +/// `|`-separated grammar alternatives, and BOTH name the program (`aura mc …`), +/// not just the first. This is the one lockstep-pinned site +/// (`mc_rejects_real_flag_with_usage_exit_2` above), but that pin only checks +/// for a single `"Usage"` occurrence — a regression that fixed only the first +/// alternative (leaving `… | mc --strategy r-sma …` unprefixed) would still +/// pass it. `--seeds` with no blueprint file is the built-in branch's own +/// grammar violation, reaching this exact literal. +#[test] +fn mc_builtin_usage_names_the_program_in_both_alternatives() { + let dir = temp_cwd("mc_usage_both_alts"); + let out = Command::new(BIN) + .args(["mc", "--seeds", "3"]) + .current_dir(&dir) + .output() + .expect("spawn aura mc --seeds"); + assert_eq!(out.status.code(), Some(2), "mc --seeds without a blueprint must exit 2; status: {:?}", out.status); + let stderr = String::from_utf8_lossy(&out.stderr); + assert!( + stderr.contains("Usage: aura mc [--name"), + "first alternative must name the program: {stderr:?}" + ); + assert!( + stderr.contains("| aura mc --strategy r-sma"), + "second alternative must ALSO name the program: {stderr:?}" + ); + let _ = std::fs::remove_dir_all(&dir); +} + +/// Property (house style, #179): `mc`'s loaded-blueprint branch usage line +/// also normalizes to `Usage: aura mc …` — before cycle 0101 +/// it was fully lowercase and carried no program name (`"usage: mc +/// …"`). Reusing `sma_signal.json` (an existing closed +/// blueprint fixture) only to reach the dispatch branch; omitting `--seeds` +/// is this branch's own grammar violation. +#[test] +fn mc_over_a_blueprint_usage_names_the_program() { + let dir = temp_cwd("mc_blueprint_usage_prefix"); + let fixture = format!("{}/tests/fixtures/sma_signal.json", env!("CARGO_MANIFEST_DIR")); + let out = Command::new(BIN) + .args(["mc", &fixture]) + .current_dir(&dir) + .output() + .expect("spawn aura mc "); + assert_eq!(out.status.code(), Some(2), "mc without --seeds must exit 2; status: {:?}", out.status); + let stderr = String::from_utf8_lossy(&out.stderr); + assert!( + stderr.contains("aura: Usage: aura mc "), + "stderr must lead with the house-style, program-named usage: {stderr:?}" + ); + let _ = std::fs::remove_dir_all(&dir); +} + /// Property: `aura chart ` overlays one series per family member, labelled /// by member key. The built-in sweep grid is fast∈{2,3} × slow∈{4,5} = 4 members; /// each member key appears as a series name in the injected AURA_TRACES.