Files
Aura/docs/plans/0095-mc-over-a-loaded-blueprint.md
T
Brummel 860b89ad93 plan: 0095 monte-carlo over a loaded blueprint
4 tasks: parse_mc_blueprint_args, blueprint_mc_family (closed-blueprint
guard + seed-driven synthetic walks), the reproduce MonteCarlo realization
branch, and run_blueprint_mc + the .json dispatch arm + E2E. All in
aura-cli/main.rs (engine/registry untouched).
2026-07-01 12:07:52 +02:00

24 KiB

Monte-Carlo over a Loaded Blueprint — Implementation Plan

Parent spec: docs/specs/0095-mc-over-a-loaded-blueprint.md

For agentic workers: REQUIRED SUB-SKILL: use the implement skill to run this plan. Steps use - [ ] checkboxes for tracking.

Goal: Add aura mc <blueprint.json> --seeds N — a Monte-Carlo family from a loaded CLOSED blueprint, each seed drawing a distinct synthetic walk — persisted as FamilyKind::MonteCarlo with the content-addressed store hook, aura reproduce bit-identical.

Architecture: All code lands in crates/aura-cli/src/main.rs (engine + registry untouched — invariants 8/9). A new .json-discriminator on the ["mc", ..] dispatch arm (mirroring the 0093 sweep arm) routes to blueprint_mc_family (builds an McFamily by running the fixed closed blueprint over N seed-driven synthetic walks through the shared run_blueprint_member path) → run_blueprint_mc (the persist seam: put_blueprint + append_family(MonteCarlo) + print). reproduce_family_in gains a MonteCarlo realization branch that reconstructs each member's seed-driven walk.

Tech Stack: Rust; SyntheticSpec::source(seed) (aura-engine), McFamily/McDraw/ McAggregate (aura-engine), run_blueprint_member (unchanged), Registry::put_blueprint

  • append_family (aura-registry), topology_hash/content_id (local).

Files this plan creates or modifies:

  • Modify: crates/aura-cli/src/main.rs — new parse_mc_blueprint_args + McBlueprintArgs (beside parse_sweep_blueprint_args ~3797-3835); new synthetic_walk_sources + blueprint_mc_family + run_blueprint_mc (beside blueprint_sweep_family/run_blueprint_sweep ~3154-3246); the MonteCarlo branch in reproduce_family_in (~2503-2574, the run at ~2561); the ["mc", rest @ ..] dispatch arm (~4006-4015).
  • Test: crates/aura-cli/src/main.rs — unit tests beside the sweep units (~5144-5230): parse_mc_blueprint_args_grammar, blueprint_mc_family_seeds_differ, reproduce_family_re_derives_every_mc_member_bit_identically.
  • Test: crates/aura-cli/tests/cli_run.rs — E2E beside the sweep/reproduce E2Es (~3296-3489): aura_mc_over_a_blueprint_reproduces_bit_identically, aura_mc_rejects_an_open_blueprint.

Facts pinned by plan-recon (line refs are current; re-anchor by symbol if drifted):

  • run_blueprint_member(signal: Composite, point: &[Cell], space: &[ParamSpec], sources: Vec<Box<dyn aura_engine::Source>>, window: (Timestamp,Timestamp), seed: u64, pip: f64, topo: &str) -> RunReportunchanged; stamps manifest.seed/manifest.topology_hash.
  • McDraw { pub seed: u64, pub report: RunReport }, McFamily { pub draws, pub aggregate }, McAggregate::from_draws(&[McDraw]) -> McAggregate (all pub; from_draws needs draws non-empty).
  • SyntheticSpec { start: f64, len: usize, step: i64 }; .source(seed) -> impl Source; window_of(&[Box<dyn Source>]) -> Option<(Timestamp,Timestamp)>.
  • mc_member_line(id, seed, report) -> String; mc_aggregate_json(&McAggregate) -> String; mc_member_reports(&McFamily) -> Vec<RunReport>.
  • Family { pub id, pub kind: FamilyKind, pub members: Vec<FamilyRunRecord> }; FamilyKind::MonteCarlo is a variant. stage1_signal(fast: Option<i64>, slow: Option<i64>) -> Composite (Some binds → closed). STAGE1_R_STOP_LENGTH = 3, fixture SMA slow=4 → the graph warms in ~4 bars, so a len-60 walk yields many trades that differ across seeds.
  • All needed imports (Cell, ParamSpec, Composite, McFamily, McDraw, McAggregate, SyntheticSpec, window_of, blueprint_from_json, blueprint_to_json, FamilyKind, mc_member_reports, std_vocabulary) are already in main.rs — no use edits.

