//! `aura` — the programmatic / CLI face of the engine (the surface the LLM and //! automation drive: author a node, run a sim/sweep, emit structured metrics). //! //! The walking skeleton's closing seam: `aura run` bootstraps a built-in sample //! signal-quality harness (synthetic source → SMA-cross → Exposure → SimBroker → //! recording sinks), runs it deterministically (C1), and prints the run's //! metrics + manifest (#6) as canonical JSON to stdout (the headline C14 move). mod render; use aura_core::{Firing, Scalar, ScalarKind, Timestamp}; use aura_engine::{ f64_field, summarize, sweep, BlueprintNode, Composite, Edge, FlatGraph, GridSpace, Harness, OutField, ParamAlias, Role, RunManifest, RunReport, SourceSpec, SweepFamily, Target, }; use aura_registry::{rank_by, Registry}; use aura_std::{Ema, Exposure, Recorder, SimBroker, Sma, Sub}; use std::sync::mpsc::{self, Receiver}; /// The built-in synthetic price stream: rises through t=4 then reverses, so the /// demo trace carries one exposure sign flip and a real drawdown (C22 populated /// trace). Deterministic and fixed (C1). fn synthetic_prices() -> Vec<(Timestamp, Scalar)> { [ (1_i64, 1.0000_f64), (2, 1.0010), (3, 1.0030), (4, 1.0060), (5, 1.0040), (6, 1.0010), (7, 0.9990), ] .iter() .map(|&(t, p)| (Timestamp(t), Scalar::F64(p))) .collect() } /// Bootstrap the sample signal-quality harness with two recording sinks (equity /// tapped on the SimBroker, exposure tapped on the Exposure node). Rust-authored /// wiring (C17/C20) over the raw bootstrap API — no builder DSL this cycle. The /// price taps both SMAs and the broker's price slot (slot 1); exposure feeds the /// broker's slot 0 (slot order is load-bearing — both are f64). // The harness-plus-two-drained-sink-receivers tuple has exactly one call site // (`run_sample`); a named type would be speculative abstraction this cycle. #[allow(clippy::type_complexity)] fn sample_harness() -> ( Harness, Receiver<(Timestamp, Vec)>, Receiver<(Timestamp, Vec)>, ) { let (tx_eq, rx_eq) = mpsc::channel(); let (tx_ex, rx_ex) = mpsc::channel(); let f64_recorder_sig = || aura_engine::NodeSchema { inputs: vec![aura_engine::PortSpec { kind: ScalarKind::F64, firing: Firing::Any, name: "in".into() }], output: vec![], params: vec![], }; let h = Harness::bootstrap(FlatGraph { nodes: vec![ Box::new(Sma::new(2)), // 0 fast SMA Box::new(Sma::new(4)), // 1 slow SMA Box::new(Sub::new()), // 2 spread Box::new(Exposure::new(0.5)), // 3 exposure Box::new(SimBroker::new(0.0001)), // 4 sim-optimal broker Box::new(Recorder::new(&[ScalarKind::F64], Firing::Any, tx_eq)), // 5 equity sink Box::new(Recorder::new(&[ScalarKind::F64], Firing::Any, tx_ex)), // 6 exposure sink ], signatures: vec![ Sma::builder().schema().clone(), Sma::builder().schema().clone(), Sub::builder().schema().clone(), Exposure::builder().schema().clone(), SimBroker::builder(0.0001).schema().clone(), f64_recorder_sig(), f64_recorder_sig(), ], sources: vec![SourceSpec { kind: ScalarKind::F64, targets: vec![ Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }, Target { node: 4, slot: 1 }, // price into the broker's price slot ], }], edges: vec![ Edge { from: 0, to: 2, slot: 0, from_field: 0 }, Edge { from: 1, to: 2, slot: 1, from_field: 0 }, Edge { from: 2, to: 3, slot: 0, from_field: 0 }, Edge { from: 3, to: 4, slot: 0, from_field: 0 }, // exposure into broker slot 0 Edge { from: 4, to: 5, slot: 0, from_field: 0 }, // equity -> sink 5 Edge { from: 3, to: 6, slot: 0, from_field: 0 }, // exposure -> sink 6 ], }) .expect("valid sample signal-quality DAG"); (h, rx_eq, rx_ex) } /// Run the sample harness and fold it into a `RunReport` (drain both sinks → /// `f64_field` → `summarize` → pair with a `RunManifest`). Pure and deterministic /// (C1): the same build yields the same report. fn run_sample() -> RunReport { let (mut h, rx_eq, rx_ex) = sample_harness(); let prices = synthetic_prices(); let window = ( prices.first().expect("non-empty stream").0, prices.last().expect("non-empty stream").0, ); h.run(vec![prices]); let eq_rows: Vec<(Timestamp, Vec)> = rx_eq.try_iter().collect(); let ex_rows: Vec<(Timestamp, Vec)> = rx_ex.try_iter().collect(); let equity = f64_field(&eq_rows, 0); let exposure = f64_field(&ex_rows, 0); let metrics = summarize(&equity, &exposure); RunReport { manifest: RunManifest { commit: option_env!("AURA_COMMIT").unwrap_or("unknown").to_string(), params: vec![ ("sma_fast".to_string(), 2.0), ("sma_slow".to_string(), 4.0), ("exposure_scale".to_string(), 0.5), ], window, seed: 0, broker: "sim-optimal(pip_size=0.0001)".to_string(), }, metrics, } } /// The SMA-cross signal as a named composite (price -> fast/slow SMA -> spread). /// CLI-local sample builder; the engine ships no sample (the duplication with /// `blueprint.rs`'s test helper is the dedup tracked in #14). Value-empty: the SMA /// lengths are injected at compile, not baked here. fn sma_cross(name: &str) -> Composite { Composite::new( name, vec![Sma::builder().into(), Sma::builder().into(), Sub::builder().into()], vec![ Edge { from: 0, to: 2, slot: 0, from_field: 0 }, Edge { from: 1, to: 2, slot: 1, from_field: 0 }, ], vec![Role { name: "price".into(), targets: vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }], source: None, }], vec![ ParamAlias { name: "fast".into(), node: 0, slot: 0 }, // fast SMA length ParamAlias { name: "slow".into(), node: 1, slot: 0 }, // slow SMA length ], vec![OutField { node: 2, field: 0, name: "cross".into() }], ) } /// The sample signal-quality blueprint (value-empty) **with its two recording /// sinks reachable**: returns the equity + exposure receivers a per-point sweep /// run drains (the SMA lengths + exposure scale are injected at compile via the /// point vector). The single source of the sample topology — `build_sample` /// (the `aura graph` entry) is expressed on top of it. #[allow(clippy::type_complexity)] fn sample_blueprint_with_sinks() -> ( Composite, Receiver<(Timestamp, Vec)>, Receiver<(Timestamp, Vec)>, ) { let (tx_eq, rx_eq) = mpsc::channel(); let (tx_ex, rx_ex) = mpsc::channel(); let bp = Composite::new( "sample", vec![ BlueprintNode::Composite(sma_cross("sma_cross")), Exposure::builder().into(), SimBroker::builder(0.0001).into(), Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq).into(), Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_ex).into(), ], vec![ Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // spread -> Exposure Edge { from: 1, to: 2, slot: 0, from_field: 0 }, // exposure -> broker slot 0 Edge { from: 2, to: 3, slot: 0, from_field: 0 }, // equity -> sink Edge { from: 1, to: 4, slot: 0, from_field: 0 }, // exposure -> sink ], vec![Role { name: "price".into(), targets: vec![ Target { node: 0, slot: 0 }, // price -> sma_cross role 0 Target { node: 2, slot: 1 }, // price -> SimBroker price slot ], source: Some(ScalarKind::F64), }], vec![], // params: the interior sma_cross carries the aliases vec![], // output: the root ends in sinks, no re-export ); (bp, rx_eq, rx_ex) } /// The sample blueprint without its sink receivers — the `aura graph` render /// entry, which never runs the graph (so the receivers are dropped). fn build_sample() -> Composite { sample_blueprint_with_sinks().0 } /// The built-in sample rendered by `aura graph`. fn sample_blueprint() -> Composite { build_sample() } /// Coerce a sweep point's `Scalar` value to the manifest's `f64` param type. /// A tuning grid carries only numeric params (`I64` lengths, `F64` scales). fn scalar_as_param_f64(s: &Scalar) -> f64 { match s { Scalar::I64(n) => *n as f64, Scalar::F64(f) => *f, other => unreachable!("non-numeric sweep param: {other:?}"), } } /// Run the built-in sample over a small built-in grid (fast ∈ {2,3}, /// slow ∈ {4,5}, scale ∈ {0.5} — 4 points) and render one JSON line per point in /// enumeration (odometer) order. Pure + deterministic (C1): the same build yields /// the same report. Each point builds a fresh blueprint (fresh sink channels), /// bootstraps it under the point vector, runs it, and folds the drained sinks to /// metrics — the per-point closure the engine `sweep` drives disjointly. fn sweep_family() -> SweepFamily { let space = sample_blueprint_with_sinks().0.param_space(); let grid = GridSpace::new( &space, vec![ vec![Scalar::I64(2), Scalar::I64(3)], // fast ∈ {2, 3} vec![Scalar::I64(4), Scalar::I64(5)], // slow ∈ {4, 5} vec![Scalar::F64(0.5)], // scale ∈ {0.5} ], ) .expect("the built-in grid matches the sample param-space"); sweep(&grid, |point| { let (bp, rx_eq, rx_ex) = sample_blueprint_with_sinks(); let mut h = bp .bootstrap_with_params(point.to_vec()) .expect("grid points are kind-checked against param_space"); let prices = synthetic_prices(); let window = ( prices.first().expect("non-empty stream").0, prices.last().expect("non-empty stream").0, ); h.run(vec![prices]); let equity = f64_field(&rx_eq.try_iter().collect::>(), 0); let exposure = f64_field(&rx_ex.try_iter().collect::>(), 0); let params = space .iter() .zip(point) .map(|(ps, v)| (ps.name.clone(), scalar_as_param_f64(v))) .collect(); RunReport { manifest: RunManifest { commit: option_env!("AURA_COMMIT").unwrap_or("unknown").to_string(), params, window, seed: 0, broker: "sim-optimal(pip_size=0.0001)".to_string(), }, metrics: summarize(&equity, &exposure), } }) } /// Render a sweep family as one `RunReport` JSON line per point. Test helper: /// production (`run_sweep`) renders *and* persists per point. #[cfg(test)] fn sweep_report() -> String { let mut out = String::new(); for pt in &sweep_family().points { out.push_str(&pt.report.to_json()); out.push('\n'); } out } /// The default run registry: an append-only JSONL store under the current /// working directory. (A project-configured runs-dir via `Aura.toml` is a later /// refinement.) fn default_registry() -> Registry { Registry::open("runs/runs.jsonl") } /// `aura sweep`: run the built-in sweep, persist each point's `RunReport` to the /// registry (the run record, queryable over time — C18), and print each as one /// JSON line. fn run_sweep() { let reg = default_registry(); for pt in &sweep_family().points { if let Err(e) = reg.append(&pt.report) { eprintln!("aura: {e}"); std::process::exit(2); } println!("{}", pt.report.to_json()); } } /// `aura runs list`: print every stored run record, in store (over-time) order. fn runs_list() { for report in &load_runs_or_exit() { println!("{}", report.to_json()); } } /// `aura runs rank `: print the stored runs best-first by `metric`. fn runs_rank(metric: &str) { match rank_by(load_runs_or_exit(), metric) { Ok(ranked) => { for report in &ranked { println!("{}", report.to_json()); } } Err(e) => { eprintln!("aura: {e}"); std::process::exit(2); } } } /// Load the registry or exit 2 with the error. A missing registry loads empty. fn load_runs_or_exit() -> Vec { default_registry().load().unwrap_or_else(|e| { eprintln!("aura: {e}"); std::process::exit(2); }) } // --- MACD proof-of-concept (a richer, nested indicator + strategy) ----------- /// The MACD signal as a named composite: price → fast/slow `Ema` → the MACD line /// (their spread) → a signal `Ema` of that line → the histogram (line − signal). /// The composite exposes all **three MACD lines** as a named output record /// (`macd`, `signal`, `histogram`); the strategy trades the histogram by reading /// `from_field: 2`. A richer fixture than `sma_cross`: a nested EMA-of-EMA chain /// with interior fan-out (the MACD line feeds *both* the signal EMA and the /// histogram). Three `length` knobs (fast, slow, signal) are injected at compile /// in node order; value-empty here. fn macd(name: &str) -> Composite { Composite::new( name, vec![ Ema::builder().into(), // 0 fast EMA Ema::builder().into(), // 1 slow EMA Sub::builder().into(), // 2 MACD line = fast − slow Ema::builder().into(), // 3 signal EMA of the MACD line Sub::builder().into(), // 4 histogram = MACD line − signal ], vec![ Edge { from: 0, to: 2, slot: 0, from_field: 0 }, // fast → line[0] Edge { from: 1, to: 2, slot: 1, from_field: 0 }, // slow → line[1] Edge { from: 2, to: 3, slot: 0, from_field: 0 }, // line → signal EMA Edge { from: 2, to: 4, slot: 0, from_field: 0 }, // line → histogram[0] Edge { from: 3, to: 4, slot: 1, from_field: 0 }, // signal → histogram[1] ], vec![Role { name: "price".into(), targets: vec![ Target { node: 0, slot: 0 }, // price → fast EMA Target { node: 1, slot: 0 }, // price → slow EMA ], source: None, }], vec![ ParamAlias { name: "fast".into(), node: 0, slot: 0 }, // fast EMA length ParamAlias { name: "slow".into(), node: 1, slot: 0 }, // slow EMA length ParamAlias { name: "signal".into(), node: 3, slot: 0 }, // signal EMA length ], vec![ OutField { node: 2, field: 0, name: "macd".into() }, // the MACD line OutField { node: 3, field: 0, name: "signal".into() }, // the signal line OutField { node: 4, field: 0, name: "histogram".into() }, // the histogram ], ) } /// The MACD strategy blueprint (value-empty): the `macd` histogram → `Exposure` → /// `SimBroker` → recording sinks. Channels are threaded so a run can drain the /// sinks; `macd_blueprint` drops the receivers for the structural render. fn macd_strategy_blueprint( tx_eq: mpsc::Sender<(Timestamp, Vec)>, tx_ex: mpsc::Sender<(Timestamp, Vec)>, ) -> Composite { Composite::new( "macd_strategy", vec![ BlueprintNode::Composite(macd("macd")), Exposure::builder().into(), SimBroker::builder(0.0001).into(), Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_eq).into(), Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx_ex).into(), ], vec![ Edge { from: 0, to: 1, slot: 0, from_field: 2 }, // histogram → Exposure Edge { from: 1, to: 2, slot: 0, from_field: 0 }, // exposure → broker slot 0 Edge { from: 2, to: 3, slot: 0, from_field: 0 }, // equity → sink Edge { from: 1, to: 4, slot: 0, from_field: 0 }, // exposure → sink ], vec![Role { name: "price".into(), targets: vec![ Target { node: 0, slot: 0 }, // price → macd role 0 Target { node: 2, slot: 1 }, // price → SimBroker price slot ], source: Some(ScalarKind::F64), }], vec![], // params: the interior macd carries the aliases vec![], // output: the root ends in sinks, no re-export ) } /// The MACD strategy blueprint as a param-space fixture (receivers dropped, since /// `param_space()` reads structure only, never runs the graph). #[cfg(test)] fn macd_blueprint() -> Composite { let (tx_eq, _rx_eq) = mpsc::channel(); let (tx_ex, _rx_ex) = mpsc::channel(); macd_strategy_blueprint(tx_eq, tx_ex) } /// The point vector for the MACD strategy, in `param_space()` slot order: /// `[fast EMA length, slow EMA length, signal EMA length, exposure scale]`. Short /// windows so the 7-tick synthetic stream still produces a non-trivial trace /// (conventional MACD is 12/26/9, meaningless on 7 points). fn macd_point() -> Vec { vec![Scalar::I64(2), Scalar::I64(4), Scalar::I64(3), Scalar::F64(0.5)] } /// A longer synthetic stream than the SMA sample's 7 ticks: MACD's EMAs each warm /// up over their `length`, so the stream rises, falls, then rises again to give the /// histogram room to flip sign more than once *after* warm-up. Deterministic (C1). fn macd_prices() -> Vec<(Timestamp, Scalar)> { [ 1.0000_f64, 1.0008, 1.0021, 1.0039, 1.0062, 1.0090, 1.0083, 1.0061, 1.0034, 1.0012, 0.9998, 1.0006, 1.0024, 1.0047, 1.0069, 1.0086, 1.0097, 1.0092, ] .iter() .enumerate() .map(|(i, &p)| (Timestamp(i as i64 + 1), Scalar::F64(p))) .collect() } /// Run the MACD strategy: compile the nested composite blueprint to a flat harness /// (the same bootstrap path the SMA sample's compiled view uses), drive it on the /// synthetic stream, and fold both sinks into a `RunReport`. Pure and /// deterministic (C1). fn run_macd() -> RunReport { let (tx_eq, rx_eq) = mpsc::channel(); let (tx_ex, rx_ex) = mpsc::channel(); let flat = macd_strategy_blueprint(tx_eq, tx_ex) .compile_with_params(&macd_point()) .expect("valid macd blueprint"); let mut h = Harness::bootstrap(flat).expect("valid macd harness"); let prices = macd_prices(); let window = ( prices.first().expect("non-empty stream").0, prices.last().expect("non-empty stream").0, ); h.run(vec![prices]); let eq_rows: Vec<(Timestamp, Vec)> = rx_eq.try_iter().collect(); let ex_rows: Vec<(Timestamp, Vec)> = rx_ex.try_iter().collect(); let equity = f64_field(&eq_rows, 0); let exposure = f64_field(&ex_rows, 0); let metrics = summarize(&equity, &exposure); RunReport { manifest: RunManifest { commit: option_env!("AURA_COMMIT").unwrap_or("unknown").to_string(), params: vec![ ("ema_fast".to_string(), 2.0), ("ema_slow".to_string(), 4.0), ("ema_signal".to_string(), 3.0), ("exposure_scale".to_string(), 0.5), ], window, seed: 0, broker: "sim-optimal(pip_size=0.0001)".to_string(), }, metrics, } } const USAGE: &str = "usage: aura run [--macd] | aura graph | aura sweep | aura runs list | aura runs rank "; fn main() { // Collect argv and match the whole vector: every accepted form is exhaustive, // so an unexpected trailing token falls through to the usage-error path rather // than masquerading as a successful run (#16 strict reading). let args: Vec = std::env::args().skip(1).collect(); match args.iter().map(String::as_str).collect::>().as_slice() { ["run"] => println!("{}", run_sample().to_json()), ["run", "--macd"] => println!("{}", run_macd().to_json()), ["graph"] => print!("{}", render::render_html(&sample_blueprint())), ["sweep"] => run_sweep(), ["runs", "list"] => runs_list(), ["runs", "rank", metric] => runs_rank(metric), ["--help"] | ["-h"] => println!("{USAGE}"), _ => { eprintln!("aura: {USAGE}"); std::process::exit(2); } } } #[cfg(test)] mod tests { use super::*; #[test] fn sample_blueprint_with_sinks_bootstraps_runs_and_drains() { // the factory returns the two Recorder receivers (build_sample drops them), // so a caller can bootstrap one point, run it, and drain both sinks. let (bp, rx_eq, rx_ex) = sample_blueprint_with_sinks(); let mut h = bp .with("sma_cross.fast", 2) .with("sma_cross.slow", 4) .with("scale", 0.5) .bootstrap() .expect("sample blueprint compiles under a valid point"); h.run(vec![synthetic_prices()]); assert!(!rx_eq.try_iter().collect::>().is_empty(), "equity sink drained empty"); assert!(!rx_ex.try_iter().collect::>().is_empty(), "exposure sink drained empty"); } #[test] fn sweep_report_renders_four_points_in_odometer_order() { let out = sweep_report(); let lines: Vec<&str> = out.lines().collect(); assert_eq!(lines.len(), 4, "one JSON line per grid point; got: {out:?}"); // each line is a full RunReport; the commit is the real git HEAD // (volatile), so pin the per-point manifest params (odometer order, last // axis fastest) + the metric keys, not the commit value. for line in &lines { assert!(line.starts_with(r#"{"manifest":{"commit":""#), "not a RunReport: {line}"); } assert!(lines[0].contains(r#""params":{"sma_cross.fast":2,"sma_cross.slow":4,"scale":0.5}"#), "line0: {}", lines[0]); assert!(lines[1].contains(r#""params":{"sma_cross.fast":2,"sma_cross.slow":5,"scale":0.5}"#), "line1: {}", lines[1]); assert!(lines[2].contains(r#""params":{"sma_cross.fast":3,"sma_cross.slow":4,"scale":0.5}"#), "line2: {}", lines[2]); assert!(lines[3].contains(r#""params":{"sma_cross.fast":3,"sma_cross.slow":5,"scale":0.5}"#), "line3: {}", lines[3]); for line in &lines { assert!(line.contains(r#""total_pips":"#), "missing total_pips: {line}"); assert!(line.contains(r#""max_drawdown":"#), "missing max_drawdown: {line}"); assert!(line.contains(r#""exposure_sign_flips":"#), "missing flips: {line}"); assert!(line.ends_with('}'), "line not closed: {line}"); } } #[test] fn sweep_report_is_deterministic() { // C1 at the CLI edge: the same build yields a bit-identical report. assert_eq!(sweep_report(), sweep_report()); } #[test] fn run_macd_compiles_from_nested_composite_and_is_deterministic() { // the MACD strategy authors a nested EMA-of-EMA composite, compiles it to a // flat runnable harness (the call not panicking proves the compile+bootstrap // path), and runs it. C1 determinism: two runs are bit-identical. let r1 = run_macd(); let r2 = run_macd(); assert_eq!(r1.metrics, r2.metrics); assert_eq!(r1.to_json(), r2.to_json()); // the synthetic stream is carried end-to-end and the trace is well-formed. let (from, to) = r1.manifest.window; assert_eq!((from.0, to.0), (1, 18)); assert!(r1.metrics.total_pips.is_finite(), "macd pips must be finite: {:?}", r1.metrics); assert!(r1.metrics.max_drawdown >= 0.0, "drawdown is non-negative: {:?}", r1.metrics); // after warm-up the EMA-of-EMA histogram crosses zero, so the strategy // reverses exposure at least once — a genuinely non-trivial trace. assert!( r1.metrics.exposure_sign_flips >= 1, "macd trace should flip exposure: {:?}", r1.metrics ); } /// E2E acceptance (#41 / spec 0019, the worked example): the real MACD strategy /// blueprint's swept param surface relabels the three otherwise-indistinguishable /// EMA `length` slots to `macd.fast` / `macd.slow` / `macd.signal` — the named /// composite boundary visible end-to-end through `param_space()`, with the slot /// count and order unchanged (C23 — pure naming overlay, not curation: every /// interior slot stays sweepable, the `scale` knob is unaffected). #[test] fn macd_param_space_surfaces_the_three_named_aliases() { let names: Vec = macd_blueprint().param_space().into_iter().map(|p| p.name).collect(); // three aliased composite slots, in declared (fast, slow, signal) order, // then the strategy-level Exposure `scale` (outside the composite, unaliased). assert_eq!( names, vec![ "macd.fast".to_string(), "macd.slow".to_string(), "macd.signal".to_string(), "scale".to_string(), ], "MACD param surface must expose the three named EMA lengths + scale", ); } #[test] fn run_sample_is_deterministic_and_non_trivial() { let r1 = run_sample(); let r2 = run_sample(); // C1 determinism: two runs are bit-identical (metrics + rendered JSON). assert_eq!(r1.metrics, r2.metrics); assert_eq!(r1.to_json(), r2.to_json()); let m = &r1.metrics; // exactly one exposure sign flip in the demo trace (rises then reverses). assert_eq!(m.exposure_sign_flips, 1); // a non-trivial, populated trace: a real drawdown. assert!(m.max_drawdown > 0.0); // hand-computed magnitudes for the chosen stream (float tolerance; the // computation's dust is ~1e-15). assert!( (m.max_drawdown - 0.17).abs() < 1e-9, "max_drawdown = {}", m.max_drawdown ); assert!( (m.total_pips - (-0.13)).abs() < 1e-9, "total_pips = {}", m.total_pips ); // manifest carries the sample's known configuration. let (from, to) = r1.manifest.window; assert_eq!((from.0, to.0), (1, 7)); // commit is the build's git identity (or the no-git "unknown" fallback); // either way it is non-empty and fixed at compile time, so it is stable // across runs of the same build (C1 determinism, already asserted above // via `to_json()`). assert!(!r1.manifest.commit.is_empty()); assert_eq!(r1.manifest.commit, r2.manifest.commit); } }