docs(aura-std): document Add/LinComb firing policy + multi-row-per-ts shape

Resolves the two spec_gaps from the cycle-0008 fieldtest
(docs/specs/fieldtest-0008-sum-combinators.md), the same class the 0007 fieldtest
raised for SimBroker (and which e9f4352 patched there) — the combinators shipped
without the equivalent note:

  - per-input firing policy: both Add and every LinComb leg are Firing::Any
    (mode-A as-of join) — the node fires on every cycle in which any leg is
    fresh, once all legs have a value, pairing the fresh leg with the held value
    of the others; None until all present (no cold-leg-as-0.0). A consumer
    combining different-rate sources (e.g. an M5 signal with a daily-bias leg)
    needs this to predict whether the bias is held-and-combined every cycle
    (mode A, what ships) or only on rare both-fresh cycles (mode B).
  - multi-row-per-timestamp: a fired node emits one row per *cycle*, and two
    sources sharing a timestamp are two distinct cycles (C4 tie-break by source
    order), so a recorded combined stream may carry >1 row per timestamp. Correct
    and forced by C4/C5; ratified here, now stated on the surface so "one row per
    timestamp" is not silently assumed.

Doc-only change; no behaviour change. Verified: RUSTDOCFLAGS="-D warnings" cargo
doc -p aura-std clean (intra-doc links to aura_core::Firing::Any resolve); clippy
--all-targets -D warnings clean; cargo test -p aura-std 14 passed.

refs #11
This commit is contained in:
2026-06-04 18:23:04 +02:00
parent 90e298d3b4
commit 521a16e56f
2 changed files with 20 additions and 0 deletions
+10
View File
@@ -6,6 +6,16 @@ use aura_core::{Ctx, FieldSpec, Firing, InputSpec, Node, NodeSchema, Scalar, Sca
/// Two-input f64 sum: input 0 plus input 1. Emits `None` until both inputs
/// have a value.
///
/// # Firing and warm-up
///
/// Both inputs are [`Firing::Any`](aura_core::Firing::Any) — a *mode-A as-of
/// join*: the node fires on every cycle in which either leg is fresh (once both
/// have produced a value), pairing the fresh leg with the held value of the
/// other. Until both legs have a value it emits `None` (no cold-leg-as-`0.0`).
/// With heterogeneous sources sharing a timestamp (same `ts` from two sources =
/// two distinct cycles, C4), a fired node emits one row per *cycle*, so a
/// recorded combined stream may carry more than one row per timestamp.
pub struct Add {
out: [Scalar; 1],
}
+10
View File
@@ -13,6 +13,16 @@ use aura_core::{Ctx, FieldSpec, Firing, InputSpec, Node, NodeSchema, Scalar, Sca
/// construction parameters that configure the node and fix its arity
/// (`weights.len()` inputs, in slot order). Emits `None` until *all* inputs
/// have a value.
///
/// # Firing and warm-up
///
/// Every input is [`Firing::Any`](aura_core::Firing::Any) — a *mode-A as-of
/// join*: the node fires on every cycle in which any leg is fresh (once all
/// legs have produced a value), pairing each fresh leg with the held value of
/// the others. Until every leg has a value it emits `None` (no cold-leg-as-
/// `0.0`). With heterogeneous sources sharing a timestamp (same `ts` from two
/// sources = two distinct cycles, C4), a fired node emits one row per *cycle*,
/// so a recorded combined stream may carry more than one row per timestamp.
pub struct LinComb {
weights: Vec<f64>,
out: [Scalar; 1],