7a386dc9bb
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
211 lines
9.8 KiB
Markdown
211 lines
9.8 KiB
Markdown
# 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")]`) renders
|
||
`Usage: aura <verb> [OPTIONS]` on parse errors and `--help`;
|
||
- the one already-capital hand-rolled site, the built-in `run` branch
|
||
(`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 six `generalize`
|
||
refusals, `"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 the `Usage:` block).
|
||
- The duplicated run-blueprint usage literal (defined in `run_data_from`
|
||
at `main.rs:3991`, verbatim inline copy in `dispatch_run` at
|
||
`main.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):
|
||
|
||
```console
|
||
$ 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):
|
||
|
||
```console
|
||
$ 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):
|
||
|
||
```console
|
||
# 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):
|
||
|
||
```rust
|
||
// 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:
|
||
|
||
1. `cargo build --workspace` clean.
|
||
2. `cargo test --workspace` green with **exactly one** test-file edit
|
||
(the `cli_run.rs:1409` casing flip). Any other failing pin means an
|
||
edit strayed outside the normative table — fix the edit, not the pin.
|
||
3. `cargo clippy --workspace --all-targets -- -D warnings` clean.
|
||
4. 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 ` (or
|
||
`aura: Usage: aura ` where the program prefix is baked into the
|
||
literal).
|
||
5. Smoke: `aura sweep --strategy bogus` prints 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:1409` pins capital `Usage`; no other test bytes change.
|
||
- [ ] Grep gates (Testing strategy #4) pass: no lowercase `usage:`
|
||
literal survives in `crates/aura-cli/src/`.
|
||
- [ ] `cargo build` / `cargo test --workspace` / clippy `-D warnings`
|
||
all green.
|
||
- [ ] Exit codes unchanged at every touched site (usage = 2).
|
||
- [ ] Out-of-scope surfaces byte-identical: refusal diagnostics,
|
||
`run_args_from` literal, clap output, fieldtests corpus, README /
|
||
design-ledger prose.
|