fix(cli): list valid taps on bad --tap, make --help uniform across subcommands

Two CLI-UX defects the Stage-1 R fieldtest surfaced (#131):

1. Tap discovery. `aura chart <name> --tap <bad>` rejected an unknown tap but
   the error omitted the valid names. filter_to_tap now enumerates the run's
   available taps in the message ("... (available: equity, exposure)"), so the
   user can correct a typo instead of guessing -- refuse, but help.

2. Uniform --help. `aura chart --help` errored with "unexpected chart argument
   '--help'" (exit 2) and `aura run --help` leaked usage to stderr via the
   error path, while bare `aura --help` printed to stdout (exit 0). main() now
   intercepts `--help`/`-h` anywhere in the invocation, prints usage to stdout,
   and exits 0 -- uniform for every subcommand. The now-dead bare-flag match arm
   is removed (the early intercept covers it).

RED-first: the existing chart_tap_nonexistent test gains an available-taps
assertion (was: message had no names), and a new per_subcommand_help test pins
`aura <sub> --help` -> stdout usage + exit 0 for run/chart/sweep/walkforward/
mc/graph/runs (was: chart --help exited 2). Unknown-subcommand stays exit 2.

Verified: clippy --all-targets -D warnings clean; full suite 514 passed / 0
failed (one new test); live `aura chart --help` prints usage to stdout, exit 0.

closes #131
This commit is contained in:
2026-06-24 14:35:51 +02:00
parent 0288878ad2
commit 5c6de45b6e
2 changed files with 48 additions and 4 deletions
+12 -4
View File
@@ -467,10 +467,12 @@ fn build_comparison_chart_data(
/// run has no such tap (refuse-don't-guess). Used by the `--tap` flag on the
/// single-run chart path; without `--tap` the single-run page is unchanged.
fn filter_to_tap(data: ChartData, tap: &str) -> Result<ChartData, String> {
let series: Vec<Series> = data.series.into_iter().filter(|s| s.name == tap).collect();
if series.is_empty() {
return Err(format!("run has no tap named '{tap}'"));
if !data.series.iter().any(|s| s.name == tap) {
// List the valid taps so the user can correct a typo (#131) — refuse, but help.
let available: Vec<&str> = data.series.iter().map(|s| s.name.as_str()).collect();
return Err(format!("run has no tap named '{tap}' (available: {})", available.join(", ")));
}
let series: Vec<Series> = data.series.into_iter().filter(|s| s.name == tap).collect();
let mut meta = data.meta;
meta.taps = vec![tap.to_string()];
Ok(ChartData { xs: data.xs, series, meta })
@@ -1966,6 +1968,13 @@ fn main() {
// so an unexpected trailing token falls through to the usage-error path rather
// than masquerading as a successful run (#16 strict reading).
let args: Vec<String> = std::env::args().skip(1).collect();
// A `--help` / `-h` anywhere prints usage to stdout and exits 0, uniformly across
// subcommands (#131): `aura chart --help` shows usage instead of erroring
// "unexpected argument", and the bare `aura --help` is the same affordance.
if args.iter().any(|a| a == "--help" || a == "-h") {
println!("{USAGE}");
return;
}
match args.iter().map(String::as_str).collect::<Vec<_>>().as_slice() {
["run", rest @ ..] => match parse_run_args(rest) {
Ok(args) => match run_dispatch(args) {
@@ -2012,7 +2021,6 @@ fn main() {
["runs", "families"] => runs_families(),
["runs", "family", id] => runs_family(id, None),
["runs", "family", id, "rank", metric] => runs_family(id, Some(metric)),
["--help"] | ["-h"] => println!("{USAGE}"),
_ => {
eprintln!("aura: {USAGE}");
std::process::exit(2);