From a31d91453cc1b43917ba1f9e1e317f233f462e89 Mon Sep 17 00:00:00 2001 From: Brummel Date: Sun, 7 Jun 2026 16:16:18 +0200 Subject: [PATCH] test(aura-engine): mirror param_space against compile() under nesting (#34) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cycle-0015 E2E guard param_space_mirrors_compiled_flat_node_param_order pins the load-bearing C23/#31 invariant — Blueprint::param_space() slot order mirrors compile()'s flat-node param order kind-by-slot — but only on the single-level composite_sma_cross_harness. collect_params (blueprint.rs:217) duplicates lower_items' depth-first traversal rather than sharing it (kept a parallel read-only projection so compile/inline_composite stay untouched and the compilat bit-identical, C9/C23), so the two orders must stay in lockstep forever. No mirror test ever compiled a composite-inside-a-composite: a future inliner reorder that desynced the projections only under nesting could pass the single-level mirror and the isolated nested param_space order test, yet break the slot-by-slot premise #31's binding rests on. Adds param_space_mirrors_compiled_flat_node_param_order_under_nesting: compiles a strategy -> { fast_slow -> [Sma, Sma, Sub], LinComb } nest and asserts param_space() equals the compiled flat-node param order kind-by-slot, pinning the concrete [I64, I64, F64, F64] shape. Green immediately — coverage-hardening, no desync in the current tree. Test-only; collect_params/lower_items/compile untouched. Verified: cargo test -p aura-engine green (55 tests), clippy --all-targets -D warnings clean. closes #34 --- crates/aura-engine/src/blueprint.rs | 63 +++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/crates/aura-engine/src/blueprint.rs b/crates/aura-engine/src/blueprint.rs index cfe400d..bc5f68f 100644 --- a/crates/aura-engine/src/blueprint.rs +++ b/crates/aura-engine/src/blueprint.rs @@ -872,6 +872,69 @@ mod tests { assert_eq!(space[2].kind, ScalarKind::F64); } + /// E2E (issue #34): the C23/#31 mirror invariant *under composite nesting*. + /// `param_space()` (via `collect_params`) duplicates `lower_items`' depth- + /// first traversal rather than sharing it, so the two orders must stay in + /// lockstep. The single-level mirror test + /// (`param_space_mirrors_compiled_flat_node_param_order`) never compiles a + /// composite whose interior holds *another* composite; the nested + /// `param_space` order test never compiles. This closes that gap: it + /// compiles a `strategy → { fast_slow → [Sma, Sma, Sub], LinComb }` nest and + /// asserts the aggregated space lines up, kind-by-slot, with the compiled + /// flat-node param order — so a future inliner reorder that desynced the two + /// projections *only under nesting* would fail here, not slip through. + #[test] + fn param_space_mirrors_compiled_flat_node_param_order_under_nesting() { + use aura_std::{LinComb, Sma, Sub}; + // inner composite "fast_slow": two SMAs + a Sub (same nest as the + // isolated path-qualification test above) + let fast_slow = Composite::new( + "fast_slow", + vec![Sma::new(2).into(), Sma::new(4).into(), Sub::new().into()], + vec![ + Edge { from: 0, to: 2, slot: 0, from_field: 0 }, + 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 }, + ); + // outer composite "strategy": the inner composite + a LinComb([1,-1]) + let strategy = Composite::new( + "strategy", + vec![BlueprintNode::Composite(fast_slow), LinComb::new(vec![1.0, -1.0]).into()], + vec![], + vec![vec![Target { node: 0, slot: 0 }]], + OutPort { node: 0, field: 0 }, + ); + let bp = Blueprint::new(vec![BlueprintNode::Composite(strategy)], vec![], vec![]); + + // the aggregated, path-qualified projection (borrows; take it first since + // compile() consumes self — same ordering as the single-level mirror test) + let space = bp.param_space(); + + // the same blueprint, compiled to its flat node array; each flat node's + // own declared params, concatenated in flat-node order + let (flat_nodes, _sources, _edges) = bp.compile().expect("nested composite compiles"); + let from_compilat: Vec = + flat_nodes.iter().flat_map(|n| n.schema().params).collect(); + + // same count, same per-slot kind, same order — the nested projection + // mirrors the compilation (names differ: param_space path-qualifies, the + // raw node does not, so compare on the load-bearing axis, kind-by-slot) + assert_eq!(space.len(), from_compilat.len(), "param count must match the compilat"); + assert_eq!( + space.iter().map(|p| p.kind).collect::>(), + from_compilat.iter().map(|p| p.kind).collect::>(), + "per-slot param kinds must line up with the compiled flat-node order, under nesting", + ); + // pin the concrete shape: two Sma lengths (I64), Sub none, two LinComb + // weights (F64) + assert_eq!( + space.iter().map(|p| p.kind).collect::>(), + [ScalarKind::I64, ScalarKind::I64, ScalarKind::F64, ScalarKind::F64], + ); + } + #[test] fn top_level_leaf_params_are_unqualified() { use aura_std::Sma;