Task 1: parse_mc_blueprint_args + McBlueprintArgs

Files:

  • Modify: crates/aura-cli/src/main.rs (beside parse_sweep_blueprint_args ~3797-3835)

  • Test: crates/aura-cli/src/main.rs (beside the sweep-arg unit parse_sweep_blueprint_args_grammar ~5107)

  • Step 1: Write the failing test

Add to the #[cfg(test)] mod tests block, beside parse_sweep_blueprint_args_grammar:

#[test]
fn parse_mc_blueprint_args_grammar() {
    // --seeds required and parsed; default name "mc"; seeds >= 1; unknown/real flags rejected.
    let ok = parse_mc_blueprint_args(&["--seeds", "8", "--name", "mc1"]).expect("valid");
    assert_eq!(ok.n_seeds, 8);
    assert_eq!(ok.name, "mc1");
    let def = parse_mc_blueprint_args(&["--seeds", "3"]).expect("default name");
    assert_eq!(def.name, "mc");
    assert!(parse_mc_blueprint_args(&[]).is_err(), "--seeds is required");
    assert!(parse_mc_blueprint_args(&["--seeds", "0"]).is_err(), "seeds must be >= 1");
    assert!(parse_mc_blueprint_args(&["--seeds", "x"]).is_err(), "seeds must be a number");
    assert!(
        parse_mc_blueprint_args(&["--seeds", "4", "--real", "EURUSD"]).is_err(),
        "synthetic-only this cycle: --real is rejected (real-data MC is #172)"
    );
}
  • Step 2: Run test to verify it fails

Run: cargo test -p aura-cli --bin aura parse_mc_blueprint_args_grammar Expected: FAIL — compile error cannot find function parse_mc_blueprint_args (the fn/struct do not exist yet).

  • Step 3: Write minimal implementation

Add beside parse_sweep_blueprint_args (~3797):

/// Parsed tail of `aura mc <blueprint.json> …`: the seed count + the family name.
/// Synthetic-only this cycle (real-data MC rides #172), so `--real`/`--from`/`--to`
/// are rejected as unknown flags. Mirrors `parse_sweep_blueprint_args`' shape.
struct McBlueprintArgs {
    n_seeds: u64,
    name: String,
}

fn parse_mc_blueprint_args(rest: &[&str]) -> Result<McBlueprintArgs, String> {
    let usage = || "usage: mc <blueprint.json> --seeds <n> [--name <n>]".to_string();
    let mut n_seeds: Option<u64> = None;
    let mut name: Option<String> = None;
    let mut tail = rest;
    while let Some((flag, t)) = tail.split_first() {
        let (value, t) = t.split_first().ok_or_else(usage)?;
        match *flag {
            "--seeds" => {
                let n: u64 = value.parse().map_err(|_| usage())?;
                if n == 0 {
                    return Err(usage());
                }
                n_seeds = Some(n);
            }
            "--name" if name.is_none() => name = Some((*value).to_string()),
            _ => return Err(usage()),
        }
        tail = t;
    }
    let n_seeds = n_seeds.ok_or_else(usage)?; // --seeds is required
    Ok(McBlueprintArgs { n_seeds, name: name.unwrap_or_else(|| "mc".to_string()) })
}
  • Step 4: Run test to verify it passes

Run: cargo test -p aura-cli --bin aura parse_mc_blueprint_args_grammar Expected: PASS.


Task 2: synthetic_walk_sources + blueprint_mc_family (closed-blueprint guard)

Files:

  • Modify: crates/aura-cli/src/main.rs (beside blueprint_sweep_family ~3154; mc_family pattern ~2210)

  • Test: crates/aura-cli/src/main.rs (beside blueprint_sweep_member_equals_single_run_and_shares_topology_hash ~5151)

  • Step 1: Write the failing test

