// Cycle-0031 fieldtest — scenario 2 (axis b): the forcing function. // // Promise probed (spec 0031 acceptance #3): author the 2-SMA cross WITHOUT // names -> both legs default to "sma" -> they collide and the fan-in is // indistinguishable -> `IndistinguishableFanIn { node }`. The recovery is the // single act of naming the colliding legs. The question this fixture answers as // a downstream consumer: is the diagnostic DISCOVERABLE and HELPFUL from the // public surface alone — does it name the node, and does the error type/text // point the author at "give the legs distinct names"? // // Public interface only (ledger C9/C23 + spec 0031 + cargo doc). No src read. use std::sync::mpsc; use aura_core::{Firing, Scalar, ScalarKind, Timestamp}; use aura_engine::{ BlueprintNode, CompileError, Composite, Edge, OutField, Role, Target, }; use aura_std::{Exposure, Recorder, SimBroker, Sma, Sub}; /// Build the cross with the two SMA legs given (caller decides whether to name /// them). `name_them = false` reproduces the minimal un-named authoring. fn sma_cross(name_them: bool) -> Composite { let (fast, slow) = if name_them { (Sma::builder().named("fast"), Sma::builder().named("slow")) } else { (Sma::builder(), Sma::builder()) }; Composite::new( "sma_cross", vec![fast.into(), slow.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![OutField { node: 2, field: 0, name: "out".into() }], ) } fn harness( name_them: bool, tx: mpsc::Sender<(Timestamp, Vec)>, ) -> Composite { Composite::new( "harness", vec![ BlueprintNode::Composite(sma_cross(name_them)), Exposure::builder().into(), SimBroker::builder(1e-4).into(), Recorder::builder(vec![ScalarKind::F64], Firing::Any, tx).into(), ], vec![ Edge { from: 0, to: 1, slot: 0, from_field: 0 }, Edge { from: 1, to: 2, slot: 0, from_field: 0 }, Edge { from: 2, to: 3, slot: 0, from_field: 0 }, ], vec![Role { name: "price".into(), targets: vec![ Target { node: 0, slot: 0 }, Target { node: 2, slot: 1 }, ], source: Some(ScalarKind::F64), }], vec![OutField { node: 0, field: 0, name: "exposure".into() }], ) } fn main() { // --- 0. What does param_space() emit for the UN-NAMED cross? -------------- // Both legs default to "sma" -> sma_cross.sma.length appears TWICE // (a collision). This is what makes a by-name bind ambiguous/unknown. let (txs, _rxs) = mpsc::channel(); let unnamed_space = harness(false, txs).param_space(); println!("[0] un-named cross param_space():"); for p in &unnamed_space { println!(" {} : {:?}", p.name, p.kind); } // --- 1. The minimal un-named authoring is correctly REJECTED. ------------- let (txu, _rxu) = mpsc::channel(); let unnamed = harness(false, txu) .with("sma_cross.length", 2) // best guess at the colliding bare name .bootstrap(); // Harness (Ok branch) may not implement Debug; inspect the error only. println!("[1] un-named cross bootstrap is_err: {}", unnamed.is_err()); match unnamed.err() { // The binder may reject first (AmbiguousKnob on the colliding // sma_cross.length), OR the compile may reject first // (IndistinguishableFanIn). Record whichever wall the author hits. Some(other) => println!(" -> diagnostic the author sees: {other:?}"), None => panic!("BUG: un-named param-bearing fan-in should be rejected"), } // --- 1a. Bind the name param_space() ACTUALLY emits (the duplicate). ------ // sma_cross.sma.length is emitted TWICE -> binding it is AmbiguousKnob, NOT // the spec's headline IndistinguishableFanIn. So the by-name author never // sees "name the colliding legs"; they see a knob-name diagnostic. let (txa, _rxa) = mpsc::channel(); let ambiguous = harness(false, txa) .with("sma_cross.sma.length", 2) .bootstrap(); println!( "[1a] bind the real emitted (duplicate) name -> {:?}", ambiguous.err() ); // --- 1b. The bare bootstrap (no binds at all) isolates the COMPILE wall. --- // This removes the binder from the picture so we see the fan-in error itself. let (txc, _rxc) = mpsc::channel(); let compiled = harness(false, txc).compile_with_params(&[ Scalar::I64(2), Scalar::I64(4), Scalar::F64(0.5), ]); // FlatGraph (the Ok branch) does not implement Debug, so we cannot // `{:?}`-print the whole Result; inspect via .err() instead. println!("\n[1b] un-named cross compile_with_params err: {:?}", compiled.as_ref().err()); match compiled.err() { Some(CompileError::IndistinguishableFanIn { node }) => { println!( " -> IndistinguishableFanIn at interior node index {node} \ (the Sub that fans in both un-named SMAs)" ); } other => println!( " -> NOTE: expected IndistinguishableFanIn, got {other:?}" ), } // --- 2. The recovery: name the legs -> the SAME topology now compiles. ----- let (tx, rx) = mpsc::channel::<(Timestamp, Vec)>(); let named = harness(true, tx) .with("sma_cross.fast.length", 2) .with("sma_cross.slow.length", 4) .with("exposure.scale", 0.5) .bootstrap(); println!("\n[2] named cross bootstrap ok: {}", named.is_ok()); let mut h = named.expect("naming the colliding legs resolves the fan-in"); let prices: Vec<(Timestamp, Scalar)> = (0..12) .map(|i| (Timestamp(60_000_000_000 * i), Scalar::F64(1.0 + 0.01 * i as f64))) .collect(); h.run(vec![prices]); drop(h); let rows: Vec<_> = rx.iter().collect(); println!("[2] named run recorded {} rows", rows.len()); assert!(!rows.is_empty(), "named cross must run and record"); println!( "\nOK: un-named fan-in rejected, naming the legs is the single recovering act." ); }