refactor(aura-cli): retire the dead-end runs list/rank CLI surface

Since cycle 0045 the flat runs.jsonl store has had no producer (sweep/mc/
walkforward persist to the family store; `aura run` does not persist), so
`aura runs list` / `runs rank` read a store nothing populates — a live dead-end
surface, the honesty gap the Runway milestone closes.

Resolve the C18 deferred fork (INDEX.md) as (b) RETIRE: drop the two CLI commands
+ their dispatch arms, usage fragments, and the now-unused load_runs_or_exit.
Families (sweep/mc/walkforward, C21) subsume standalone over-time comparison. The
aura-registry flat lib API (append/load/rank_by/optimize) is RETAINED — optimize
backs walk-forward's in-sample step, rank_by backs `runs family ... rank`.
`runs families` / `runs family` are untouched.

Decision recorded on #73 (user-directed (b), 2026-06-18). Cosmetic sub-item
(json! family-wrapper -> to_json() stdout key-order unification) deferred to a
follow-up: json! routes the embedded report through serde_json::Value, which
re-alphabetizes keys, so it needs manual JSON construction, not a trivial swap.

RED-first: the retire test asserted `runs list`/`rank` now exit 2 (was exit 0).
Verified: cargo build --workspace, clippy --all-targets -D warnings, and
cargo test --workspace all green.

closes #73
This commit is contained in:
2026-06-18 19:53:26 +02:00
parent 03f80f0a95
commit 3dac2c0c99
3 changed files with 18 additions and 47 deletions
+2 -35
View File
@@ -683,28 +683,6 @@ fn mc_report() -> String {
out
}
/// `aura runs list`: print every stored run record, in store (over-time) order.
fn runs_list() {
for report in &load_runs_or_exit() {
println!("{}", report.to_json());
}
}
/// `aura runs rank <metric>`: print the stored runs best-first by `metric`.
fn runs_rank(metric: &str) {
match rank_by(load_runs_or_exit(), metric) {
Ok(ranked) => {
for report in &ranked {
println!("{}", report.to_json());
}
}
Err(e) => {
eprintln!("aura: {e}");
std::process::exit(2);
}
}
}
/// `aura runs families`: one header line per stored family (id, kind, member
/// count), in first-seen store order.
fn runs_families() {
@@ -726,8 +704,7 @@ fn runs_families() {
/// `aura runs family <id> [rank <metric>]`: list one family's member reports in
/// ordinal order, or best-first by `metric`. An unknown id is an empty family
/// (prints nothing, exit 0, consistent with `runs list` over an empty store); an
/// unknown metric is a usage error (stderr + exit 2).
/// (prints nothing, exit 0); an unknown metric is a usage error (stderr + exit 2).
fn runs_family(id: &str, rank: Option<&str>) {
let reg = default_registry();
let members = match reg.load_family_members() {
@@ -756,14 +733,6 @@ fn runs_family(id: &str, rank: Option<&str>) {
}
}
/// Load the registry or exit 2 with the error. A missing registry loads empty.
fn load_runs_or_exit() -> Vec<RunReport> {
default_registry().load().unwrap_or_else(|e| {
eprintln!("aura: {e}");
std::process::exit(2);
})
}
// --- MACD proof-of-concept (a richer, nested indicator + strategy) -----------
/// The MACD signal as a named composite: price → fast/slow `Ema` → the MACD line
@@ -887,7 +856,7 @@ fn run_macd() -> RunReport {
}
const USAGE: &str =
"usage: aura run [--macd] | aura run --real <SYMBOL> [--from <ms>] [--to <ms>] | aura graph | aura sweep [--name <n>] | aura mc [--name <n>] | aura walkforward [--name <n>] | aura runs list | aura runs rank <metric> | aura runs families | aura runs family <id> [rank <metric>]";
"usage: aura run [--macd] | aura run --real <SYMBOL> [--from <ms>] [--to <ms>] | aura graph | aura sweep [--name <n>] | aura mc [--name <n>] | aura walkforward [--name <n>] | aura runs families | aura runs family <id> [rank <metric>]";
fn main() {
// Collect argv and match the whole vector: every accepted form is exhaustive,
@@ -911,8 +880,6 @@ fn main() {
["walkforward", "--name", n] => run_walkforward(n),
["mc"] => run_mc("mc"),
["mc", "--name", n] => run_mc(n),
["runs", "list"] => runs_list(),
["runs", "rank", metric] => runs_rank(metric),
["runs", "families"] => runs_families(),
["runs", "family", id] => runs_family(id, None),
["runs", "family", id, "rank", metric] => runs_family(id, Some(metric)),
+10 -8
View File
@@ -429,8 +429,7 @@ fn runs_families_list_and_per_family_rank_across_invocations() {
.expect("spawn bogus");
assert_eq!(bogus.status.code(), Some(2), "bogus exit: {:?}", bogus.status);
// an unknown family id is an empty family: zero lines, exit 0 (consistent
// with `runs list` over an empty store).
// an unknown family id is an empty family: zero lines, exit 0.
let unknown = Command::new(BIN)
.args(["runs", "family", "nope-9"])
.current_dir(&cwd)
@@ -443,12 +442,15 @@ fn runs_families_list_and_per_family_rank_across_invocations() {
}
#[test]
fn runs_list_on_empty_registry_is_zero_lines_exit_zero() {
let cwd = temp_cwd("runs-empty");
let out = Command::new(BIN).args(["runs", "list"]).current_dir(&cwd).output().expect("spawn list");
assert!(out.status.success(), "list exit: {:?}", out.status);
assert_eq!(out.stdout.len(), 0, "expected no output, got: {:?}", out.stdout);
let _ = std::fs::remove_dir_all(&cwd);
fn runs_list_and_rank_are_retired_and_exit_two() {
// `aura runs list` / `runs rank <metric>` were retired (#73): families
// (sweep/mc/walkforward, C21) subsume standalone over-time comparison. The
// dropped subcommands now fall through to the strict-arg usage error (exit 2),
// before any registry access.
let list = Command::new(BIN).args(["runs", "list"]).output().expect("spawn list");
assert_eq!(list.status.code(), Some(2), "retired `runs list` must exit 2: {:?}", list.status);
let rank = Command::new(BIN).args(["runs", "rank", "total_pips"]).output().expect("spawn rank");
assert_eq!(rank.status.code(), Some(2), "retired `runs rank` must exit 2: {:?}", rank.status);
}
#[test]
+6 -4
View File
@@ -740,10 +740,12 @@ never a materialized-`Vec` scan at the call site. CLI surface: `aura mc`,
`walkforward` / `mc` persist via `append_family` with an optional `--name`.
Deferred (Non-goals): content-addressed identity + replay-dedup; the "run-diff"
depth and ranking families against each other (cross-family, vs. within-family);
and a live producer for the flat `runs.jsonl` standalone-run path — after this
cycle no CLI command writes it (sweep/walkforward moved to the family store, and
`aura run` does not persist), so `aura runs list` / `rank` await either a
persisting `aura run` or retirement (forward-queue follow-up).
and a live producer for the flat `runs.jsonl` standalone-run path — no CLI command
writes it (sweep/walkforward persist to the family store; `aura run` does not
persist). **Resolved (#73, 2026-06): retired** `aura runs list` / `rank` dropped;
families (C21) subsume standalone over-time comparison. The `aura-registry` flat
lib API (append/load/rank_by/optimize) is retained — `optimize` backs
walk-forward's in-sample step, `rank_by` backs `runs family … rank`.
### C19 — Bootstrap: blueprint → instance (recursive)
**Guarantee.** Construction is a distinct phase, recursive at every level. Each