#[test]
fn blueprint_mc_family_seeds_differ() {
    // MC over a CLOSED signal (both SMA knobs bound): 3 seeds -> 3 draws, one shared
    // topology_hash, and DIFFERING metrics — the seed reaches the DATA (a distinct
    // synthetic walk per draw), not just the manifest label. The anti-degenerate guard:
    // a regression to seed-as-label-only would make the three draws identical.
    let closed = stage1_signal(Some(2), Some(4));
    let doc = blueprint_to_json(&closed).expect("serializes");
    let family = blueprint_mc_family(&doc, 3, &DataSource::Synthetic);

    assert_eq!(family.draws.len(), 3, "one draw per seed");
    assert_eq!(family.draws.iter().map(|d| d.seed).collect::<Vec<_>>(), vec![1, 2, 3]);
    let topo = family.draws[0].report.manifest.topology_hash.clone();
    assert!(topo.is_some(), "members carry a topology_hash");
    assert!(
        family.draws.iter().all(|d| d.report.manifest.topology_hash == topo),
        "all members share one topology_hash"
    );
    let m: Vec<_> = family.draws.iter().map(|d| &d.report.metrics).collect();
    assert!(m[0] != m[1] || m[1] != m[2], "seeds must yield differing realizations");
}
  • Step 2: Run test to verify it fails

Run: cargo test -p aura-cli --bin aura blueprint_mc_family_seeds_differ Expected: FAIL — compile error cannot find function blueprint_mc_family (fn does not exist yet).

  • Step 3: Write minimal implementation

Add beside blueprint_sweep_family (~3154):

/// A fresh seeded synthetic price walk for one Monte-Carlo draw — the `mc_family`
/// pattern (a distinct realization per seed). A FIXED `SyntheticSpec` shared by the
/// `aura mc <blueprint.json>` persist path AND the reproduce MonteCarlo branch, so the
/// seed->walk reconstruction is bit-exact (C1). Length 60 comfortably warms the loaded
/// stage1-r graph (SMA slow=4 + the len-3 vol stop) so draws produce differing trades.
fn synthetic_walk_sources(seed: u64) -> Vec<Box<dyn aura_engine::Source>> {
    let spec = SyntheticSpec { start: 1.0, len: 60, step: 1 };
    vec![Box::new(spec.source(seed))]
}

/// Build a Monte-Carlo family from a loaded CLOSED signal blueprint: run the fixed
/// blueprint across `n_seeds` seeds, each seed drawing a distinct synthetic walk. The
/// blueprint must be CLOSED (empty wrapped `param_space`) — MC binds no axis, so a free
/// knob has no binder; an OPEN blueprint is rejected with a named error (exit 2) before
/// any run, pre-empting the `compile_with_params` arity panic. Each draw runs the shared
/// reduce-mode member path (`run_blueprint_member`, the same fn reproduce re-runs), so
/// reproduction is bit-identical (C1); every member carries the shared `topology_hash`.
fn blueprint_mc_family(doc: &str, n_seeds: u64, data: &DataSource) -> McFamily {
    let reload = |d: &str| {
        blueprint_from_json(d, &|t| std_vocabulary(t))
            .expect("doc parse-validated at the dispatch boundary; reload is infallible")
    };
    let topo = topology_hash(&reload(doc));
    let pip = data.pip_size();
    // probe the wrapped param_space (as blueprint_sweep_family does); MC needs it empty.
    let (tx_eq, _) = mpsc::channel();
    let (tx_ex, _) = mpsc::channel();
    let (tx_r, _) = mpsc::channel();
    let (tx_req, _) = mpsc::channel();
    let space =
        wrap_stage1r(reload(doc), tx_eq, tx_ex, tx_r, tx_req, false, true, None).param_space();
    if !space.is_empty() {
        eprintln!(
            "aura: mc requires a closed blueprint (no free parameters); {} free knob(s) — \
             bind them or use `aura sweep --axis`",
            space.len()
        );
        std::process::exit(2);
    }
    let point: Vec<Cell> = Vec::new(); // closed -> empty point (as `aura run <blueprint.json>`)
    let draws: Vec<McDraw> = (1..=n_seeds)
        .map(|seed| {
            let sources = synthetic_walk_sources(seed);
            let window = window_of(&sources).expect("non-empty synthetic walk");
            let report =
                run_blueprint_member(reload(doc), &point, &space, sources, window, seed, pip, &topo);
            McDraw { seed, report }
        })
        .collect();
    let aggregate = McAggregate::from_draws(&draws);
    McFamily { draws, aggregate }
}
  • Step 4: Run test to verify it passes

Run: cargo test -p aura-cli --bin aura blueprint_mc_family_seeds_differ Expected: PASS. (If the three draws come back identical, the walk did not warm the stop — increase SyntheticSpec.len; 60 should suffice for slow=4 + stop-len 3.)


Task 3: reproduce_family_in MonteCarlo realization branch

