test(engine): pin that a no-out-edge producer runs inertly (#282 substrate)

Current-behaviour pin for the declared-taps design: a producer node
with a non-empty output and zero out-edges compiles, bootstraps, and
runs — its per-cycle output silently discarded — observably identical
to the same graph with the producer removed. This is the exact shape
of an unbound declared tap (a study wire left un-recorded in a sweep),
and it establishes that the engine already tolerates it: no DCE is
needed for correctness, because check_ports_connected gates only input
slots and the Kahn sort emits a no-out-edge node as a valid sink. The
build-time-elision soundness the tap mechanism will rely on.

refs #282
This commit is contained in:
2026-07-17 21:31:32 +02:00
parent c302c5d5a0
commit b11bcb6202
+61
View File
@@ -1424,6 +1424,67 @@ mod tests {
);
}
#[test]
fn a_producer_with_no_out_edge_runs_inertly_identical_to_its_removal() {
// CURRENT-BEHAVIOUR PIN (#282 substrate): a producer node with a
// NON-EMPTY output and ZERO out-edges compiles, bootstraps, and runs —
// its per-cycle Some(row) is simply discarded, disturbing nothing. This
// is the exact shape of an *unbound declared tap*: the tap mechanism
// will let such a producer be declared and left un-recorded in a sweep,
// and this pins that the engine already tolerates it (no DCE needed for
// correctness — check_ports_connected gates only input slots, and the
// Kahn sort emits a no-out-edge node as a valid sink). The proof is
// observable identity: the run's drained output is byte-for-byte the same
// with the dangling producer present as with it removed.
let prices = f64_stream(&[(1, 10.0), (2, 20.0), (3, 30.0)]);
// WITH a dangling producer: source fans out to Sma(1) [node 0, a real
// producer that returns Some every warm cycle, wired to NOTHING] and to
// a Recorder [node 1] fed directly by the source.
let (tx, rx) = mpsc::channel();
let mut h_with = boot(
vec![
Box::new(Sma::new(1)),
Box::new(Recorder::new(&[ScalarKind::F64], Firing::Any, tx)),
],
vec![Sma::builder().schema().clone(), recorder_sig(&[ScalarKind::F64], Firing::Any)],
vec![SourceSpec::raw(
ScalarKind::F64,
vec![Target { node: 0, slot: 0 }, Target { node: 1, slot: 0 }],
)],
vec![], // NO edge out of node 0 — the producer dangles
)
.expect("a no-out-edge producer is a valid graph");
h_with.run(vec![Box::new(VecSource::new(prices.clone()))]);
let with_dangling: Vec<(Timestamp, Vec<Scalar>)> = rx.try_iter().collect();
// WITHOUT it: just the Recorder, fed by the same source.
let (tx2, rx2) = mpsc::channel();
let mut h_without = boot(
vec![Box::new(Recorder::new(&[ScalarKind::F64], Firing::Any, tx2))],
vec![recorder_sig(&[ScalarKind::F64], Firing::Any)],
vec![SourceSpec::raw(ScalarKind::F64, vec![Target { node: 0, slot: 0 }])],
vec![],
)
.expect("valid");
h_without.run(vec![Box::new(VecSource::new(prices))]);
let without_dangling: Vec<(Timestamp, Vec<Scalar>)> = rx2.try_iter().collect();
assert_eq!(
with_dangling,
vec![
(Timestamp(1), vec![Scalar::f64(10.0)]),
(Timestamp(2), vec![Scalar::f64(20.0)]),
(Timestamp(3), vec![Scalar::f64(30.0)]),
],
"the drained output is the source series; the dangling producer changes nothing"
);
assert_eq!(
with_dangling, without_dangling,
"a producer wired to nothing is observably inert — identical to its removal"
);
}
#[test]
fn mixed_a_and_b_or_combine_on_one_node() {
// MixedSum @0 (in0,in1 barrier group 0; in2 as-of); node 1 = Recorder taps it.