Files
claude f3daded514 feat(engine): compile resolves and hoists declared taps (#282)
The compile core. resolve_tap_wire resolves a blueprint Tap's interior
{node, field} through the lowering table exactly as OutField re-exports and
edges resolve — a leaf to its remapped index (output-arity checked), a nested
composite through its output remap. Taps are terminal (not re-exported through
the boundary): a flat_taps accumulator threads through the lowering recursion,
so an interior composite's taps hoist to the root FlatGraph.taps with remapped
wires — the case the CLI single-run wrapper relies on. validate_wiring gains a
tap leg (threaded at all three call sites incl. GraphSession::finish) and
CompileError::TapWireOutOfRange names a bad wire. Root-resolve, out-of-arity,
and nested-hoist are pinned green.

refs #282
2026-07-17 23:18:00 +02:00

137 lines
6.6 KiB
Rust

//! End-to-end coverage for the `FlatTap` / `FlatGraph.taps` plumbing (#282):
//! a `Composite` can declare a `Tap` (via `with_taps`), and `compile()` now
//! resolves it into the flat graph it hands to `Harness::bootstrap`. These
//! tests pin the two halves of that contract at the public `aura_engine`
//! boundary: `compile()` surfaces a declared `Tap` into `FlatGraph.taps`,
//! resolved to its flat producer `(node, field)`
//! (`compile_resolves_a_declared_tap_at_the_public_boundary`), and doing so
//! is otherwise transparent to compilation of the rest of the graph — the
//! same node/edge structure, just an added taps entry
//! (`tapped_composite_compiles_identically_to_untapped_besides_the_taps_list`).
use aura_core::ScalarKind;
use aura_engine::{BlueprintNode, CompileError, Composite, FlatTap, OutField, Role, Tap, TapWire, Target};
use aura_std::Sub;
/// A trivial one-node composite with a single declared output (`Sub`'s
/// difference), matching the `taps` argument requested by each test. Both of
/// `Sub`'s input slots are fed by one root-bound (source) role — enough to
/// satisfy `compile()`'s port-connectivity + root-binding checks.
fn leaf(taps: Vec<Tap>) -> Composite {
Composite::new(
"leaf",
vec![Sub::builder().into()],
vec![],
vec![Role {
name: "price".into(),
targets: vec![Target { node: 0, slot: 0 }, Target { node: 0, slot: 1 }],
source: Some(ScalarKind::F64),
}],
vec![OutField { node: 0, field: 0, name: "o".into() }],
)
.with_taps(taps)
}
/// Property: **`compile()` resolves a declared `Tap` at the public
/// boundary.** A composite declaring a valid `Tap` (naming a real interior
/// producer, `node: 0, field: 0`) compiles to a `FlatGraph` whose `taps` list
/// carries the resolved `FlatTap` — name preserved, wire resolved to the flat
/// producer index.
#[test]
fn compile_resolves_a_declared_tap_at_the_public_boundary() {
let tapped = leaf(vec![Tap { name: "spread".into(), from: TapWire { node: 0, field: 0 } }]);
let flat = tapped.compile().expect("a tap-bearing composite compiles");
assert_eq!(
flat.taps,
vec![FlatTap { name: "spread".into(), node: 0, field: 0 }],
"compile() must resolve the declared Tap into FlatGraph.taps: {:?}",
flat.taps
);
}
/// Property: **a declared `Tap` is otherwise inert at compile time** — it
/// changes nothing about the flattened node/edge structure. Compiling the
/// same composite with and without a `Tap` declaration must produce
/// structurally identical flat graphs (same node count, same edges); the
/// only difference tap-bearing scaffolding is allowed to make is the `taps`
/// list itself (empty vs. carrying the one resolved entry).
#[test]
fn tapped_composite_compiles_identically_to_untapped_besides_the_taps_list() {
let untapped = leaf(vec![]).compile().expect("untapped compiles");
let tapped = leaf(vec![Tap { name: "spread".into(), from: TapWire { node: 0, field: 0 } }])
.compile()
.expect("tapped compiles");
assert_eq!(untapped.nodes.len(), tapped.nodes.len(), "tap declaration must not add/remove flat nodes");
assert_eq!(untapped.edges, tapped.edges, "tap declaration must not perturb flat edges");
assert!(untapped.taps.is_empty(), "the untapped composite compiles to an empty taps list");
assert_eq!(
tapped.taps,
vec![FlatTap { name: "spread".into(), node: 0, field: 0 }],
"the tapped composite compiles to the one resolved entry"
);
}
/// Property: **the `validate_wiring` structural leg rejects a tap whose wire
/// names a node index beyond the composite's own node count**, before any
/// lowering or field-arity resolution ever runs — mirroring how the sibling
/// `output` leg raises `OutputPortOutOfRange` for the same shape of mistake.
/// This is the early, cheap structural gate (`tap.from.node >= nodes.len()`)
/// added alongside tap resolution in this iteration; without it, a
/// wildly-out-of-range tap wire would only be caught by luck at whichever
/// later stage happens to index into a lowering table, not deterministically
/// at compile's first validation pass.
#[test]
fn compile_rejects_a_tap_wire_node_out_of_range() {
let tapped = leaf(vec![Tap { name: "ghost".into(), from: TapWire { node: 1, field: 0 } }]);
match tapped.compile() {
Err(CompileError::TapWireOutOfRange { tap }) => assert_eq!(tap, "ghost"),
other => panic!("expected TapWireOutOfRange{{tap: \"ghost\"}}, got {:?}", other.map(|_| ())),
}
}
/// Property: **a `Tap` declared on a nested composite hoists all the way to
/// the root `FlatGraph.taps`, with its wire remapped through the inline
/// offset** accumulated by every flat node lowered ahead of it — not just
/// carried through unresolved, and not resolved to its interior-local index.
/// The root here lowers a plain leaf node first (flat index 0) followed by
/// the tapped composite (whose interior `Sub` therefore lands at flat index
/// 1, not 0), so a remap that silently stayed at the interior-local index —
/// or hoisted at all — is caught by asserting the hoisted `FlatTap` points at
/// index 1. This is the exact shape the by-name tap-binding CLI wrapper
/// needs: a tap declared arbitrarily deep in an authored blueprint must
/// surface as one flat, addressable measurement point at the root.
#[test]
fn compile_hoists_a_nested_composite_tap_to_the_root_with_remapped_index() {
let inner = Composite::new(
"inner",
vec![Sub::builder().into()],
vec![],
vec![Role { name: "x".into(), targets: vec![Target { node: 0, slot: 0 }, Target { node: 0, slot: 1 }], source: None }],
vec![OutField { node: 0, field: 0, name: "o".into() }],
)
.with_taps(vec![Tap { name: "inner_d".into(), from: TapWire { node: 0, field: 0 } }]);
let root = Composite::new(
"root",
vec![Sub::builder().into(), BlueprintNode::Composite(inner)],
vec![],
vec![
Role {
name: "lead".into(),
targets: vec![Target { node: 0, slot: 0 }, Target { node: 0, slot: 1 }],
source: Some(ScalarKind::F64),
},
Role { name: "a".into(), targets: vec![Target { node: 1, slot: 0 }], source: Some(ScalarKind::F64) },
],
vec![],
);
let flat = root.compile().expect("root with a nested tapped composite compiles");
assert_eq!(
flat.taps,
vec![FlatTap { name: "inner_d".into(), node: 1, field: 0 }],
"the inner tap must hoist to root FlatGraph.taps, remapped to the inline-offset flat node index"
);
}