Files:

  • Modify: crates/aura-cli/src/main.rs (reproduce_family_in ~2503-2574; the run at ~2561-2570)

  • Test: crates/aura-cli/src/main.rs (beside reproduce_family_re_derives_every_member_bit_identically ~5194)

  • Step 1: Write the failing test

#[test]
fn reproduce_family_re_derives_every_mc_member_bit_identically() {
    let dir = std::env::temp_dir().join(format!("aura-repro-mc-{}", std::process::id()));
    let _ = std::fs::remove_dir_all(&dir);
    std::fs::create_dir_all(&dir).expect("temp dir");
    let reg = Registry::open(dir.join("runs.jsonl"));

    // a CLOSED signal (both SMA knobs bound) — MC binds no axis.
    let closed = stage1_signal(Some(2), Some(4));
    let doc = blueprint_to_json(&closed).expect("serializes");
    let data = DataSource::Synthetic;
    let family = blueprint_mc_family(&doc, 3, &data);

    // persist exactly as run_blueprint_mc does: store the blueprint, append the MC family.
    let topo = family.draws[0].report.manifest.topology_hash.clone().expect("topo");
    let canonical =
        blueprint_to_json(&blueprint_from_json(&doc, &|t| std_vocabulary(t)).unwrap()).unwrap();
    reg.put_blueprint(&topo, &canonical).expect("store blueprint");
    let id = reg
        .append_family("mcrepro", FamilyKind::MonteCarlo, &mc_member_reports(&family))
        .expect("append");

    // reproduce: every MC member re-derives bit-identically (its seed-driven walk is
    // reconstructed from manifest.seed — the realization branch).
    let rep = reproduce_family_in(&reg, &id, &data);
    assert_eq!(rep.outcomes.len(), 3, "three MC members reproduced");
    assert!(
        rep.outcomes.iter().all(|(_, ok)| *ok),
        "every MC member re-derives bit-identically: {:?}",
        rep.outcomes
    );
    let _ = std::fs::remove_dir_all(&dir);
}
  • Step 2: Run test to verify it fails

Run: cargo test -p aura-cli --bin aura reproduce_family_re_derives_every_mc_member_bit_identically Expected: FAIL — the assertion every MC member re-derives bit-identically fails: without the branch, reproduce re-runs MC members over data.run_sources() (the showcase series), not the seed-driven walk, so rerun.metrics != stored.metrics for every member.

  • Step 3: Write minimal implementation

In reproduce_family_in, replace the single member run (currently ~2561-2570):

        let rerun = run_blueprint_member(
            reload(),
            &point,
            &space,
            data.run_sources(),
            window,
            stored.manifest.seed,
            pip,
            &hash,
        );

with a realization-aware version that reconstructs the member's data from the facet its family kind recorded:

        // Realization-aware: a MonteCarlo member ran over a seed-driven synthetic walk,
        // not the showcase — reconstruct it from manifest.seed so the re-run matches (C1).
        let (sources, member_window, seed) = match family.kind {
            FamilyKind::MonteCarlo => {
                let s = synthetic_walk_sources(stored.manifest.seed);
                let w = window_of(&s).expect("non-empty synthetic walk");
                (s, w, stored.manifest.seed)
            }
            _ => (data.run_sources(), window, stored.manifest.seed),
        };
        let rerun = run_blueprint_member(
            reload(),
            &point,
            &space,
            sources,
            member_window,
            seed,
            pip,
            &hash,
        );

(window is the pre-loop data.full_window() at ~2515, used unchanged for the _ arm; family.kind is on the grouped Family the loop iterates.)

  • Step 4: Run test to verify it passes

Run: cargo test -p aura-cli --bin aura reproduce_family_re_derives_every_mc_member_bit_identically Expected: PASS.

  • Step 5: Verify the Sweep reproduce path stays green

Run: cargo test -p aura-cli --bin aura reproduce_family_re_derives_every_member_bit_identically Expected: PASS (the _ arm is byte-unchanged for Sweep members).


Task 4: run_blueprint_mc + the ["mc", ..] dispatch arm + E2E

Files:

  • Modify: crates/aura-cli/src/main.rs (run_blueprint_mc beside run_blueprint_sweep ~3210; the ["mc", rest @ ..] dispatch arm ~4006-4015)

  • Test: crates/aura-cli/tests/cli_run.rs (beside aura_reproduce_re_derives_a_persisted_sweep_bit_identically ~3456)

  • Step 1: Write the failing E2E tests

