// Milestone fieldtest — axis (b): deliberately mis-wire a graph (swap the // fast/slow SMA legs of the cross) and confirm the wiring reads back // differently — the milestone's headline promise that a render visibly // surfaces a mis-wiring. // // The headline render is the `aura graph` CLI (exercised separately, in the // accompanying .sh-free flow: see the spec). HERE we test the property the // render RELIES ON from the consumer side: that the labels + graph-as-data of a // correctly-wired composite differ observably from a swapped one. If a swap // produced an identical graph-as-data, no renderer could ever surface it. // // Public interface only: Composite / Blueprint accessors + Node::label() via // the boxed node; ledger C8-refinement (label is the disambiguating symbol). use aura_engine::{BlueprintNode, Composite, Edge, OutPort, Target}; // NB: `node.label()` on a `&Box` leaf dispatches via the trait object // without an explicit `use aura_core::Node` (the method is in scope through the // boxed trait object). A consumer does not need to import the Node trait to read // labels off a built graph. use aura_std::{Sma, Sub}; /// Build an SMA-cross composite. `swap` flips which SMA feeds Sub's slot 0. /// Correct: fast(SMA2) -> minuend(slot0), slow(SMA4) -> subtrahend(slot1). /// Swapped: slow -> slot0, fast -> slot1 (the classic fast/slow mis-wire). fn cross(swap: bool) -> Composite { let (e_fast, e_slow) = if swap { // slow(node 1) into slot0, fast(node 0) into slot1 ( Edge { from: 1, to: 2, slot: 0, from_field: 0 }, Edge { from: 0, to: 2, slot: 1, from_field: 0 }, ) } else { ( Edge { from: 0, to: 2, slot: 0, from_field: 0 }, Edge { from: 1, to: 2, slot: 1, from_field: 0 }, ) }; Composite::new( "sma_cross", vec![ BlueprintNode::from(Sma::new(2)), BlueprintNode::from(Sma::new(4)), BlueprintNode::from(Sub::new()), ], vec![e_fast, e_slow], vec![vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }]], OutPort { node: 2, field: 0 }, ) } fn node_labels(c: &Composite) -> Vec { c.nodes() .iter() .map(|n| match n { BlueprintNode::Leaf(node) => node.label(), BlueprintNode::Composite(inner) => format!("composite:{}", inner.name()), }) .collect() } fn main() { let correct = cross(false); let swapped = cross(true); // The per-node labels carry identifying params (SMA(2) vs SMA(4)) so two // SMAs are distinguishable in a render. println!("correct labels: {:?}", node_labels(&correct)); println!("swapped labels: {:?}", node_labels(&swapped)); // The mis-wire lives in the EDGE table; the graph-as-data must reflect it. let ce: Vec = correct.edges().to_vec(); let se: Vec = swapped.edges().to_vec(); println!("correct interior edges: {ce:?}"); println!("swapped interior edges: {se:?}"); // Headline property: the two graphs-as-data differ. A renderer reading the // edge table + labels can therefore make the swap visible. assert_ne!( ce, se, "a fast/slow swap MUST change the graph-as-data, else no render could surface it" ); // And the disambiguating labels are present (SMA(2)/SMA(4) distinguishable). let labels = node_labels(&correct); assert!( labels.iter().any(|l| l.contains("SMA(2)")) && labels.iter().any(|l| l.contains("SMA(4)")), "labels must carry SMA params so a swapped leg reads differently: {labels:?}" ); println!("OK: a fast/slow swap is observable in graph-as-data + labels."); }