The pure functional core of the metrics collector: folds normalized bloccs
telemetry into per-node rolling windows and renders a snapshot. No processes,
no clock of its own — now (monotonic ms) is always passed in, so the whole
thing is deterministically testable with synthetic events.
Normalized events (produced by Bloccs.Web.Telemetry.Handler):
{:start, node}— a message entered the node{:stop, node, duration_ms, :ok | :failed}— it finished{:exception, node}— it raised{:event, node, kind}— retry / skipped / dropped / dispatch_error
A node's state (:idle | :running | :ok | :failed) drives the topology
glyph; the windowed stats drive the metrics panel.
Summary
Functions
Render a {node => view} snapshot, pruning samples older than the window.
Types
@type node_state() :: %{ completed: non_neg_integer(), errors: non_neg_integer(), events: non_neg_integer(), samples: [{integer(), number()}], state: :idle | :running | :ok | :failed, last_at: integer() | nil }
@type node_view() :: %{ state: atom(), completed: non_neg_integer(), errors: non_neg_integer(), error_rate: float(), throughput: float(), p50: number() | nil, p95: number() | nil }
@type t() :: %{optional(atom()) => node_state()}