Files
Brummel 90e298d3b4 fieldtest: cycle-0008 — 5 examples, 6 findings
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.
2026-06-04 18:21:57 +02:00

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
}
}