Ring-buffered diffs (N=32). Robust to missed pulses and split messages; negative diffs (out-of-order) filtered.
Fixed grid, created once per stream. Jitter tolerance = period/2 via rounding. Preserved across batch-length changes.
Close when a message lands in the last slot — not when N messages arrive. Missing pulses don't block; splits don't trick.
Logical clock = max observed timestamp. Timeout closes at 1.2 · batch_length. New HWM clamped toward start + 3·batch_length (old value preserved for monotonicity) — self-heals in a few closes; one far-future outlier can't DoS.
Steady stream; every batch closes exactly on the last slot.
Pulse 7 never arrives. Pulse 14 lands in last slot → close (count-based would hang).
Duplicate timestamp at slot 2 doesn't inflate slot count; last-slot check is what matters.
Early arrival past the last slot is queued and routed into the next batch after close. Only gridded gated streams enter _overflow; ungridded streams (non-gated or sub-Hz gated) past window.end take the separate hold-back path (see below).
Batch closes only when every gated stream has filled its last slot.
Stream starts 0.04 s into the window. PulseGrid origin picks a real pulse; all 14 slots fit.
No messages for 5 batches. Instead of emitting 5 empty placeholders, window advances to where traffic resumes.
Only partial traffic this window, but a later non-gated message advances HWM past 1.2·batch_length → close.
Log messages bypass the slot grid — no rate estimation, no slot routing. Msgs with ts ≤ window.end join the active batch; msgs ahead of the window are held for the next one (same path sub-Hz gated streams take — see below). Det’s slot gate closes each batch normally.
0.5 Hz monitor: rate too low to build a grid. No slot routing, no overflow stashing; msgs with ts ≤ window.end join the active batch and msgs ahead of the window are held for the next one (same path as non-gated — see below). Det’s slot gate closes every batch.
Ungridded msg (non-gated, or sub-Hz gated) with ts > window.end sits in _future and re-routes at close, so a future-ts reading doesn’t mis-correlate with this batch’s detector data. Capped at 3·batch_length ahead: beyond that the ts is implausibly far and the msg falls through to the active batch — a bad epoch can’t cache messages indefinitely.
Bifrost regression: monitor with ~43 year offset. Grid origin too far → refused; good stream closes as normal.
One message ~1 year ahead used to pin HWM → millions of empty closures. Clamped to start + 3·batch_length; self-heals.
New stream gates after MIN_DIFFS_FOR_GATE convergence. Absent for 5 batches → evicted. Can re-join later.
set_batch_length(2s) applies at next close. Grid origin preserved; slots_per_batch updates (14 → 28).
Slots have ±period/2 jitter budget via round(). At ~30 ms (42% of period at 14 Hz) batch continuity holds, with occasional ±1 slot drift.