Add to crates/aura-cli/tests/cli_run.rs:

/// E2E (acc 1+3+4): `aura mc <closed blueprint.json> --seeds N` builds a MonteCarlo
/// family (one member per seed), writes exactly one content-addressed blueprint, and
/// `aura reproduce <id>` re-derives every member bit-identically (exit 0).
#[test]
fn aura_mc_over_a_blueprint_reproduces_bit_identically() {
    let cwd = temp_cwd("mc-blueprint-reproduce");
    let fixture = format!("{}/tests/fixtures/stage1_signal.json", env!("CARGO_MANIFEST_DIR"));

    let mc = Command::new(BIN)
        .args(["mc", &fixture, "--seeds", "4", "--name", "mcx"])
        .current_dir(&cwd)
        .output()
        .expect("spawn aura mc blueprint");
    assert!(mc.status.success(), "mc exit: {:?} stderr={}", mc.status, String::from_utf8_lossy(&mc.stderr));
    let mc_out = String::from_utf8(mc.stdout).expect("utf-8 stdout");
    // one member line per seed (each carries "seed":n) + one aggregate line.
    assert_eq!(
        mc_out.lines().filter(|l| l.contains("\"seed\":")).count(),
        4,
        "one member line per seed: {mc_out}"
    );
    assert!(mc_out.contains("mc_aggregate"), "prints the aggregate line: {mc_out}");

    // exactly one blueprint stored for the whole family (one topology per family).
    let store = cwd.join("runs/blueprints");
    let entries: Vec<_> = std::fs::read_dir(&store)
        .unwrap_or_else(|e| panic!("mc must create the blueprint store {store:?}: {e}"))
        .map(|e| e.expect("dir entry").path())
        .collect();
    assert_eq!(entries.len(), 1, "one stored topology per family: {entries:?}");

    let repro = Command::new(BIN)
        .args(["reproduce", "mcx-0"])
        .current_dir(&cwd)
        .output()
        .expect("spawn aura reproduce");
    assert!(repro.status.success(), "reproduce exit: {:?} stderr={}", repro.status, String::from_utf8_lossy(&repro.stderr));
    let repro_out = String::from_utf8(repro.stdout).expect("utf-8 stdout");
    assert!(
        repro_out.contains("reproduced 4/4 members bit-identically"),
        "every MC member re-derives: {repro_out}"
    );
    let _ = std::fs::remove_dir_all(&cwd);
}

/// E2E (acc 2): `aura mc <open blueprint.json>` is rejected with a named error + exit 2
/// (NOT a panic / exit 101) — MC binds no axis, so a free knob has no binder.
#[test]
fn aura_mc_rejects_an_open_blueprint() {
    let cwd = temp_cwd("mc-blueprint-open-reject");
    let fixture = format!("{}/tests/fixtures/stage1_signal_open.json", env!("CARGO_MANIFEST_DIR"));
    let out = Command::new(BIN)
        .args(["mc", &fixture, "--seeds", "4"])
        .current_dir(&cwd)
        .output()
        .expect("spawn aura mc open-blueprint");
    assert_eq!(out.status.code(), Some(2), "an open blueprint fails clean (exit 2, not a panic): stderr={}", String::from_utf8_lossy(&out.stderr));
    let stderr = String::from_utf8(out.stderr).expect("utf-8");
    assert!(stderr.contains("closed blueprint"), "names the closed-blueprint requirement: {stderr}");
    let _ = std::fs::remove_dir_all(&cwd);
}
  • Step 2: Run tests to verify they fail

Run: cargo test -p aura-cli --test cli_run aura_mc_over_a_blueprint_reproduces_bit_identically Expected: FAIL — aura mc <fixture>.json currently falls into parse_mc_args, which does not accept a .json first positional, so the command errors (no .json discriminator yet).

  • Step 3: Write run_blueprint_mc

Add beside run_blueprint_sweep (~3210):

