143c87c0aeca8a7ff6422a7479cb6137967bd296
13 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
94cdbf90cc |
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 |
||
|
|
e7efe1f765 |
feat(cli): walkforward + mc go blueprint-generic (#220 slice 2)
Tasks 3-4 of the verb-axis-generalization plan: - walkforward: the welded r-sma campaign branch moves out of the None arm into the blueprint mode, split on --real (campaign sugar) vs the unchanged synthetic family path; WalkforwardCmd drops --strategy/--fast/--slow; the full-window clip block, wf_ms_sizes and WINNER_SELECTION_METRIC move unchanged; the --select plateau #215 refusal survives. walkforward_summary_json_from_reports is de-welded: the axis-name list is passed in from the invocation instead of the const AXES r-sma quadruple. - mc: McCmd drops the welded flags and gains --axis; the blueprint arm splits on --real (campaign pipeline: requires --axis, refuses --seeds) vs the unchanged synthetic seed family (--seeds); block-len/ resamples/seed defaults and the --name/--trace refusal stay byte-identical. - translate_walkforward/run_walkforward_sugar and translate_mc/ run_mc_sugar adopt SugarInvocation (+ WfWindows/McKnobs); ZERO #[allow(clippy::too_many_arguments)] remain in verb_sugar.rs (#214 complete). - Tests: wf/mc e2e groups migrate argv-only (grade anchors keep all pinned floats verbatim); collateral usage/retired-token tests assert clap's structural rejection of the deleted flags; the synthetic family control groups pass unmodified. Necessary deviation from the plan's literal step text: re-added #[allow(clippy::type_complexity)] on walkforward_args_from (clippy -D warnings flags the 6-tuple; mirrors its siblings). Held quality nits (validate/strip block now quadruplicated across the four dispatch fns; inline exit-mapping recurrence) are deliberate plan-holds — dedup follows as the rule-of-three cleanup at cycle end. Verification: full workspace suite green in the loop's independent verify; build + clippy -D warnings clean. refs #220, refs #214 |
||
|
|
a2294c7c3a |
feat(cli): SugarInvocation + generalize goes blueprint-generic (#220 slice 1)
Tasks 1-2 of the verb-axis-generalization plan (spec auto-signed on a grounding-check PASS, decision log on #220): - verb_sugar gains the shared SugarInvocation<'_> carrier (#214) with VolStop and the factored doc_axes_from/risk_from/probe_params_from helpers; translate_sweep/run_sweep_sugar adopt it (no behaviour change — sweep argv and family output byte-identical), and translate_generalize/run_generalize_sugar follow, dropping their too_many_arguments allows (4 remain for wf/mc). - generalize is de-welded: GeneralizeCmd takes a blueprint positional plus repeatable --axis <wrapped-name>=<value> (one value per axis); --strategy/--fast/--slow are deleted; dispatch_generalize runs the sweep sequence (read, wrapped-namespace validation, canonicalize + topology_hash registration, wrapped_to_raw_axis strip) with a new usage closure. RGrid is deleted workspace-wide. - cli_run generalize tests migrate argv-only; the exact-grade anchor keeps all pinned floats verbatim; generalize_refuses_a_non_r_sma_ strategy is deleted (its premise — a strategy gate — dissolves by design); the multi-value refusal now fires per --axis. Verification: full workspace suite green in the loop's independent verify; build + clippy clean. Held quality finding (kept as a plan-hold): dispatch_generalize duplicates dispatch_sweep's validation/strip block verbatim — dedup follows once wf/mc land the same block (rule-of-three, end of this cycle). refs #220, refs #214 |
||
|
|
07a73fb66e |
docs(cli,ledger): sweep #159 demo-retirement drift — stale comments + ledger notes
Cycle-close audit tidy for the #159 demo-retirement arc: behaviour-free reword of prose/comments/docs left pointing at symbols the arc deleted. Adversarially verified by the audit workflow; cargo build --workspace --all-targets green. - README: drop the removed `--harness` token; restrict the legacy built-in form claim to walkforward/mc/generalize (run/sweep no longer resolve one). - glossary: the built-in `--strategy` sweep surface is retired (#159), not a live inline path. - INDEX.md ledger: append [HISTORY] supersession markers (house style) to the dated realization notes advertising the retired `--harness` / built-in `--strategy` CLI forms (cost-flag #153, cycles 0065/0066/0067, name-res, #210 status); historical text preserved. - aura-engine test-fixture docs: drop dead cross-crate refs to the deleted aura-cli fns sample_harness / build_sample and the deleted `macd` composite. - cli_run.rs: reword the #159-cut-2 pin doc to the current clap generic-usage reality (no strategy_from / Strategy::RBreakout); r_sma_sweep_family -> blueprint_sweep_family. - verb_sugar.rs: fix the pre-existing `E[R]` unresolved-doc-link warning. Remaining tidy — the main.rs comment cluster plus its rustdoc link at :1546 (the second cargo-doc warning) — held for a follow-up issue. The dead cost-graph branch is tracked in #221. refs #159 |
||
|
|
7f5f576f41 |
feat(cli): dissolve aura mc --strategy r-sma --real (#210)
The FOURTH and LAST verb dissolution. `aura mc --strategy r-sma --real` now runs as thin sugar over the one campaign executor: a generated, content-addressed [std::sweep(argmax), std::walk_forward, std::monte_carlo] campaign document whose terminal monte_carlo stage produces the same StageBootstrap::PooledOos(RBootstrap). The sugar reads it from run.outcome.record.cells[].stages[].bootstrap and reprints the existing mc_r_bootstrap_json — one line, byte-identical to the inline path. Pure sugar, NO executor change (aura-campaign/aura-engine/aura-research diff-free). Mirrors the shipped walkforward dissolution: - verb_sugar.rs: GeneratedMc, translate_mc, register_generated_mc, run_mc_sugar (record-read + one grade line, no member lines). - main.rs: mc_args_from + the dispatch_mc built-in-arm split gated on `--strategy r-sma && --real`. The one new load-bearing decision is the seed mapping: translate_mc sets campaign.seed = the mc --seed. The wf winners are argmax hence deflation-seed-independent (proven by the shipped walkforward anchor), so the pooled OOS series is unchanged while the terminal r_bootstrap at that seed reproduces the inline bootstrap. A new e2e (mc_dissolved_seed_changes_the_bootstrap_draw_not_the_pooled_series) pins exactly this: two --seed values yield identical n_trades but a moved E[R] mean. Fork C retention (as walkforward): the arm splits, run_mc_r_bootstrap / run_mc stay inline for the fenced synthetic/non-real paths (until #159). The three synthetic-r-sma tests use no --real, so they never enter the sugar branch and run the inline path unchanged. --name/--trace are rejected on the sugar path, matching the inline mc r-path. Verification: the committed byte-identity anchor mc_r_bootstrap_real_e2e_pins_the_exact_current_grade runs the real GER40 archive and passes byte-for-byte through the new path; full workspace suite green; clippy --all-targets -D warnings clean. refs #210 |
||
|
|
a047af65fb |
feat(cli): dissolve aura walkforward --strategy r-sma --real (#210 T3-T4)
The dispatch split lands: `dispatch_walkforward`'s built-in arm now routes
`--strategy r-sma --real` through the one campaign path (`run_walkforward_sugar`
-> generated campaign doc -> executor -> summary reprint), reproducing the
`{"walkforward":{…}}` line byte-for-byte. Everything else — synthetic (any
strategy) and non-r-sma real — stays on the inline `run_walkforward`, fenced
until #159 (Reading A). The committed anchor
`walkforward_real_e2e_pins_the_exact_current_grade` passes: the dissolved path
reproduces windows=9, stitched_total_pips, oos_r, and the param_stability means
exactly. `--select plateau:*` is refused (exit 2 -> #215, Fork B); a multi-value
stop is refused (Fork A).
Two plan guesses were wrong and the anchor + real-data e2e caught both; the fixes
are documented inline:
- Window resolution: the plan mirrored generalize's `(--from,--to) -> (f,t)`
shortcut, but the inline `wf_full_span` ALWAYS clips `--from`/`--to` to the
archive's actual first/last bar (`probe_window`), even when both flags are
explicit — a holiday/weekend edge shifts every IS/OOS window's calendar
placement (stitched_total_pips diverged by ~5.16M, param_stability means intact,
confirming only window boundaries moved). The dissolved path now calls
`source.full_window(env)` unconditionally, matching the inline behaviour;
verified byte-for-byte against the still-fenced inline path.
- Reducer axis match: the campaign path records the blueprint axes wrapped
(`sma_signal.fast.length`) while the stop rides unwrapped via the risk regime, so
the reducer's exact-name match panicked on real data. Fixed with the existing
`campaign_run::raw_matches_wrapped` (#203) wrap-convention helper.
Tests: the dissolution-proof e2e `walkforward_dissolves_through_the_campaign_path`
(one process + one campaign + one campaign-run auto-registered, windows=9) plus
front-end refusal coverage (multi-value stop, `--select plateau`, missing knobs,
empty `--real`, unknown `--select`, `--name`/`--trace` exclusivity). Full workspace
suite green; `clippy --all-targets -D warnings` clean.
refs #210
|
||
|
|
a72cd6b115 |
feat(cli): walkforward campaign translator + summary reducer (#210 T1-T2)
The additive machinery for the walkforward dissolution, unwired this slice (dispatch_walkforward untouched — `aura walkforward` behaviour byte-for-byte unchanged). Two pieces, both mirroring the generalize seam: - `translate_walkforward` + `register_generated_wf` + `GeneratedWalkforward` (verb_sugar.rs): argv -> a `[std::sweep(argmax), std::walk_forward]` process + a campaign carrying the multi-value fast/slow IS-refit grid over one instrument under a single Vol risk regime (the stop). The preceding sweep enumerates the survivor grid; the wf stage refits it per window. - `walkforward_summary_json_from_reports` (main.rs): the pure-sugar reducer that reconstructs the verb's summary line from the campaign's recorded per-window OOS reports — `stitched_total_pips` = the per-window total_pips summed in roll order, `param_stability` over the four r-sma axes via the same `MetricStats::from_values`, pooled `oos_r` via `r_metrics_from_rs`. Byte-identical to the inline `walkforward_summary_json` (the committed anchor gates it once Task 3 wires it). Both carry `#[allow(dead_code)]` until Task 3's dispatch split calls them. The third `Generated*`/`register_generated_*` trio duplicates the sweep/generalize shape by design (per-verb mirror); the bundle consolidation is deferred to #214. Unit test note: `r_metrics_from_rs` clears `trade_rs` (it is the terminal pooled reducer), so the reducer's characterization fixture rebuilds each window's `RMetrics` with `trade_rs` restored, modelling what a real OOS window's report carries (from `summarize_r`). refs #210 |
||
|
|
256ec7320c |
feat(cli): dissolve aura generalize into the campaign path (#210 T3)
`dispatch_generalize` synthesizes the bare `sma_signal` blueprint, resolves the
window (explicit --from/--to, else the first symbol's full window), and calls
the new `run_generalize_sugar`; the inline `run_generalize` is deleted. The
sugar builds the selection-bearing campaign doc, runs it through
`run_campaign_returning`, reprints the byte-identical `{"generalize":…}` +
`{"family_id":…}` lines, and persists the CrossInstrument family.
The exact-grade anchor stays green through the path shift — proving the two stop
mechanisms (former grid axis, now the risk-regime seam -> StopRule::Vol) yield
identical R. The family pin relaxes to "exactly one CrossInstrument family"
since the campaign executor now also persists a per-instrument Sweep family per
cell (the durable audit trail; #210 Q4). Two new e2e pin the window fallback and
the family member attribution order.
Deviations from the plan (necessary, verified): cross_instrument_members returns
Vec<aura_engine::RunReport> (aura_registry::RunReport is not pub); a shared
validate_before_register helper is factored out of run_sweep_sugar (DRY); the
run_generalize doc-comment and the now-unused `generalization` import removed.
refs #210
|
||
|
|
ed8b179e34 |
feat(cli): the generalize campaign translator (#210 T2)
`translate_generalize` builds the selection-bearing campaign document (`[std::sweep(argmax), std::generalize]`, a single risk regime carrying the stop, the candidate as single-value raw axes) + `GeneratedGeneralize` + `register_generated_g`, mirroring the sweep translator. A determinism unit test pins identical-invocation -> identical content id and the regime/pipeline shape. The new items are unused until the dispatch-rewire task (#210 T3) wires the runner; a bridge `#![allow(dead_code)]` (removed in T3) mirrors the sweep sibling's landing. refs #210 |
||
|
|
2f9f3571ae |
feat(research): RiskRegime vocabulary + CampaignDoc.risk field + validation (#210 T1-2)
The first slice of the risk-regime structural axis (spec/plan
|
||
|
|
c9d962f0ea |
fix(cli): sweep-entry ergonomics — honest refusals, no store litter (#210 c0110 fieldtest)
Three test-first fixes from the cycle-0110 fieldtest findings: - Axis-name preflight on the dissolved sweep arm: every --axis name is checked against the wrapped probe namespace (the --list-axes names) BEFORE the #203 strip; an unknown or raw-form name is refused house-style echoing exactly what the user typed, with a --list-axes pointer — no more stripped-then-mangled fragments from the deep referential refusal. Data-free, fires before the archive is touched. - Validate-before-register in the sweep sugar: intrinsic doc checks + executor preflight + the pure bind_axes coverage check (reused, not re-inlined; now pub(crate)) all run in-memory before register_generated — a refused invocation leaves no generated document in the content-addressed store and no campaign_runs.jsonl line (previously P2/P3 refusals littered runs/campaigns, one doc malformed). - blueprint_slot_prose: the sweep/walkforward/mc loaded-blueprint slots share one dispatch-boundary presenter — an op-script array gets a targeted 'run aura graph build first' hint, every load error goes through blueprint_load_prose + the missing-project hint, and the raw {e:?} Debug leak (#184's pattern, deliberately left on these three slots back then) is gone. Two stale Debug-leak pins flipped, two new refusal tests. Suite 1060/0, clippy -D warnings clean. refs #210 |
||
|
|
b7aaa0ba59 |
feat(cli): dissolve the real-data blueprint sweep into the campaign path (#210 T4)
aura sweep <bp.json> --real ... --axis ... now runs as thin sugar over its generated campaign document through the one campaign executor: the dispatch registers the canonical blueprint (topology_hash IS content_id_of over the same canonical bytes, so the strategy ref resolves against the existing single store write), strips the CLI's wrapped axis names to the raw campaign namespace (#203 bind convention), converts the probed archive window back through the ingest seam's own epoch_ns_to_unix_ms (made pub; C3 — the seam owns the ms/ns convention, never an inline division), and hands off to verb_sugar::run_sweep_sugar. The inline real-data arm is gone; the synthetic arm and --list-axes are untouched. campaign_run gains RunPresentation {Full, MemberLinesOnly}: run_campaign keeps Full (record line unchanged, project gate unchanged); the sugar path runs MemberLinesOnly — the generated doc's emit already limits stdout to family_table member lines, the mode suppresses only the final record line. The record append is identical in both modes; no project gate on the by-id path (the dissolved verb works project-less, exactly as before). The characterization pin survives the re-cut with exactly the one sanctioned assertion flip (instrument-absence -> instrument==GER40) and now also pins the generated artifacts: one process + one campaign document auto-registered (dedup on a second identical invocation), raw axis keys in the stored document, the stored window a ms sub-range of the request, C1 at the member-report level with the registry's run-suffix increment isolated. Suite 1055/0, clippy clean, gated real-data paths executed locally. refs #210 |
||
|
|
fd0b21d070 |
feat(cli): verb_sugar translator — argv to generated campaign documents (#210 T3)
The pure half of the sweep re-cut: translate_sweep maps a real-data blueprint-sweep invocation (axes, name, symbol, window, canonical blueprint bytes) to a selection-free single-stage process document plus a campaign document carrying exactly the invocation's intent (seed 0 — no stage consumes it — so identical invocations dedupe onto identical content ids); register_generated puts both into the content-addressed stores. Unit tests pin determinism, name flow, mixed-kind-axis refusal, and that generated documents pass intrinsic validation + preflight. Dead-code-allowed until the dispatch rewire wires the module (next task removes the bridge attribute). Plan bytes reconciled with three necessary deviations the spec gate surfaced: derive(Debug) for unwrap_err, ScalarKind lives in aura_core, and 'gen' is reserved in edition 2024. refs #210 |