Files
Aura/docs/specs/0101-normalize-cli-usage-casing.md
T
Brummel 7a386dc9bb spec: 0101 normalize CLI usage-message casing (boss-signed)
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
2026-07-02 12:31:48 +02:00

211 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.