/// `aura mc <blueprint.json> --seeds N`: build a Monte-Carlo family from a loaded CLOSED
/// blueprint (the World/C21 verb), store the canonical blueprint ONCE keyed by the shared
/// `topology_hash` (the 0094 hook, so `aura reproduce` re-derives it), record it as a
/// `FamilyKind::MonteCarlo` family (C18/C21 lineage), and print each draw's member line
/// (carrying the seed) plus the aggregate — mirroring `run_mc` / `run_blueprint_sweep`.
fn run_blueprint_mc(doc: &str, n_seeds: u64, name: &str, data: DataSource) {
    let family = blueprint_mc_family(doc, n_seeds, &data);
    let reg = default_registry();
    // Store the canonical blueprint ONCE, keyed by the family's shared topology_hash.
    let topo = family.draws[0]
        .report
        .manifest
        .topology_hash
        .clone()
        .expect("a blueprint mc stamps every member's topology_hash");
    let canonical = blueprint_to_json(
        &blueprint_from_json(doc, &|t| std_vocabulary(t))
            .expect("doc parse-validated at the dispatch boundary"),
    )
    .expect("a loaded blueprint re-serializes");
    reg.put_blueprint(&topo, &canonical).unwrap_or_else(|e| {
        eprintln!("aura: {e}");
        std::process::exit(2);
    });
    let id = match reg.append_family(name, FamilyKind::MonteCarlo, &mc_member_reports(&family)) {
        Ok(id) => id,
        Err(e) => {
            eprintln!("aura: {e}");
            std::process::exit(2);
        }
    };
    for draw in &family.draws {
        println!("{}", mc_member_line(&id, draw.seed, &draw.report));
    }
    println!("{}", mc_aggregate_json(&family.aggregate));
}
  • Step 4: Grow the ["mc", ..] dispatch arm

Replace the current arm (~4006-4015):

        ["mc", rest @ ..] => match parse_mc_args(rest) {
            Ok(McArgs::Synthetic { name, persist }) => run_mc(&name, persist),
            Ok(McArgs::RealR { choice, grid, block_len, n_resamples, seed }) => {
                run_mc_r_bootstrap(DataSource::from_choice(choice), &grid, block_len, n_resamples, seed)
            }
            Err(msg) => {
                eprintln!("aura: {msg}");
                std::process::exit(2);
            }
        },

with the .json-discriminator arm (mirroring the ["sweep", ..] arm ~3952):

        ["mc", rest @ ..] => {
            // A `.json`-file first argument selects the loaded-blueprint Monte-Carlo (the
            // World/C21 verb), mirroring the `["sweep", ..]` discriminator; a bare
            // `--strategy`/`--name` token never ends in `.json`, so the paths stay disjoint.
            if let Some(path) =
                rest.first().filter(|a| a.ends_with(".json") && std::path::Path::new(a).is_file())
            {
                let doc = std::fs::read_to_string(path).unwrap_or_else(|e| {
                    eprintln!("aura: {path}: {e}");
                    std::process::exit(2);
                });
                if let Err(e) = blueprint_from_json(&doc, &|t| std_vocabulary(t)) {
                    eprintln!("aura: {path}: {e:?}");
                    std::process::exit(2);
                }
                let McBlueprintArgs { n_seeds, name } =
                    parse_mc_blueprint_args(&rest[1..]).unwrap_or_else(|msg| {
                        eprintln!("aura: {msg}");
                        std::process::exit(2);
                    });
                run_blueprint_mc(&doc, n_seeds, &name, DataSource::Synthetic);
                return;
            }
            match parse_mc_args(rest) {
                Ok(McArgs::Synthetic { name, persist }) => run_mc(&name, persist),
                Ok(McArgs::RealR { choice, grid, block_len, n_resamples, seed }) => {
                    run_mc_r_bootstrap(DataSource::from_choice(choice), &grid, block_len, n_resamples, seed)
                }
                Err(msg) => {
                    eprintln!("aura: {msg}");
                    std::process::exit(2);
                }
            }
        }
  • Step 5: Run the E2E tests to verify they pass

Run: cargo test -p aura-cli --test cli_run aura_mc_over_a_blueprint_reproduces_bit_identically Expected: PASS.

Run: cargo test -p aura-cli --test cli_run aura_mc_rejects_an_open_blueprint Expected: PASS.

  • Step 6: Verify the hard-wired aura mc path is undisturbed

Run: cargo test -p aura-cli --test cli_run mc Expected: PASS (the hard-wired-mc E2Es still green — the .json discriminator keeps the non-.json path falling through to parse_mc_args).


Task 5: Full-workspace gate

Files: none (verification only)

  • Step 1: Build the workspace

Run: cargo build --workspace Expected: builds clean, 0 errors.

  • Step 2: Run the full test suite

Run: cargo test --workspace Expected: all suites pass (the 3 new units + 2 new E2Es green; no regressions).

  • Step 3: Lint

Run: cargo clippy --workspace --all-targets -- -D warnings Expected: 0 warnings.