Single house style for every hand-rolled usage line: 'Usage: aura <verb> ...' (clap's capital prefix + program name), 10 literal edits + the one casing-sensitive test pin in lockstep. Refusal diagnostics, the conforming run_args_from site, clap output, and the fieldtests corpus stay byte-identical. Grounding-check PASS is the autonomous signature; scope-fork decisions are recorded on the issue. refs #179
9.8 KiB
Normalize CLI usage-message casing — Design Spec
Date: 2026-07-02 Status: Draft — awaiting sign-off (grounding-check gate; /boss auto-sign) Authors: orchestrator + Claude
Goal
Erase the three-way inconsistency in the aura CLI's hand-rolled usage
messages (issue #179). After the clap adoption (#175), the binary speaks
three styles at once: clap's own parse errors and one hand-rolled site say
Usage: (capital), six hand-rolled sites say usage: (lowercase), and
four hand-rolled closures carry no prefix word at all. This cycle
normalizes every hand-rolled usage line to the single house style clap
already speaks — cosmetic only, no behaviour change: exit codes,
emission channels (stderr), and control flow are byte-for-byte preserved;
only the usage-string literals and their casing-sensitive test pins move,
in lockstep.
Decision log (house-style shape, bare-closure inclusion, refusal exclusion, no de-dup refactor): issue #179, comment "Design reconciliation (specify)".
Architecture
The house style (normative)
One rule: every hand-rolled usage line reads Usage: aura <verb> … —
capital Usage: prefix and the program name included in the grammar,
exactly the shape of the two already-conforming producers:
- clap itself (derive API,
#[command(name = "aura")]) rendersUsage: aura <verb> [OPTIONS]on parse errors and--help; - the one already-capital hand-rolled site, the built-in
runbranch (run_args_from,main.rs:4012):"Usage: aura run [--harness …] …".
The emission convention around the literal is untouched: hand-rolled
sites keep printing through the existing eprintln!("aura: {…}") /
baked-in aura: prefix, so the final stderr line reads
aura: Usage: aura <verb> … — which is already today's output shape of
the conforming run_args_from path. Exit codes keep the #175 partition
(usage = 2, runtime = 1) at every touched site.
Scope boundary
In scope (10 literal edits + 1 test-pin edit): the 6 lowercase
usage: literals, the 4 bare usage closures, and the single
casing-sensitive test pin.
Out of scope (bytes preserved verbatim):
- The already-conforming site
run_args_from(main.rs:4012) and all clap-generated output. - Every aura-specific refusal/diagnostic in the dispatch area — e.g.
"no recorded geometry for symbol …","walkforward <blueprint.json> requires >= 1 --axis to re-fit per window", the sixgeneralizerefusals,"cost flags require an R-evaluator harness …","--harness is not valid with a blueprint file","--list-axes lists axes and takes no other flags","run/mc requires a closed blueprint …". A refusal is a diagnostic, not a usage line; clap itself keeps the two apart (error: …vs theUsage:block). - The duplicated run-blueprint usage literal (defined in
run_data_fromatmain.rs:3991, verbatim inline copy indispatch_runatmain.rs:4187): both copies are normalized identically, but no shared constant is extracted — a de-dup refactor exceeds the cosmetic contract. - Historical fieldtest corpus text quoting an old usage line
(
fieldtests/milestone-cost-model-graph/FINDINGS.md) — the corpus is a tracked record, never retro-edited. - Rustdoc/comment prose saying "usage error" (no colon, not a message).
Concrete code shapes
Worked user-facing example (the acceptance evidence)
Before (bare closure, built-in sweep):
$ aura sweep --strategy bogus
aura: sweep [--strategy <sma|momentum|r-sma|r-breakout|r-meanrev>] [--real <SYMBOL> [--from <ms>] [--to <ms>]] [--name <n> | --trace <n>] [--fast <csv>] [--slow <csv>] [--stop-length <csv>] [--stop-k <csv>] [--channel <csv>] [--window <csv>] [--band-k <csv>]
$ echo $?
2
After (house style; exit code unchanged):
$ aura sweep --strategy bogus
aura: Usage: aura sweep [--strategy <sma|momentum|r-sma|r-breakout|r-meanrev>] [--real <SYMBOL> [--from <ms>] [--to <ms>]] [--name <n> | --trace <n>] [--fast <csv>] [--slow <csv>] [--stop-length <csv>] [--stop-k <csv>] [--channel <csv>] [--window <csv>] [--band-k <csv>]
$ echo $?
2
And a lowercase site (mc, loaded-blueprint branch):
# before
aura: usage: mc <blueprint.json> --seeds <n> [--name <n>]
# after
aura: Usage: aura mc <blueprint.json> --seeds <n> [--name <n>]
Normative literal edits (before → after, exact bytes)
All in crates/aura-cli sources. Only the leading bytes of each literal
change; the grammar tails (… below) are preserved verbatim from the
current tree.
| # | Site | Before (leading bytes) | After (leading bytes) |
|---|---|---|---|
| 1 | graph_construct.rs:191 (introspect_cmd) |
"aura: usage: aura graph introspect …" |
"aura: Usage: aura graph introspect …" |
| 2 | main.rs:3991 (run_data_from) |
"usage: aura run <blueprint.json> …" |
"Usage: aura run <blueprint.json> …" |
| 3 | main.rs:4187 (dispatch_run, inline copy of #2) |
"aura: usage: aura run <blueprint.json> …" |
"aura: Usage: aura run <blueprint.json> …" |
| 4 | main.rs:4272 (dispatch_runs) |
"aura: usage: aura runs family <id> [rank <metric>]" |
"aura: Usage: aura runs family <id> [rank <metric>]" |
| 5 | main.rs:4465 (dispatch_mc, blueprint) |
"usage: mc <blueprint.json> …" |
"Usage: aura mc <blueprint.json> …" |
| 6 | main.rs:4503 (dispatch_mc, built-in) |
"usage: mc [--name <n>|--trace <n>] | mc --strategy r-sma …" |
"Usage: aura mc [--name <n>|--trace <n>] | aura mc --strategy r-sma …" |
| 7 | main.rs:4288 (dispatch_sweep, blueprint) |
"sweep <blueprint.json> --axis …" |
"Usage: aura sweep <blueprint.json> --axis …" |
| 8 | main.rs:4346 (dispatch_sweep, built-in) |
"sweep [--strategy …" |
"Usage: aura sweep [--strategy …" |
| 9 | main.rs:4386 (dispatch_walkforward, blueprint) |
"walkforward <blueprint.json> --axis …" |
"Usage: aura walkforward <blueprint.json> --axis …" |
| 10 | main.rs:4424 (dispatch_walkforward, built-in) |
"walkforward [--strategy …" |
"Usage: aura walkforward [--strategy …" |
Site #6 carries two grammar alternatives separated by |; both
gain the program name (… | aura mc --strategy r-sma …), so the line
stays internally consistent.
Test-pin edit (lockstep, exactly one casing-sensitive pin)
crates/aura-cli/tests/cli_run.rs:1409 (test mc_rejects_real_flag, pinning
site #6):
// before
assert!(err.contains("usage"), …);
// after
assert!(err.contains("Usage"), …);
(The exact assertion text at that line is preserved except for the pinned substring's casing; if the assertion carries a message argument, it is left untouched.)
All other pins are casing-neutral and stay byte-identical, verified
against the current suite: cli_run.rs:116 and :669 already require
capital Usage (satisfied by site main.rs:4012 and by clap,
respectively — both unedited); cli_run.rs:1444/1445 pin the verbs
sweep/walkforward and the flag --real (still substrings after the
prefix is prepended); cli_run.rs:3192/3628/3876 pin out-of-scope
refusal texts (unedited); tests/graph_construct.rs:294-297 pins only
the exit code and an echo of the bogus node name.
Components
| File | Change |
|---|---|
crates/aura-cli/src/main.rs |
9 usage-literal edits (sites #2–#10) |
crates/aura-cli/src/graph_construct.rs |
1 usage-literal edit (site #1) |
crates/aura-cli/tests/cli_run.rs |
1 pin flip (:1409, lowercase usage → Usage) |
No new files, no signature changes, no dependency changes.
Data flow
Unchanged. Every touched literal keeps its current path to the user:
defined in its dispatch branch (as &str, String closure, or inline
eprintln!), printed to stderr with the aura: program prefix,
followed by exit(2). No emission moves between stdout/stderr, no exit
code changes, no message is added or removed — only bytes inside
existing literals change.
Error handling
The #175 exit-code partition is the invariant to preserve: usage errors
exit 2, runtime failures exit 1. All twelve edited sites are on exit-2
paths today and stay there. The suite's exit-code pins
(exit_codes_partition_usage_two_from_runtime_one, the per-command
rejection tests) run unmodified except for the single casing pin above.
Testing strategy
No new tests: the change is cosmetic and the existing suite already pins the touched surface. The gate is the suite itself:
cargo build --workspaceclean.cargo test --workspacegreen with exactly one test-file edit (thecli_run.rs:1409casing flip). Any other failing pin means an edit strayed outside the normative table — fix the edit, not the pin.cargo clippy --workspace --all-targets -- -D warningsclean.- Literal grep gates over
crates/aura-cli/src/:grep -rn '"usage:' crates/aura-cli/src/→ 0 hits;grep -rn 'aura: usage:' crates/aura-cli/src/→ 0 hits;- every hand-rolled usage literal now starts
Usage: aura(oraura: Usage: aurawhere the program prefix is baked into the literal).
- Smoke:
aura sweep --strategy bogusprints the house-style line to stderr and exits 2 (the worked example above).
Acceptance criteria
- All 10 normative literal edits applied exactly as tabled (11 grammar alternatives counting site #6's two; sites #2/#3 remain verbatim twins).
cli_run.rs:1409pins capitalUsage; no other test bytes change.- Grep gates (Testing strategy #4) pass: no lowercase
usage:literal survives incrates/aura-cli/src/. cargo build/cargo test --workspace/ clippy-D warningsall green.- Exit codes unchanged at every touched site (usage = 2).
- Out-of-scope surfaces byte-identical: refusal diagnostics,
run_args_fromliteral, clap output, fieldtests corpus, README / design-ledger prose.