chore(cli,docs): #220 cycle-close audit — doc reconcile + dispatch dedup

Cycle-close audit for the verb-axis-generalization cycle (a2294c7,
e7efe1f, b849bb7). Architect drift review: what holds — all four verbs
route through the one validate_and_register_axes seam into the campaign
executor; RGrid gone workspace-wide; zero too_many_arguments allows in
verb_sugar.rs; no r_sma_open embed at the verb dispatch sites (the
aura-graph default embed survives by design). Five drift items found,
all resolved fix-path in this commit:

- README (blueprint-verbs section + mc row), glossary (walk-forward
  entry), and ledger (verb-dissolution passage) reconciled to the
  shipped generic grammar; the ledger HISTORY note now separates the
  two supersessions correctly (#159 retired the built-in --strategy
  demo surface; #220 de-welded the verbs) instead of mis-attributing
  the weld removal to #159.
- Four stale characterization doc-comments in cli_run.rs reworded:
  the retired welded grammar is now named as the pinned grades'
  provenance, not the current spelling; one dangling reference to a
  deleted test repointed to the live anchor.
- The quadruplicated dispatch blocks extracted into two shared helpers
  beside validate_and_register_axes: exit_axis_register_error (the
  AxisRegisterError exit-map, byte-identical stderr + exit codes) and
  campaign_window_ms (from_choice -> full_window -> unix-ms clip);
  generalize's explicit unclipped (Some, Some) passthrough arm is
  untouched — only its fallback arm, which already ran the identical
  mechanics, calls the helper.

Regression gate green, no baseline moved (nothing to ratify):
cargo test --workspace exit 0, 62 test-result groups, 0 failures;
clippy --workspace --all-targets -D warnings clean; cargo doc
--workspace --no-deps 0 warnings. Spent cycle spec + plan working
files discarded per lifecycle.

refs #220
This commit is contained in:
2026-07-09 15:35:47 +02:00
parent b849bb76b9
commit 4acea45519
5 changed files with 97 additions and 108 deletions
+6 -4
View File
@@ -44,16 +44,18 @@ Invoke it as `aura <command> …` (examples below use the plain name).
## Running & orchestrating a loaded blueprint ## Running & orchestrating a loaded blueprint
These verbs all take the **blueprint file as their first positional argument** These verbs all take the **blueprint file as their first positional argument**
and drive a family of runs over it. (`walkforward`, `mc`, and `generalize` also and drive a family of runs over it. `walkforward`, `mc`, and `generalize` share
accept a legacy built-in form selected by `--strategy` instead of a `.json` `sweep`'s generic grammar — the blueprint positional plus `--real` and a
path, for the engine's own bundled example strategies — see `--help`.) repeatable `--axis <name>=<csv>` (`generalize` takes `--real <SYM1,SYM2,…>`, at
least two instruments, with a single value per axis) — see `--help`.
| Command | Purpose | | Command | Purpose |
|---|---| |---|---|
| `aura run <bp.json>` | Run one backtest of a **closed** blueprint and print its report. An open (free-knob) blueprint is refused with a clean error — bind it, or use `sweep`. | | `aura run <bp.json>` | Run one backtest of a **closed** blueprint and print its report. An open (free-knob) blueprint is refused with a clean error — bind it, or use `sweep`. |
| `aura sweep <bp.json> --list-axes` | **Discover** the blueprint's open, sweepable knobs. Prints each as `<name>:<kind>`. Run this first to learn the axis names. | | `aura sweep <bp.json> --list-axes` | **Discover** the blueprint's open, sweepable knobs. Prints each as `<name>:<kind>`. Run this first to learn the axis names. |
| `aura sweep <bp.json> --axis <name>=<csv> [--axis …]` | Run a **grid family** over the named axes and persist it. | | `aura sweep <bp.json> --axis <name>=<csv> [--axis …]` | Run a **grid family** over the named axes and persist it. |
| `aura mc <bp.json> --seeds <n>` | Run a **Monte-Carlo family** of `n` seeded realizations. Wants a **closed** blueprint (the inverse of sweep). | | `aura mc <bp.json> --seeds <n>` | Run a **synthetic Monte-Carlo family** of `n` seeded realizations. Wants a **closed** blueprint (the inverse of sweep). |
| `aura mc <bp.json> --real <sym> --axis <name>=<csv> [--axis …]` | Run a **Monte-Carlo R-bootstrap campaign** over recorded data — driven through the same generated-campaign pipeline as `sweep`; `--block-len`/`--resamples`/`--seed` tune the bootstrap. |
| `aura walkforward <bp.json> --axis <name>=<csv> [--axis …]` | Run an **in-sample-refit walk-forward family** — rolling optimize + out-of-sample test across windows, stitched into one verdict + parameter stability. `--select` chooses the per-window objective. | | `aura walkforward <bp.json> --axis <name>=<csv> [--axis …]` | Run an **in-sample-refit walk-forward family** — rolling optimize + out-of-sample test across windows, stitched into one verdict + parameter stability. `--select` chooses the per-window objective. |
| `aura runs families` | List every persisted family (id, kind, member count). | | `aura runs families` | List every persisted family (id, kind, member count). |
| `aura runs family <id> [rank <metric>]` | List one family's members, optionally ranked best-first by an R metric (e.g. `sqn_normalized`, `expectancy_r`). | | `aura runs family <id> [rank <metric>]` | List one family's members, optionally ranked best-first by an R metric (e.g. `sqn_normalized`, `expectancy_r`). |
+52 -81
View File
@@ -2509,6 +2509,44 @@ fn validate_and_register_axes(
Ok((canonical, raw_axes)) Ok((canonical, raw_axes))
} }
/// Exit-map for a `validate_and_register_axes` failure — the stderr line and
/// exit code every campaign dispatcher (sweep/generalize/walkforward/mc)
/// preserves: an unknown axis is a usage error (exit 2, echoed before the
/// archive is touched), a registry write failure a runtime error (exit 1).
fn exit_axis_register_error(e: AxisRegisterError) -> ! {
match e {
AxisRegisterError::UnknownAxis(m) => {
eprintln!("aura: {m}");
std::process::exit(2)
}
AxisRegisterError::Registry(m) => {
eprintln!("aura: {m}");
std::process::exit(1)
}
}
}
/// The shared campaign window in Unix-ms, clipped to the archive. `full_window`
/// probes the ARCHIVE's actual first/last bar in range (never the literal ms
/// request) and returns aura's native epoch-ns `Timestamp` (the ms->ns crossing
/// happens once, at the ingest seam — C3); the campaign document's `Window`
/// field is Unix-ms (same currency as `--from`/`--to` and every existing
/// campaign fixture, e.g. `campaign_doc_json` in research_docs.rs). Convert
/// back through the seam's own `aura_ingest::epoch_ns_to_unix_ms` at this one
/// seam-crossing (never reimplement the division inline) so the executor's
/// `unix_ms_to_epoch_ns` re-normalizes exactly once downstream, not twice.
/// Shared by the campaign dispatchers — sweep/walkforward/mc always clip;
/// generalize reaches here only through its no-explicit-window fallback (an
/// explicit `--from`+`--to` pair passes through unclipped there BY DESIGN).
fn campaign_window_ms(choice: DataChoice, env: &project::Env) -> (i64, i64) {
let source = DataSource::from_choice(choice, env);
let (from_ts, to_ts) = source.full_window(env);
(
aura_ingest::epoch_ns_to_unix_ms(from_ts),
aura_ingest::epoch_ns_to_unix_ms(to_ts),
)
}
/// `aura run`: the loaded-blueprint branch (an existing `.json` first-positional) or /// `aura run`: the loaded-blueprint branch (an existing `.json` first-positional) or
/// the built-in harness-kind dispatch. /// the built-in harness-kind dispatch.
fn dispatch_run(a: RunCmd, env: &project::Env) { fn dispatch_run(a: RunCmd, env: &project::Env) {
@@ -2638,33 +2676,17 @@ fn dispatch_generalize(a: GeneralizeCmd, env: &project::Env) {
std::process::exit(2); std::process::exit(2);
} }
} }
let (canonical, raw_axes) = let (canonical, raw_axes) = validate_and_register_axes(&doc, &axes, env)
validate_and_register_axes(&doc, &axes, env).unwrap_or_else(|e| match e { .unwrap_or_else(|e| exit_axis_register_error(e));
AxisRegisterError::UnknownAxis(m) => {
eprintln!("aura: {m}");
std::process::exit(2);
}
AxisRegisterError::Registry(m) => {
eprintln!("aura: {m}");
std::process::exit(1);
}
});
// Window: the explicit --from/--to when both present (byte-identical to the // Window: the explicit --from/--to when both present (byte-identical to the
// retired welded path, verified by the exact-grade anchor); otherwise the // retired welded path, verified by the exact-grade anchor); otherwise the
// first symbol's full archive window as the single shared campaign window. // first symbol's full archive window as the single shared campaign window.
let (from_ms, to_ms) = match (from, to) { let (from_ms, to_ms) = match (from, to) {
(Some(f), Some(t)) => (f, t), (Some(f), Some(t)) => (f, t),
_ => { _ => campaign_window_ms(
let source = DataSource::from_choice( DataChoice::Real { symbol: symbols[0].clone(), from_ms: from, to_ms: to },
DataChoice::Real { symbol: symbols[0].clone(), from_ms: from, to_ms: to }, env,
env, ),
);
let (from_ts, to_ts) = source.full_window(env);
(
aura_ingest::epoch_ns_to_unix_ms(from_ts),
aura_ingest::epoch_ns_to_unix_ms(to_ts),
)
}
}; };
let inv = verb_sugar::SugarInvocation { let inv = verb_sugar::SugarInvocation {
axes: &raw_axes, axes: &raw_axes,
@@ -2796,34 +2818,11 @@ fn dispatch_sweep(a: SweepCmd, env: &project::Env) {
// strip are single-sourced in `validate_and_register_axes` // strip are single-sourced in `validate_and_register_axes`
// (data-free, so this fires before the archive is touched). // (data-free, so this fires before the archive is touched).
let (canonical, raw_axes) = validate_and_register_axes(&doc, &axes, env) let (canonical, raw_axes) = validate_and_register_axes(&doc, &axes, env)
.unwrap_or_else(|e| match e { .unwrap_or_else(|e| exit_axis_register_error(e));
AxisRegisterError::UnknownAxis(m) => {
eprintln!("aura: {m}");
std::process::exit(2);
}
AxisRegisterError::Registry(m) => {
eprintln!("aura: {m}");
std::process::exit(1);
}
});
let symbol = symbol.clone(); let symbol = symbol.clone();
let source = DataSource::from_choice(data, env); // Archive-clipped Unix-ms window; the ns->ms seam-crossing
let (from_ts, to_ts) = source.full_window(env); // rationale lives on `campaign_window_ms`.
// `full_window` probes the ARCHIVE's actual first/last bar let (from_ms, to_ms) = campaign_window_ms(data, env);
// and returns aura's native epoch-ns `Timestamp` (the
// ms->ns crossing happens once, at the ingest seam — C3);
// the campaign document's `Window` field is Unix-ms (same
// currency as `--from`/`--to` and every existing campaign
// fixture, e.g. `campaign_doc_json` in research_docs.rs).
// Convert back through the seam's own
// `aura_ingest::epoch_ns_to_unix_ms` at this one
// seam-crossing (never reimplement the division inline) so
// the executor's `unix_ms_to_epoch_ns` re-normalizes
// exactly once downstream, not twice.
let (from_ms, to_ms) = (
aura_ingest::epoch_ns_to_unix_ms(from_ts),
aura_ingest::epoch_ns_to_unix_ms(to_ts),
);
let inv = verb_sugar::SugarInvocation { let inv = verb_sugar::SugarInvocation {
axes: &raw_axes, axes: &raw_axes,
name: name.clone(), name: name.clone(),
@@ -2900,16 +2899,7 @@ fn dispatch_walkforward(a: WalkforwardCmd, env: &project::Env) {
// strip are single-sourced in `validate_and_register_axes` // strip are single-sourced in `validate_and_register_axes`
// (data-free, so this fires before the archive is touched). // (data-free, so this fires before the archive is touched).
let (canonical, raw_axes) = validate_and_register_axes(&doc, &axes, env) let (canonical, raw_axes) = validate_and_register_axes(&doc, &axes, env)
.unwrap_or_else(|e| match e { .unwrap_or_else(|e| exit_axis_register_error(e));
AxisRegisterError::UnknownAxis(m) => {
eprintln!("aura: {m}");
std::process::exit(2);
}
AxisRegisterError::Registry(m) => {
eprintln!("aura: {m}");
std::process::exit(1);
}
});
// Unlike `dispatch_generalize` (a single run per instrument, insensitive // Unlike `dispatch_generalize` (a single run per instrument, insensitive
// to a day's edge shift), `blueprint_walkforward_family` sources its span // to a day's edge shift), `blueprint_walkforward_family` sources its span
// from `DataSource::wf_full_span`, which — for Real — ALWAYS clips // from `DataSource::wf_full_span`, which — for Real — ALWAYS clips
@@ -2918,15 +2908,10 @@ fn dispatch_walkforward(a: WalkforwardCmd, env: &project::Env) {
// calendar placement, so the roller must clip identically here too, even // calendar placement, so the roller must clip identically here too, even
// when both flags are given, or the per-window winners and OOS pips diverge // when both flags are given, or the per-window winners and OOS pips diverge
// from the committed exact-grade anchor. // from the committed exact-grade anchor.
let source = DataSource::from_choice( let (from_ms, to_ms) = campaign_window_ms(
DataChoice::Real { symbol: symbol.clone(), from_ms: from, to_ms: to }, DataChoice::Real { symbol: symbol.clone(), from_ms: from, to_ms: to },
env, env,
); );
let (from_ts, to_ts) = source.full_window(env);
let (from_ms, to_ms) = (
aura_ingest::epoch_ns_to_unix_ms(from_ts),
aura_ingest::epoch_ns_to_unix_ms(to_ts),
);
let (is_ms, oos_ms, step_ms) = wf_ms_sizes(); let (is_ms, oos_ms, step_ms) = wf_ms_sizes();
let inv = verb_sugar::SugarInvocation { let inv = verb_sugar::SugarInvocation {
axes: &raw_axes, axes: &raw_axes,
@@ -3017,31 +3002,17 @@ fn dispatch_mc(a: McCmd, env: &project::Env) {
// strip are single-sourced in `validate_and_register_axes` // strip are single-sourced in `validate_and_register_axes`
// (data-free, so this fires before the archive is touched). // (data-free, so this fires before the archive is touched).
let (canonical, raw_axes) = validate_and_register_axes(&doc, &axes, env) let (canonical, raw_axes) = validate_and_register_axes(&doc, &axes, env)
.unwrap_or_else(|e| match e { .unwrap_or_else(|e| exit_axis_register_error(e));
AxisRegisterError::UnknownAxis(m) => {
eprintln!("aura: {m}");
std::process::exit(2);
}
AxisRegisterError::Registry(m) => {
eprintln!("aura: {m}");
std::process::exit(1);
}
});
// `blueprint_walkforward_family` sources its span from // `blueprint_walkforward_family` sources its span from
// `DataSource::wf_full_span`, which for Real ALWAYS clips `--from`/`--to` // `DataSource::wf_full_span`, which for Real ALWAYS clips `--from`/`--to`
// to the archive's actual first/last bar in range: a holiday/weekend edge // to the archive's actual first/last bar in range: a holiday/weekend edge
// shifts every IS/OOS window's calendar placement, so the roller must clip // shifts every IS/OOS window's calendar placement, so the roller must clip
// identically here too, even when both flags are given, or the per-window // identically here too, even when both flags are given, or the per-window
// winners and pooled OOS series diverge from the committed exact-grade anchor. // winners and pooled OOS series diverge from the committed exact-grade anchor.
let source = DataSource::from_choice( let (from_ms, to_ms) = campaign_window_ms(
DataChoice::Real { symbol: symbol.clone(), from_ms: from, to_ms: to }, DataChoice::Real { symbol: symbol.clone(), from_ms: from, to_ms: to },
env, env,
); );
let (from_ts, to_ts) = source.full_window(env);
let (from_ms, to_ms) = (
aura_ingest::epoch_ns_to_unix_ms(from_ts),
aura_ingest::epoch_ns_to_unix_ms(to_ts),
);
let (is_ms, oos_ms, step_ms) = wf_ms_sizes(); let (is_ms, oos_ms, step_ms) = wf_ms_sizes();
let inv = verb_sugar::SugarInvocation { let inv = verb_sugar::SugarInvocation {
axes: &raw_axes, axes: &raw_axes,
+25 -16
View File
@@ -1036,9 +1036,11 @@ fn aura_sweep_real_blueprint_subset_axes_refuses_without_store_litter() {
/// The synthetic seed-resweep `mc` is undefined over real bars (one realization -> /// The synthetic seed-resweep `mc` is undefined over real bars (one realization ->
/// identical members), so a bare `aura mc --real EURUSD` — with no R candidate — is /// identical members), so a bare `aura mc --real EURUSD` — with no R candidate — is
/// REJECTED (usage on stderr, exit 2) at the binary boundary. The real path is /// REJECTED (usage on stderr, exit 2) at the binary boundary. The real path is
/// reachable ONLY via `--strategy r-sma` (the R-bootstrap over the pooled OOS R /// reachable ONLY via the blueprint positional + `--axis` campaign form (the
/// series, `mc_strategy_r_sma_prints_a_bootstrap_line_deterministically`); a /// R-bootstrap over the pooled OOS R series,
/// `--real` without it stays a usage error. NOT gated — the refusal precedes any /// `mc_r_bootstrap_real_e2e_pins_the_exact_current_grade`; spelled
/// `--strategy r-sma` before the #220 argv migration); a `--real` without a
/// blueprint stays a usage error. NOT gated — the refusal precedes any
/// data access, so it is CI-safe on every machine. /// data access, so it is CI-safe on every machine.
#[test] #[test]
fn mc_rejects_real_flag_with_usage_exit_2() { fn mc_rejects_real_flag_with_usage_exit_2() {
@@ -1770,14 +1772,16 @@ fn walkforward_dissolved_refuses_name_and_trace_together() {
assert!(stderr.contains("mutually exclusive"), "name/trace refusal: {stderr}"); assert!(stderr.contains("mutually exclusive"), "name/trace refusal: {stderr}");
} }
/// Property (#210 T3, dispatch split): a successful `aura walkforward --strategy /// Property (#210 T3, dispatch split): a successful real-data walkforward
/// r-sma --real` durably auto-registers exactly one generated process document, one /// (`aura walkforward <blueprint> --real --axis …`; spelled `--strategy r-sma
/// --real` before the #220 argv migration) durably auto-registers exactly one
/// generated process document, one
/// generated campaign document (carrying the `--name` handle and the stop as a /// generated campaign document (carrying the `--name` handle and the stop as a
/// non-empty single risk regime), and one campaign-run record — mirroring the /// non-empty single risk regime), and one campaign-run record — mirroring the
/// generalize dissolution's audit trail. The persisted family set is exactly one /// generalize dissolution's audit trail. The persisted family set is exactly one
/// `Sweep` family (the pipeline's leading `std::sweep(argmax)` stage) plus one /// `Sweep` family (the pipeline's leading `std::sweep(argmax)` stage) plus one
/// `WalkForward` family (the per-window OOS reports) — the observable proof that /// `WalkForward` family (the per-window OOS reports) — the observable proof that
/// `dispatch_walkforward`'s r-sma-real branch now runs through the one campaign /// `dispatch_walkforward`'s real-data path runs through the one campaign
/// executor rather than the deleted inline roller. Gated on the shared GER40 /// executor rather than the deleted inline roller. Gated on the shared GER40
/// archive; skips cleanly on a data refusal. /// archive; skips cleanly on a data refusal.
#[test] #[test]
@@ -3234,19 +3238,22 @@ fn exit_codes_partition_usage_two_from_runtime_one() {
} }
/// Characterization pin (byte-identity anchor for the mc R-bootstrap dissolution, /// Characterization pin (byte-identity anchor for the mc R-bootstrap dissolution,
/// #210, the last verb). The current `aura mc --strategy r-sma --real` runs the /// #210, the last verb; argv since migrated to the generic form by #220).
/// inline rolling walk-forward, pools the per-window OOS trade-R series, and /// `aura mc <blueprint> --real --axis …` pools the per-window OOS trade-R
/// r-bootstraps E[R] (`mc_r_bootstrap_report`). The dissolution reroutes this /// series and r-bootstraps E[R] through the campaign
/// through the campaign `[std::sweep(argmax), std::walk_forward, std::monte_carlo]` /// `[std::sweep(argmax), std::walk_forward, std::monte_carlo]`
/// process, whose terminal monte_carlo stage does the identical /// process, whose terminal monte_carlo stage does the
/// `StageBootstrap::PooledOos(r_bootstrap(...))` seeded from the campaign seed -- so /// `StageBootstrap::PooledOos(r_bootstrap(...))` seeded from the campaign seed -- so
/// the campaign seed must carry the mc `--seed`. The wf winners are /// the campaign seed carries the mc `--seed`. The wf winners are
/// deflation-seed-independent (argmax), so the pooled series, and thus the /// deflation-seed-independent (argmax), so the pooled series, and thus the
/// bootstrap, is stable across that seed remap (the walkforward anchor already /// bootstrap, is stable across that seed remap (the walkforward anchor already
/// proved winner seed-independence). The multi-point grid makes the per-window /// proved winner seed-independence). The multi-point grid makes the per-window
/// winner selection non-degenerate. This pins the EXACT current bootstrap grade of /// winner selection non-degenerate. The pinned bootstrap grade of this fixed 2025
/// a fixed 2025 GER40 invocation; after the dissolution the same command must /// GER40 invocation ORIGINATES from the pre-#210 inline rolling walk-forward
/// reproduce these bytes (the acceptance gate). Gated on the GER40 archive; skips /// (`mc_r_bootstrap_report`) under the retired welded `aura mc --strategy r-sma
/// --real` grammar, and survived both the campaign dissolution and the #220 argv
/// migration byte-identically (the acceptance gate then, the regression anchor
/// now). Gated on the GER40 archive; skips
/// cleanly on a data refusal. /// cleanly on a data refusal.
#[test] #[test]
fn mc_r_bootstrap_real_e2e_pins_the_exact_current_grade() { fn mc_r_bootstrap_real_e2e_pins_the_exact_current_grade() {
@@ -3350,7 +3357,9 @@ fn mc_dissolves_a_non_r_sma_blueprint() {
); );
} }
/// Property: `aura mc --strategy r-sma --real` is now thin sugar over the one campaign /// Property: real-data `aura mc <blueprint> --real --axis …` (the #210 dissolution
/// of the retired welded `--strategy r-sma` form, argv since migrated by #220) is
/// thin sugar over the one campaign
/// path — a successful run durably auto-registers exactly one generated process /// path — a successful run durably auto-registers exactly one generated process
/// document, one generated campaign document (carrying the constant "mc" name and the /// document, one generated campaign document (carrying the constant "mc" name and the
/// stop as a non-empty single risk regime), and one campaign-run record, and emits the /// stop as a non-empty single risk regime), and one campaign-run record, and emits the
+13 -6
View File
@@ -2374,12 +2374,19 @@ the project layout and docs-by-role are open (#192 context).
(`<bp.json> --real`), while `aura sweep --strategy r-sma --real` stays the inline built-in (`<bp.json> --real`), while `aura sweep --strategy r-sma --real` stays the inline built-in
path (its member lines carry no instrument/topology_hash/selection stamp and register no path (its member lines carry no instrument/topology_hash/selection stamp and register no
campaign document) — the built-in `--strategy` demo surface is #159's hard-wired-harness campaign document) — the built-in `--strategy` demo surface is #159's hard-wired-harness
retirement target, not the dissolution's; for generalize/walkforward/mc the dissolved form retirement target, not the dissolution's; for generalize/walkforward/mc the dissolved
is `--strategy r-sma --real`. [HISTORY — #159 (cuts 1b-4) has since landed form is now the same generic grammar as sweep — an arbitrary blueprint positional plus
(post-2026-07-07): the retirement target has been hit — the built-in `--real` and repeatable `--axis <wrapped-name>=<csv>` (#220). [HISTORY — superseded in
`--strategy` sweep/demo surface is retired and the inline `--strategy r-sma two steps. #159 (cuts 1b-4, post-2026-07-07) retired the built-in `--strategy`
--real` path no longer exists; the surviving form is `aura <verb> sweep/demo surface: the inline `aura sweep --strategy r-sma --real` path no longer
<blueprint.json> --axis …` over examples/r_*.json.] Milestone closed 2026-07-07 on a green end-to-end fieldtest exists. #220 then removed the verbs' weld — at milestone close the dissolved form of
generalize/walkforward/mc was still the welded `--strategy r-sma --real`; #220 made
the three verbs blueprint-generic (arbitrary blueprint + arbitrary `--axis`; mc keeps
the synthetic `--seeds` family unchanged beside the new `--real --axis` campaign mode;
generalize takes `--real <SYM1,SYM2,…>`, >=2, with one value per axis), deleted the
welded `--strategy` grammar (clap rejects it, exit 2), dissolved `RGrid`, and unified
the verbs on the #214 invocation struct. The surviving form everywhere is `aura <verb>
<blueprint.json> --real … --axis …` over examples/r_*.json.] Milestone closed 2026-07-07 on a green end-to-end fieldtest
(0 bugs; behaviour preservation, campaign-substrate reach-through, and the risk-regime axis (0 bugs; behaviour preservation, campaign-substrate reach-through, and the risk-regime axis
all confirmed); residual findings are discoverability/ergonomics follow-ups (#216 risk-axis all confirmed); residual findings are discoverability/ergonomics follow-ups (#216 risk-axis
discoverability, #217 verb knob asymmetry, #218 no-project store litter). discoverability, #217 verb knob asymmetry, #218 no-project store litter).
+1 -1
View File
@@ -301,7 +301,7 @@ The **optional** documented pre-trade-gate seam in the execution chain (`stop-ru
### walk-forward ### walk-forward
**Avoid:** — **Avoid:** —
An orchestration axis: rolling in-sample optimize + out-of-sample test across moving windows, stitched into one out-of-sample verdict plus parameter stability. As with `sweep`, every open knob named by `--list-axes` is **required** on a loaded blueprint — a subset is refused with the missing knob named; pin one with a single-value axis, there is no default. The `aura walkforward --strategy r-sma --real` CLI verb is now thin sugar over the `campaign document` path — translated to a generated campaign (`std::sweep → std::walk_forward`) run through the one executor (#210). An orchestration axis: rolling in-sample optimize + out-of-sample test across moving windows, stitched into one out-of-sample verdict plus parameter stability. As with `sweep`, every open knob named by `--list-axes` is **required** on a loaded blueprint — a subset is refused with the missing knob named; pin one with a single-value axis, there is no default. The `aura walkforward <blueprint.json> --real <sym> --axis <name>=<csv> …` CLI verb is thin sugar over the `campaign document` path — translated to a generated campaign (`std::sweep → std::walk_forward`) run through the one executor (#210; blueprint-generic over arbitrary blueprints and axes since #220).
### World ### World
**Avoid:** — **Avoid:** —