diff --git a/crates/aura-cli/src/graph.rs b/crates/aura-cli/src/graph.rs index 36641f9..c21dd83 100644 --- a/crates/aura-cli/src/graph.rs +++ b/crates/aura-cli/src/graph.rs @@ -100,8 +100,8 @@ fn collect_distinct_composites(bp: &Blueprint) -> Vec<&Composite> { /// Render one composite's interior as a flat graph: interior leaves as `[type]`, /// nested composites as opaque `[name]`, plus an `[in:k]` entry marker per input -/// role (wired to its interior targets) and an `[out]` marker (wired from the -/// output port). Prefixed `":\n"`. +/// role (wired to its interior targets) and an `[out:]` marker per +/// re-exported output field (wired from its producer). Prefixed `":\n"`. fn render_definition(c: &Composite) -> String { let mut labels: Vec = Vec::with_capacity(c.nodes().len()); for inner in c.nodes() { @@ -121,9 +121,11 @@ fn render_definition(c: &Composite) -> String { edges.push((in_id, t.node)); } } - let out_id = labels.len(); - labels.push("out".to_string()); - edges.push((c.output().node, out_id)); + for of in c.output() { + let out_id = labels.len(); + labels.push(format!("out:{}", of.name)); + edges.push((of.node, out_id)); + } format!("{}:\n{}", c.name(), render_flat(&labels, &edges)) } diff --git a/crates/aura-cli/src/main.rs b/crates/aura-cli/src/main.rs index 7091842..6cdd8c7 100644 --- a/crates/aura-cli/src/main.rs +++ b/crates/aura-cli/src/main.rs @@ -10,7 +10,7 @@ mod graph; use aura_core::{Firing, Scalar, ScalarKind, Timestamp}; use aura_engine::{ - f64_field, summarize, Blueprint, BlueprintNode, Composite, Edge, Harness, OutPort, + f64_field, summarize, Blueprint, BlueprintNode, Composite, Edge, Harness, OutField, RunManifest, RunReport, SourceSpec, Target, }; use aura_std::{Ema, Exposure, Recorder, SimBroker, Sma, Sub}; @@ -127,7 +127,7 @@ fn sma_cross(name: &str) -> Composite { Edge { from: 1, to: 2, slot: 1, from_field: 0 }, ], vec![vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }]], - OutPort { node: 2, field: 0 }, + vec![OutField { node: 2, field: 0, name: "cross".into() }], ) } @@ -176,12 +176,13 @@ fn sample_point() -> Vec { // --- 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), -/// which is the composite's single output — the line the strategy trades on. 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. +/// (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, @@ -203,7 +204,11 @@ fn macd(name: &str) -> Composite { Target { node: 0, slot: 0 }, // price → fast EMA Target { node: 1, slot: 0 }, // price → slow EMA ]], - OutPort { node: 4, field: 0 }, // the histogram + 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 + ], ) } @@ -230,7 +235,7 @@ fn macd_strategy_blueprint( ], }], vec![ - Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // histogram → Exposure + 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 @@ -374,7 +379,7 @@ mod tests { let out = graph::render_blueprint(&sample_blueprint()); // the sma_cross body is defined exactly once, with its interior + ports assert_eq!(out.matches("sma_cross:").count(), 1, "definition not rendered once:\n{out}"); - for needle in ["[SMA]", "[Sub]", "[in:0]", "[out]"] { + for needle in ["[SMA]", "[Sub]", "[in:0]", "[out:cross]"] { assert!(out.contains(needle), "missing {needle} in definition:\n{out}"); } } @@ -388,14 +393,14 @@ mod tests { vec![Sma::factory().into()], vec![], vec![vec![Target { node: 0, slot: 0 }]], - OutPort { node: 0, field: 0 }, + vec![OutField { node: 0, field: 0, name: "out".into() }], ); let outer = Composite::new( "outer", vec![BlueprintNode::Composite(inner), Sub::factory().into()], vec![Edge { from: 0, to: 1, slot: 0, from_field: 0 }], vec![vec![Target { node: 0, slot: 0 }]], - OutPort { node: 1, field: 0 }, + vec![OutField { node: 1, field: 0, name: "out".into() }], ); let bp = Blueprint::new( vec![BlueprintNode::Composite(outer)], @@ -494,7 +499,7 @@ sma_cross: [Sub] │ ↓ - [out] + [out:cross] "#; diff --git a/crates/aura-engine/src/blueprint.rs b/crates/aura-engine/src/blueprint.rs index 55c254b..f9cafda 100644 --- a/crates/aura-engine/src/blueprint.rs +++ b/crates/aura-engine/src/blueprint.rs @@ -15,11 +15,15 @@ use aura_core::{LeafFactory, Node, ParamSpec, Scalar, ScalarKind}; use crate::harness::{BootstrapError, Edge, Harness, SourceSpec, Target}; -/// Which interior `(node, output-field)` is a composite's single output port (C8). -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct OutPort { +/// One re-exported field of a composite's output record: an interior +/// `(node, output-field)` surfaced at the boundary under `name`. `name` is a +/// non-load-bearing render/debug symbol (C23) — like `FieldSpec.name` and +/// `Composite.name`, it does not reach the compilat. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct OutField { pub node: usize, pub field: usize, + pub name: String, } /// A blueprint item: a leaf node or a nested composite. Both present a declared @@ -45,7 +49,7 @@ pub struct Composite { nodes: Vec, edges: Vec, input_roles: Vec>, - output: OutPort, + output: Vec, } impl Composite { @@ -58,7 +62,7 @@ impl Composite { nodes: Vec, edges: Vec, input_roles: Vec>, - output: OutPort, + output: Vec, ) -> Self { Self { name: name.into(), nodes, edges, input_roles, output } } @@ -79,9 +83,10 @@ impl Composite { pub fn input_roles(&self) -> &[Vec] { &self.input_roles } - /// The single exposed output port. - pub fn output(&self) -> OutPort { - self.output + /// The exposed output record: each entry re-exports one interior + /// `(node, output-field)` under a boundary name (C8 — one port, K columns). + pub fn output(&self) -> &[OutField] { + &self.output } } @@ -243,9 +248,10 @@ fn collect_params(items: &[BlueprintNode], prefix: &str, out: &mut Vec> }, + /// A composite lowered to its interior: its output record is these flat + /// `(node, field)` producers (one per re-exported field, declared order), and + /// input role `r` fans into `roles[r]` (flat targets). Names dropped (C23). + Composite { output: Vec<(usize, usize)>, roles: Vec> }, } /// Lower a list of blueprint items into the flat node array, appending interior @@ -301,12 +307,6 @@ fn inline_composite( let Composite { name: _, nodes, edges, input_roles, output } = c; let item_count = nodes.len(); - // the output port must name an in-range interior item (field range checked - // once the item's lowering is known) - if output.node >= item_count { - return Err(CompileError::OutputPortOutOfRange); - } - // recursively lower interior items, then rewrite interior edges through them let interior = lower_items(nodes, params, cursor, flat_nodes, flat_edges)?; for e in &edges { @@ -315,22 +315,25 @@ fn inline_composite( } } - // resolve the output port to a flat (node, field) - let out = match &interior[output.node] { - ItemLowering::Leaf { index } => { - if output.field >= flat_nodes[*index].schema().output.len() { - return Err(CompileError::OutputPortOutOfRange); - } - (*index, output.field) + // resolve each re-exported field to a flat (node, field), in declared order + let mut out: Vec<(usize, usize)> = Vec::with_capacity(output.len()); + for of in &output { + if of.node >= item_count { + return Err(CompileError::OutputPortOutOfRange); } - ItemLowering::Composite { output: nested, .. } => { - // a nested composite exposes exactly one output field - if output.field != 0 { - return Err(CompileError::OutputPortOutOfRange); + let resolved = match &interior[of.node] { + ItemLowering::Leaf { index } => { + if of.field >= flat_nodes[*index].schema().output.len() { + return Err(CompileError::OutputPortOutOfRange); + } + (*index, of.field) } - *nested - } - }; + ItemLowering::Composite { output: nested, .. } => { + *nested.get(of.field).ok_or(CompileError::OutputPortOutOfRange)? + } + }; + out.push(resolved); + } // resolve each input role to flat targets (a target into a nested composite // fans further) and kind-check every role @@ -373,11 +376,7 @@ fn rewrite_edge( (*index, e.from_field) } ItemLowering::Composite { output, .. } => { - // a composite exposes one output field; reading any other is malformed - if e.from_field != 0 { - return Err(CompileError::BadInteriorIndex); - } - *output + *output.get(e.from_field).ok_or(CompileError::BadInteriorIndex)? } }; let targets = resolve_target(&Target { node: e.to, slot: e.slot }, lowerings)?; @@ -529,7 +528,7 @@ mod tests { Edge { from: 1, to: 2, slot: 1, from_field: 0 }, ], vec![vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }]], - OutPort { node: 2, field: 0 }, + vec![OutField { node: 2, field: 0, name: "out".into() }], ) } @@ -546,7 +545,7 @@ mod tests { // 3 interior nodes (Pass1, Pass1, Join2) at flat 0..2, then SinkF64 at 3 assert_eq!(nodes.len(), 4); // interior edges rewritten at offset 0, then the output edge resolves the - // composite's OutPort (interior node 2, field 0) to the sink (flat node 3) + // composite's OutField (interior node 2, field 0) to the sink (flat node 3) assert_eq!( edges, vec![ @@ -563,6 +562,54 @@ mod tests { ); } + #[test] + fn composite_reexports_two_fields_to_distinct_consumers() { + // composite: two independent Pass1 leaves; role 0 -> leaf 0, role 1 -> leaf 1; + // output record re-exports leaf 0 as "a", leaf 1 as "b". + let c = Composite::new( + "two_out", + vec![pass1(), pass1()], + vec![], + vec![ + vec![Target { node: 0, slot: 0 }], + vec![Target { node: 1, slot: 0 }], + ], + vec![ + OutField { node: 0, field: 0, name: "a".into() }, + OutField { node: 1, field: 0, name: "b".into() }, + ], + ); + // composite is item 0; two sinks (items 1, 2) read its two output fields by + // from_field; one source fans into both roles. + let bp = Blueprint::new( + vec![BlueprintNode::Composite(c), sink_f64(), sink_f64()], + vec![SourceSpec { + kind: ScalarKind::F64, + targets: vec![Target { node: 0, slot: 0 }, Target { node: 0, slot: 1 }], + }], + vec![ + Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // field "a" -> sink 1 + Edge { from: 0, to: 2, slot: 0, from_field: 1 }, // field "b" -> sink 2 + ], + ); + let (nodes, sources, edges) = bp.compile().expect("valid multi-output composite"); + // flat layout: Pass1(0), Pass1(1), SinkF64(2), SinkF64(3) + assert_eq!(nodes.len(), 4); + // from_field 0 resolves to leaf 0, from_field 1 to leaf 1 — distinct producers + assert_eq!( + edges, + vec![ + Edge { from: 0, to: 2, slot: 0, from_field: 0 }, + Edge { from: 1, to: 3, slot: 0, from_field: 0 }, + ] + ); + // the source fanned into both interior leaves + assert_eq!( + sources[0].targets, + vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }] + ); + } + #[test] fn nested_composite_inlines() { // outer composite wraps the inner fan_composite as its only interior item, @@ -575,7 +622,7 @@ mod tests { vec![BlueprintNode::Composite(inner)], vec![], vec![vec![Target { node: 0, slot: 0 }]], - OutPort { node: 0, field: 0 }, + vec![OutField { node: 0, field: 0, name: "out".into() }], ); let bp = Blueprint::new( vec![BlueprintNode::Composite(outer), sink_f64()], @@ -600,6 +647,58 @@ mod tests { ); } + #[test] + fn outer_reexports_two_fields_of_inner_composite() { + // inner re-exports two leaves as "a","b"; outer re-exposes both inner roles + // and re-exports inner field 0 and field 1 (the latter exercises the nested arm). + let inner = Composite::new( + "inner_two", + vec![pass1(), pass1()], + vec![], + vec![ + vec![Target { node: 0, slot: 0 }], + vec![Target { node: 1, slot: 0 }], + ], + vec![ + OutField { node: 0, field: 0, name: "a".into() }, + OutField { node: 1, field: 0, name: "b".into() }, + ], + ); + let outer = Composite::new( + "outer_two", + vec![BlueprintNode::Composite(inner)], + vec![], + vec![ + vec![Target { node: 0, slot: 0 }], // outer role 0 -> inner role 0 + vec![Target { node: 0, slot: 1 }], // outer role 1 -> inner role 1 + ], + vec![ + OutField { node: 0, field: 0, name: "x".into() }, // inner field 0 + OutField { node: 0, field: 1, name: "y".into() }, // inner field 1 (nested arm) + ], + ); + let bp = Blueprint::new( + vec![BlueprintNode::Composite(outer), sink_f64(), sink_f64()], + vec![SourceSpec { + kind: ScalarKind::F64, + targets: vec![Target { node: 0, slot: 0 }, Target { node: 0, slot: 1 }], + }], + vec![ + Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // outer field x -> sink 1 + Edge { from: 0, to: 2, slot: 0, from_field: 1 }, // outer field y -> sink 2 + ], + ); + let (nodes, _sources, edges) = bp.compile().expect("valid nested multi-output"); + assert_eq!(nodes.len(), 4); // Pass1, Pass1, SinkF64, SinkF64 + assert_eq!( + edges, + vec![ + Edge { from: 0, to: 2, slot: 0, from_field: 0 }, + Edge { from: 1, to: 3, slot: 0, from_field: 0 }, + ] + ); + } + #[test] fn bad_interior_index_rejected() { // interior edge references interior node 9, which does not exist @@ -608,7 +707,7 @@ mod tests { vec![pass1()], vec![Edge { from: 0, to: 9, slot: 0, from_field: 0 }], vec![vec![Target { node: 0, slot: 0 }]], - OutPort { node: 0, field: 0 }, + vec![OutField { node: 0, field: 0, name: "out".into() }], ); let bp = Blueprint::new(vec![BlueprintNode::Composite(c)], vec![], vec![]); // the Ok arm holds Box (not Debug), so assert via the Err arm. @@ -623,7 +722,7 @@ mod tests { vec![pass1(), sink_i64()], vec![], vec![vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }]], - OutPort { node: 0, field: 0 }, + vec![OutField { node: 0, field: 0, name: "out".into() }], ); let bp = Blueprint::new(vec![BlueprintNode::Composite(c)], vec![], vec![]); // the Ok arm holds Box (not Debug), so assert via the Err arm. @@ -638,13 +737,32 @@ mod tests { vec![pass1()], vec![], vec![vec![Target { node: 0, slot: 0 }]], - OutPort { node: 0, field: 5 }, + vec![OutField { node: 0, field: 5, name: "out".into() }], ); let bp = Blueprint::new(vec![BlueprintNode::Composite(c)], vec![], vec![]); // the Ok arm holds Box (not Debug), so assert via the Err arm. assert_eq!(bp.compile().err(), Some(CompileError::OutputPortOutOfRange)); } + #[test] + fn consume_of_missing_output_field_is_rejected() { + // a single-field composite; a consumer reads from_field 1 (past the 1-field + // record) -> the rewrite_edge range-check rejects it. + let c = Composite::new( + "c", + vec![pass1()], + vec![], + vec![vec![Target { node: 0, slot: 0 }]], + vec![OutField { node: 0, field: 0, name: "a".into() }], + ); + let bp = Blueprint::new( + vec![BlueprintNode::Composite(c), sink_f64()], + vec![SourceSpec { kind: ScalarKind::F64, targets: vec![Target { node: 0, slot: 0 }] }], + vec![Edge { from: 0, to: 1, slot: 0, from_field: 1 }], // only field 0 exists + ); + assert_eq!(bp.compile().err(), Some(CompileError::BadInteriorIndex)); + } + #[test] fn bootstrap_error_is_wrapped() { // a top-level kind mismatch: a Pass1 f64 output wired into a SinkI64 i64 @@ -733,7 +851,7 @@ mod tests { Edge { from: 1, to: 2, slot: 1, from_field: 0 }, ], vec![vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }]], - OutPort { node: 2, field: 0 }, + vec![OutField { node: 2, field: 0, name: "out".into() }], ) } @@ -799,6 +917,64 @@ mod tests { assert!(!comp_ex_v.is_empty(), "exposure trace must be populated"); } + /// E2E (cycle 0018): a composite's multi-field output record is selected + /// field-wise downstream all the way through `bootstrap + run` — two consumers + /// reading distinct `from_field`s off one multi-output composite record the two + /// distinct interior producers' streams, deterministically. The Task-2 unit + /// tests stop at `compile()` (edge resolution); this one runs the harness, so a + /// regression that resolved both taps to the same producer (or dropped a field) + /// would surface as identical recorded traces here, not just a bad edge table. + #[test] + fn multi_output_composite_taps_distinct_fields_through_a_run() { + let prices = synthetic_prices(); + let (tx_a, rx_a) = mpsc::channel(); + let (tx_b, rx_b) = mpsc::channel(); + // composite: two SMAs of different lengths, each its own input role; the + // output record re-exports SMA-fast as "a" (field 0) and SMA-slow as "b" + // (field 1). One source fans into both roles; two recorders tap the two + // fields by from_field. + let c = Composite::new( + "two_sma", + vec![Sma::factory().into(), Sma::factory().into()], + vec![], + vec![ + vec![Target { node: 0, slot: 0 }], + vec![Target { node: 1, slot: 0 }], + ], + vec![ + OutField { node: 0, field: 0, name: "a".into() }, + OutField { node: 1, field: 0, name: "b".into() }, + ], + ); + let bp = Blueprint::new( + vec![ + BlueprintNode::Composite(c), + Recorder::factory(vec![ScalarKind::F64], Firing::Any, tx_a).into(), + Recorder::factory(vec![ScalarKind::F64], Firing::Any, tx_b).into(), + ], + vec![SourceSpec { + kind: ScalarKind::F64, + targets: vec![Target { node: 0, slot: 0 }, Target { node: 0, slot: 1 }], + }], + vec![ + Edge { from: 0, to: 1, slot: 0, from_field: 0 }, // field "a" (SMA-2) -> recorder a + Edge { from: 0, to: 2, slot: 0, from_field: 1 }, // field "b" (SMA-4) -> recorder b + ], + ); + let mut h = bp + .bootstrap_with_params(vec![Scalar::I64(2), Scalar::I64(4)]) + .expect("multi-output composite bootstraps"); + h.run(vec![prices]); + + let a = rx_a.try_iter().collect::>(); + let b = rx_b.try_iter().collect::>(); + // both fields recorded something (the equality below is meaningful only if + // populated) and the two taps captured different streams — distinct fast vs + // slow SMA, so the two from_field selections resolve to distinct producers. + assert!(!a.is_empty() && !b.is_empty(), "both field taps must be populated"); + assert_ne!(a, b, "the two from_field taps must record distinct interior streams"); + } + #[test] fn injecting_a_different_vector_changes_the_run() { let prices = synthetic_prices(); @@ -913,7 +1089,7 @@ mod tests { Edge { from: 1, to: 2, slot: 1, from_field: 0 }, ], vec![vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }]], - OutPort { node: 2, field: 0 }, + vec![OutField { node: 2, field: 0, name: "out".into() }], ); // outer composite "strategy": the inner composite + a LinComb([1,-1]) let strategy = Composite::new( @@ -921,7 +1097,7 @@ mod tests { vec![BlueprintNode::Composite(fast_slow), LinComb::factory(2).into()], vec![], vec![vec![Target { node: 0, slot: 0 }]], - OutPort { node: 0, field: 0 }, + vec![OutField { node: 0, field: 0, name: "out".into() }], ); let bp = Blueprint::new(vec![BlueprintNode::Composite(strategy)], vec![], vec![]); @@ -964,7 +1140,7 @@ mod tests { Edge { from: 1, to: 2, slot: 1, from_field: 0 }, ], vec![vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }]], - OutPort { node: 2, field: 0 }, + vec![OutField { node: 2, field: 0, name: "out".into() }], ); // outer composite "strategy": the inner composite + a LinComb([1,-1]) let strategy = Composite::new( @@ -972,7 +1148,7 @@ mod tests { vec![BlueprintNode::Composite(fast_slow), LinComb::factory(2).into()], vec![], vec![vec![Target { node: 0, slot: 0 }]], - OutPort { node: 0, field: 0 }, + vec![OutField { node: 0, field: 0, name: "out".into() }], ); let bp = Blueprint::new(vec![BlueprintNode::Composite(strategy)], vec![], vec![]); diff --git a/crates/aura-engine/src/lib.rs b/crates/aura-engine/src/lib.rs index 8702225..b4ce7f3 100644 --- a/crates/aura-engine/src/lib.rs +++ b/crates/aura-engine/src/lib.rs @@ -35,7 +35,7 @@ mod blueprint; mod harness; mod report; -pub use blueprint::{Blueprint, BlueprintNode, CompileError, Composite, OutPort}; +pub use blueprint::{Blueprint, BlueprintNode, CompileError, Composite, OutField}; pub use harness::{BootstrapError, Edge, Harness, SourceSpec, Target}; pub use report::{f64_field, summarize, RunManifest, RunMetrics, RunReport}; // #29: re-export the core scalar vocabulary a Blueprint builder needs diff --git a/fieldtests/milestone-construction-layer/mc_1_composite_build_run.rs b/fieldtests/milestone-construction-layer/mc_1_composite_build_run.rs index 2355da1..c2a8478 100644 --- a/fieldtests/milestone-construction-layer/mc_1_composite_build_run.rs +++ b/fieldtests/milestone-construction-layer/mc_1_composite_build_run.rs @@ -3,7 +3,7 @@ // and confirm the recorded trace is non-empty. // // Public interface only: Blueprint / Composite / BlueprintNode / Edge / Target / -// OutPort / SourceSpec from aura-engine; Sma / Sub / Exposure / SimBroker / +// OutField / SourceSpec from aura-engine; Sma / Sub / Exposure / SimBroker / // Recorder from aura-std; ScalarKind / Scalar / Firing / Timestamp from // aura-core. No crates/*/src was read. // @@ -17,7 +17,7 @@ use std::sync::mpsc; use aura_core::{Firing, Scalar, ScalarKind, Timestamp}; -use aura_engine::{Blueprint, BlueprintNode, Composite, Edge, OutPort, SourceSpec, Target}; +use aura_engine::{Blueprint, BlueprintNode, Composite, Edge, OutField, SourceSpec, Target}; use aura_std::{Exposure, Recorder, SimBroker, Sma, Sub}; fn main() { @@ -44,7 +44,7 @@ fn main() { Target { node: 1, slot: 0 }, ]], // single exposed output: Sub's output field 0 - OutPort { node: 2, field: 0 }, + vec![OutField { node: 2, field: 0, name: "out".into() }], ); // --- The harness blueprint that USES the composite. ---------------------- diff --git a/fieldtests/milestone-construction-layer/mc_2_miswire_render.rs b/fieldtests/milestone-construction-layer/mc_2_miswire_render.rs index 2e599ad..99bf474 100644 --- a/fieldtests/milestone-construction-layer/mc_2_miswire_render.rs +++ b/fieldtests/milestone-construction-layer/mc_2_miswire_render.rs @@ -12,7 +12,7 @@ // 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}; +use aura_engine::{BlueprintNode, Composite, Edge, OutField, 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 @@ -44,7 +44,7 @@ fn cross(swap: bool) -> Composite { ], vec![e_fast, e_slow], vec![vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }]], - OutPort { node: 2, field: 0 }, + vec![OutField { node: 2, field: 0, name: "out".into() }], ) } diff --git a/fieldtests/milestone-construction-layer/mc_3_nested_composite.rs b/fieldtests/milestone-construction-layer/mc_3_nested_composite.rs index 5902e64..366ab81 100644 --- a/fieldtests/milestone-construction-layer/mc_3_nested_composite.rs +++ b/fieldtests/milestone-construction-layer/mc_3_nested_composite.rs @@ -15,7 +15,7 @@ use std::sync::mpsc; use aura_core::{Firing, Scalar, ScalarKind, Timestamp}; -use aura_engine::{Blueprint, BlueprintNode, Composite, Edge, OutPort, SourceSpec, Target}; +use aura_engine::{Blueprint, BlueprintNode, Composite, Edge, OutField, SourceSpec, Target}; use aura_std::{Exposure, Recorder, SimBroker, Sma, Sub}; /// Inner composite: the SMA(2)/SMA(4) cross (one price role -> spread output). @@ -32,7 +32,7 @@ fn inner_cross() -> Composite { Edge { from: 1, to: 2, slot: 1, from_field: 0 }, ], vec![vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }]], - OutPort { node: 2, field: 0 }, + vec![OutField { node: 2, field: 0, name: "out".into() }], ) } @@ -49,7 +49,7 @@ fn strategy() -> Composite { ], vec![Edge { from: 0, to: 1, slot: 0, from_field: 0 }], // cross -> Exposure vec![vec![Target { node: 0, slot: 0 }]], // price -> inner role 0 - OutPort { node: 1, field: 0 }, + vec![OutField { node: 1, field: 0, name: "out".into() }], ) } diff --git a/fieldtests/milestone-construction-layer/mc_4_introspect_graph.rs b/fieldtests/milestone-construction-layer/mc_4_introspect_graph.rs index a123f28..a454d72 100644 --- a/fieldtests/milestone-construction-layer/mc_4_introspect_graph.rs +++ b/fieldtests/milestone-construction-layer/mc_4_introspect_graph.rs @@ -10,7 +10,7 @@ // Public interface only. use aura_core::ScalarKind; -use aura_engine::{Blueprint, BlueprintNode, Composite, Edge, OutPort, SourceSpec, Target}; +use aura_engine::{Blueprint, BlueprintNode, Composite, Edge, OutField, SourceSpec, Target}; // NB (fieldtest finding): `aura_engine::ScalarKind` does NOT resolve — the // construction surface (aura-engine) does not re-export the core scalar // vocabulary (ScalarKind / Scalar / Firing) that a graph-builder needs. A @@ -31,7 +31,7 @@ fn sma_cross() -> Composite { Edge { from: 1, to: 2, slot: 1, from_field: 0 }, ], vec![vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }]], - OutPort { node: 2, field: 0 }, + vec![OutField { node: 2, field: 0, name: "out".into() }], ) }