# mc R-bootstrap dissolution — Design Spec **Date:** 2026-07-06 **Status:** Draft — awaiting user spec review **Authors:** orchestrator + Claude > Milestone #210, the FOURTH and LAST verb dissolution. Reference issue: Gitea > Brummel/Aura #210. Mirrors the just-shipped walkforward dissolution; the only new > load-bearing decision is the seed mapping (recorded as a #210 comment, > 2026-07-06). Scope reading: decision 2 / Reading A (Fork C retention). ## Goal Dissolve `aura mc --strategy r-sma --real`'s inline execution into thin sugar over the one campaign executor (`aura_campaign::execute`), completing the verb set (sweep, generalize, walkforward, mc). The `dispatch_mc` built-in arm gains an r-sma-real branch that runs through a generated, content-addressed campaign document with a `[std::sweep(argmax), std::walk_forward, std::monte_carlo]` process, reproducing the `{"mc_r_bootstrap":{…}}` line byte-for-byte. The committed anchor `mc_r_bootstrap_real_e2e_pins_the_exact_current_grade` (3fc491a) is the acceptance gate. `run_mc_r_bootstrap`, `run_mc`, and their helpers are NOT deleted (Fork C, as walkforward): the built-in arm splits, and everything the dissolution predicate does not match stays inline, fenced until #159 — the synthetic-r-sma path (`--strategy r-sma` without `--real`), the synthetic seed-resweep (`aura mc`), and the blueprint mc path. ## Architecture Pure sugar — NO executor change (`aura-campaign`/`aura-engine`/`aura-research` diff-free). The campaign monte_carlo stage already does the identical work: `crates/aura-campaign/src/exec.rs:400-446`, `StageBlock::MonteCarlo` after a `std::walk_forward` stage produces `StageBootstrap::PooledOos(r_bootstrap( &pooled_trade_rs(&fam.reports), resamples, block_len, seed))`, seeded from the campaign seed, landing in `StageRealization.bootstrap` (`crates/aura-registry/src/lineage.rs:130`). It is reachable in-memory at `run.outcome.record.cells[0].stages[].bootstrap`. So the sugar reads that `PooledOos(RBootstrap)` and renders the existing `mc_r_bootstrap_json(&RBootstrap)`. mc prints ONLY the one summary line (no per-window member lines, unlike walkforward). The inline mc R-bootstrap is `run_mc_r_bootstrap` → `mc_r_bootstrap_report` → `walkforward_family` → `pooled_oos_trade_rs` → `r_bootstrap` → `mc_r_bootstrap_json` (main.rs ~2106-2133). The dissolution reroutes the same rolling walk-forward + pooled-OOS bootstrap through the campaign path. **The seed mapping (the one new load-bearing detail).** The inline path uses TWO seeds: a fixed `DEFLATION_SEED` for the per-window wf winner selection, and the mc `--seed` for the terminal `r_bootstrap`. The campaign path has ONE `campaign.seed`, used for both. This reconciles because the wf winners are **argmax** — the deflation seed only annotates the recorded selection provenance, never changes the pick — so the winners, and the pooled OOS series, are **seed-independent**. The shipped walkforward anchor already proved this (the campaign path at `campaign.seed = 0` reproduced the inline `DEFLATION_SEED` winners byte-for-byte). So `translate_mc` sets `campaign.seed = the mc --seed`: the pooled series is unchanged (n_trades = 20681, matching the walkforward multi-grid anchor — mc pools the same series) and the `r_bootstrap` at that seed reproduces the inline bootstrap. `block_len` / `resamples` map to `StageBlock::MonteCarlo{resamples, block_len}`. Fork A (stop): a single `RiskRegime::Vol{stop_length, stop_k}` (multi-value stop refused); fast/slow are the multi-value IS-refit grid. ## Concrete code shapes ### The user-facing program (unchanged surface, dissolved execution) ```console $ aura mc --strategy r-sma --real GER40 \ --from 1735689600000 --to 1767225599000 \ --fast 3,5 --slow 12,20 --stop-length 14 --stop-k 2.0 \ --block-len 5 --resamples 1000 --seed 42 {"mc_r_bootstrap":{"block_len":5,"e_r":{"mean":-0.0025753095301594307,…}, "n_resamples":1000,"n_trades":20681,"prob_le_zero":0.558}} ``` Byte-identical to today's inline output (the acceptance gate). The audience — a researcher stress-testing a candidate's E[R] distribution — runs the identical command; the execution becomes a durable, content-addressed campaign document (C18, reproducible-from-manifest). It reintroduces no look-ahead or determinism failure: the same deterministic engine `r_bootstrap` over the same pooled OOS series (C1), no new streamed non-scalar (invariant 4). All mc-specific logic stays in the CLI translator/reader (invariant 10: no verb-special engine logic); the World runs it through the one campaign path (invariant 12). ### Sugar translator — mirror of `translate_walkforward` (verb_sugar.rs), + the monte_carlo stage ```rust // crates/aura-cli/src/verb_sugar.rs — new, mirror of translate_walkforward pub(crate) struct GeneratedMc { pub process: ProcessDoc, pub campaign: CampaignDoc } #[allow(clippy::too_many_arguments)] // #214: bundle deferred until after this last verb pub(crate) fn translate_mc( fast: &[i64], slow: &[i64], stop_length: i64, stop_k: f64, metric: &str, // "sqn_normalized" (the wf objective) in_sample_ms: u64, out_of_sample_ms: u64, step_ms: u64, resamples: u32, block_len: u32, seed: u64, // seed = the mc --seed name: &str, symbol: &str, from_ms: i64, to_ms: i64, blueprint_canonical: &str, ) -> Result { let process = ProcessDoc { format_version: FORMAT_VERSION, kind: DocKind::Process, name: "mc".to_string(), description: None, pipeline: vec![ StageBlock::Sweep { selection: Some(SweepSelection { metric: metric.to_string(), select: SelectRule::Argmax, deflate: false }) }, StageBlock::WalkForward { in_sample_ms, out_of_sample_ms, step_ms, mode: WfMode::Rolling, metric: metric.to_string(), select: SelectRule::Argmax }, StageBlock::MonteCarlo { resamples, block_len }, ], }; // …doc_axes for fast.length / slow.length exactly as translate_walkforward… let campaign = CampaignDoc { // …instruments: [symbol], windows: [{from_ms,to_ms}], one StrategyEntry with the axes… risk: vec![RiskRegime::Vol { length: stop_length, k: stop_k }], seed, // <-- the mc --seed (the wf winners are seed-independent) // …presentation: persist_taps [], emit ["family_table"]… // (process/data/strategies fields identical in shape to translate_walkforward) }; Ok(GeneratedMc { process, campaign }) } ``` `register_generated_mc` is the verbatim `register_generated_wf` shape (put_process + put_campaign). ### Sugar runner — read the bootstrap from the record, print one line ```rust // crates/aura-cli/src/verb_sugar.rs — new, mirror of run_walkforward_sugar #[allow(clippy::too_many_arguments)] pub(crate) fn run_mc_sugar(/* translate_mc args + env */) -> Result<(), String> { let generated = translate_mc(/* … */)?; let probe_params = vec![ ("fast.length".to_string(), Scalar::i64(fast[0])), ("slow.length".to_string(), Scalar::i64(slow[0])), ]; validate_before_register(&generated.process, &generated.campaign, blueprint_canonical, &probe_params, env)?; let reg = env.registry(); let (_p, campaign_id) = register_generated_mc(®, &generated)?; let run = crate::campaign_run::run_campaign_returning(&campaign_id, env)?; // The single cell's monte_carlo StageRealization carries the pooled-OOS bootstrap. let boot = run.outcome.record.cells.iter() .flat_map(|c| c.stages.iter()) .find_map(|s| match &s.bootstrap { Some(aura_registry::StageBootstrap::PooledOos(b)) => Some(b), _ => None, }) .ok_or("mc produced no pooled-OOS bootstrap")?; println!("{}", crate::mc_r_bootstrap_json(boot)); Ok(()) } ``` ### Dispatch split — mirror of the walkforward split; `run_mc_r_bootstrap`/`run_mc` STAY ```rust // crates/aura-cli/src/main.rs — dispatch_mc, the None (built-in) arm None => { // …existing usage / blueprint+seeds guards, unchanged… if a.strategy.as_deref() == Some("r-sma") && a.real.is_some() { // r-sma --real: the dissolved sugar path. let (name, symbol, fast, slow, stop_length, stop_k, block_len, resamples, seed, from, to) = mc_args_from(&a) /* unwrap_or_else: eprintln + exit 2 */; let signal = sma_signal(None, None); let canonical = blueprint_to_json(&signal).expect("a bare sma_signal serializes"); let reg = env.registry(); reg.put_blueprint(&topology_hash(&signal), &canonical) /* exit 1 on err */; let (from_ms, to_ms) = resolve_window(&symbol, from, to, env); // full_window unconditionally let (is_ms, oos_ms, step_ms) = wf_ms_sizes(); verb_sugar::run_mc_sugar(&fast, &slow, stop_length, stop_k, "sqn_normalized", is_ms, oos_ms, step_ms, resamples, block_len, seed, &name, &symbol, from_ms, to_ms, &canonical, env) /* unwrap_or_else: eprintln + exit 1 */; return; } // …EXISTING fenced body, verbatim: the r_path / McArgs::RealR / McArgs::Synthetic // logic → run_mc_r_bootstrap / run_mc… } ``` `mc_args_from` mirrors `walkforward_args_from` plus `block_len` (default 1), `resamples` (default 1000), `seed` (default 1) — single-value stop (multi-value refused, Fork A), fast/slow CSV grids, `--real` required. `run_mc_r_bootstrap`, `run_mc`, `mc_r_bootstrap_report`, `mc_r_bootstrap_json`, `pooled_oos_trade_rs` are all RETAINED (the fenced paths + `mc_r_bootstrap_json` is reused by the sugar). ## Components - **`crates/aura-cli/src/verb_sugar.rs`** — `GeneratedMc`, `translate_mc`, `register_generated_mc`, `run_mc_sugar`. Reuses `axis_from_values`, `validate_before_register`, `run_campaign_returning`. - **`crates/aura-cli/src/main.rs`** — `mc_args_from`, the `dispatch_mc` built-in-arm split (add the r-sma-real sugar branch; retain the existing body). Reuses `wf_ms_sizes`, `resolve_window` (the walkforward helper if it was extracted, else the same inline match), `sma_signal`, `mc_r_bootstrap_json`. - **No executor change.** ## Data flow argv → `mc_args_from` → `dispatch_mc` synthesizes + stores the `sma_signal` blueprint, resolves the window, ms roller sizes → `run_mc_sugar` → `translate_mc` builds `[sweep(argmax), walk_forward, monte_carlo]` + a campaign (fast/slow axes, one instrument, one Vol regime, seed = mc_seed) → `validate_before_register` → `register_generated_mc` → `run_campaign_returning` (sweep → wf refit per window → monte_carlo pools the OOS R + bootstraps at the campaign seed) → the sugar reads `StageBootstrap::PooledOos(RBootstrap)` from the record's stages and prints `mc_r_bootstrap_json`. ## Error handling - Non-`r-sma`, missing knobs, empty `--real`: the fenced arm's / `mc_args_from`'s refusals (exit 2), reusing message strings where shared. - Multi-value `--stop-length`/`--stop-k`: exit 2 (Fork A), same message shape as walkforward. - Referential faults: `validate_before_register` refuses before any store write. - Data refusal (no archive): the member runner's `no_real_data` path (exit 1). ## Testing strategy - **The committed anchor** `mc_r_bootstrap_real_e2e_pins_the_exact_current_grade` (3fc491a) is the equivalence gate: it must stay green through the dissolution (block_len, n_resamples, n_trades, e_r.mean, e_r.p50, prob_le_zero all exact). - **`translate_mc` unit tests** (mirror the walkforward translator tests): deterministic content ids; the pipeline is `[sweep(argmax), walk_forward, monte_carlo]` with the right roller/mc fields + seed; the regime carries the stop; diverging seeds/stops do not collide content ids; `register_generated_mc` resolves. - **Dissolution-proof e2e** (mirror `walkforward_dissolves_through_the_campaign_path`): a successful real run auto-registers one process + one campaign + one campaign-run, and the output is the single `mc_r_bootstrap` line. - **Front-end refusal tests**: multi-value stop (exit 2), non-r-sma real handled by the fenced path (still works). ## Acceptance criteria 1. `aura mc --strategy r-sma --real …` runs through the campaign path; the anchor stays green (the `mc_r_bootstrap` line byte-identical). 2. `--strategy r-sma --real` no longer flows through `run_mc_r_bootstrap`; it runs the sugar path. `run_mc_r_bootstrap` / `run_mc` are RETAINED for the fenced branches. 3. The fenced branches are behaviour-unchanged (Reading A): synthetic-r-sma (`--strategy r-sma` no `--real`), the synthetic seed-resweep (`aura mc`), and the blueprint mc path all run inline exactly as before. 4. Multi-value `--stop-length`/`--stop-k` is refused (Fork A). 5. No executor change (`aura-campaign`/`aura-engine`/`aura-research` diff-free). 6. Full workspace suite green; `clippy --all-targets -D warnings` clean.