feat(campaign,cli): permit --select plateau in the walk_forward stage (#215)

The campaign walk_forward stage picked its per-window in-sample winner by
optimize_deflated (argmax) unconditionally, dropping the stage's `select`
field, and both the preflight and the `walkforward` verb's `--real` campaign
sub-branch refused plateau:* with a forward-pointer here (the #210 decision-4
deferral; the synthetic non-`--real` sub-branch has accepted plateau since
#173). Honour plateau selection when no gate precedes the wf stage.

- Preflight: the plateau-in-wf refusal is narrowed to gate-preceded stages.
  With no preceding gate the survivors are the full sweep grid, so the
  parameter lattice (grid.axis_lens) is exact and optimize_plateau is sound; a
  gate filters survivors below the grid, breaking the lattice, so plateau after
  any gate stays refused. The refusal is static — document validity must be
  data-independent (a gate's filtering depends on the data) — and keeps the
  existing "a gated survivor subset has no parameter lattice" message, which now
  describes exactly the only case it fires.
- Executor: run_walk_forward_stage threads the per-cell grid.axis_lens and picks
  each window's in-sample winner via select_sweep_winner — the same dispatch the
  sweep stage uses. (Argmax, deflate=true) is byte-identical to the old
  optimize_deflated call, so the argmax grade and content-id pins are unchanged;
  the plateau arms read axis_lens and ignore deflate.
- CLI: the walkforward verb threads --select through translate_walkforward /
  run_walkforward_sugar (a walkforward-local param, not the shared four-verb
  SugarInvocation), mapping the parsed Selection to the research SelectRule via
  select_rule_of; the refusal and its select_is_plateau helper are deleted.
  Scope is walkforward-only — the mc verb's wf-stage select is untouched.

The plateau:worst grade anchor (stitched_total_pips = -9683776.67) is distinct
from the argmax anchor (-10398606.67), proving the plateau path genuinely
changes winner selection rather than falling back to argmax. A gate-free plateau
process validates clean while a gate-preceded one is refused (a new
campaign-validate e2e). Argmax defaults stay byte-identical.

closes #215
This commit is contained in:
2026-07-10 02:59:33 +02:00
parent 35b996e3bf
commit 94cdbf90cc
6 changed files with 335 additions and 67 deletions
+21 -17
View File
@@ -686,6 +686,16 @@ fn parse_select(s: &str) -> Result<Selection, ()> {
}
}
/// Map the CLI `--select` value to the research selection rule the campaign
/// document carries.
fn select_rule_of(sel: Selection) -> aura_research::SelectRule {
match sel {
Selection::Argmax => aura_research::SelectRule::Argmax,
Selection::Plateau(PlateauMode::Mean) => aura_research::SelectRule::PlateauMean,
Selection::Plateau(PlateauMode::Worst) => aura_research::SelectRule::PlateauWorst,
}
}
/// Parse a comma-separated list of `T` (each item parsed via `FromStr`), rejecting
/// any item that fails to parse — the shared validator for the r-sma grid flags
/// (#137). The caller folds the `Err` into the subcommand `usage()` so a malformed
@@ -2300,13 +2310,6 @@ fn wf_ms_sizes() -> (u64, u64, u64) {
)
}
/// True iff `--select` names a plateau mode (refused on the dissolved path, Fork B,
/// until #215 ships the wf-stage plateau relaxation). Reuses `parse_select` so the
/// vocabulary stays single-sourced.
fn select_is_plateau(select: Option<&str>) -> bool {
matches!(select.map(parse_select), Some(Ok(Selection::Plateau(_))))
}
/// Resolve a single optional stop knob (`--stop-length`/`--stop-k`): an absent flag
/// defaults to `default`; a present one parses via [`parse_csv_list`] and refuses
/// unless it holds exactly one value (the stop is a risk regime, not a swept axis).
@@ -2938,16 +2941,16 @@ fn dispatch_walkforward(a: WalkforwardCmd, env: &project::Env) {
// The campaign path (#220): the real-archive execution routes
// through the one campaign executor, over the user's own
// blueprint and axes (formerly the welded r-sma branch).
if let Some(s) = a.select.as_deref()
&& parse_select(s).is_err()
{
eprintln!("aura: {}", usage());
std::process::exit(2);
}
if select_is_plateau(a.select.as_deref()) {
eprintln!("aura: --select plateau is not yet available on the campaign path; see #215");
std::process::exit(2);
}
let select = match a.select.as_deref() {
Some(s) => match parse_select(s) {
Ok(sel) => select_rule_of(sel),
Err(()) => {
eprintln!("aura: {}", usage());
std::process::exit(2);
}
},
None => aura_research::SelectRule::Argmax,
};
let (name, symbol, stop_length, stop_k, from, to) =
walkforward_args_from(&a).unwrap_or_else(|m| {
eprintln!("aura: {m}");
@@ -2988,6 +2991,7 @@ fn dispatch_walkforward(a: WalkforwardCmd, env: &project::Env) {
out_of_sample_ms: oos_ms,
step_ms,
},
select,
env,
)
.unwrap_or_else(|m| {