90e298d3b4
First fieldtest of the sum combinators. A standalone downstream-consumer crate
(fieldtests/cycle-0008-sum-combinators/) path-depends on the engine crates and
exercises the post-0008 surface from the public interface only (rustdoc + ledger
+ glossary + project-layout, never crates/*/src).
Primary axis empirically met: the north-star two-signal combine move now uses the
shipped Add (Add::new() dropped in exactly where the 0007 fixture hand-authored
an Add2) — the 0007 boilerplate is retired, end to end through SimBroker to a
deterministic recorded pip curve.
Findings: 0 bugs, 1 friction, 2 spec_gaps, 3 working.
- working ×3: Add2 retired; LinComb weighted/variadic sums exact (<1e-12);
warm-up barrier + empty-weights panic as documented.
- spec_gap: Add/LinComb per-input firing policy (Firing::Any / mode-A as-of
join) absent from the public surface — same class as the 0007 SimBroker gap.
- spec_gap: a fired combinator emits one row per *cycle*, so a heterogeneous
same-timestamp multi-source trace can carry >1 row per timestamp (correct per
C4/C5, undocumented at this surface).
- friction: nested consumer crate still needs the empty [workspace] table
(carried from 0006/0007; #9 closed the docs half; folds into aura new).
Spec feeds the next plan as reference.
46 lines
1.3 KiB
Rust
46 lines
1.3 KiB
Rust
//! A minimal recording sink a downstream consumer would author (C8 sink role,
|
|
//! C22: displayable = exactly what a sink recorded).
|
|
//!
|
|
//! It declares a single `f64` input with `Firing::Any`, an empty `output`
|
|
//! (the pure-consumer declaration), and in `eval` pushes `(now, value)` to a
|
|
//! destination it holds as a field — an out-of-graph side effect. Returns
|
|
//! `None` (records nothing into the graph).
|
|
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
|
|
use aura_core::{Ctx, Firing, InputSpec, Node, NodeSchema, Scalar, ScalarKind, Timestamp};
|
|
|
|
/// Shared, out-of-graph destination the recorder writes into.
|
|
pub type Tape = Rc<RefCell<Vec<(Timestamp, f64)>>>;
|
|
|
|
pub struct Recorder {
|
|
tape: Tape,
|
|
}
|
|
|
|
impl Recorder {
|
|
pub fn new(tape: Tape) -> Self {
|
|
Self { tape }
|
|
}
|
|
}
|
|
|
|
impl Node for Recorder {
|
|
fn schema(&self) -> NodeSchema {
|
|
NodeSchema {
|
|
inputs: vec![InputSpec {
|
|
kind: ScalarKind::F64,
|
|
lookback: 1,
|
|
firing: Firing::Any,
|
|
}],
|
|
output: vec![], // pure consumer (sink)
|
|
}
|
|
}
|
|
|
|
fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Scalar]> {
|
|
if let Some(v) = ctx.f64_in(0).get(0) {
|
|
self.tape.borrow_mut().push((ctx.now(), v));
|
|
}
|
|
None
|
|
}
|
|
}
|