schema.runtime
Turns the pure-data specs of com.eignex.kumulant.schema into live, running accumulators. The specs in the parent package say what to build; this package builds it, groups the results, and fans every update out to the right stat. Nothing here goes on the wire: it is the materialization and grouping layer that sits between a decoded schema and the live stats it stands up.
Materialization
Every spec materializes through one of the modality-specific extension functions, each of which takes a Concurrency mode and returns a live stat of the matching modality. The concurrency mode is the only input that isn't already on the spec; it is the deployment knob passed in at build time, so the same decoded payload can run unsynchronized in a test and strictly synchronized in a contended hot loop.
Declaring and reading a group
StatSchema is the declarative entry point. A schema subclass registers its stats through the series, paired, vector, and discrete delegates, and each delegate hands back a com.eignex.kumulant.schema.StatKey carrying the result type. The schema materializes into a StatGroup, which is itself a series stat over com.eignex.kumulant.schema.GroupResult: it forwards every update to all of its entries and collects their snapshots into one result, read back by typed key rather than by string. The PairedStatGroup, VectorStatGroup, and DiscreteStatGroup variants fan updates only to entries of their own modality.
A schema flattens to its pure-data form as a StatSchemaDef, which is the part that crosses the wire. A coordinator can stand up the same group from a definition, run it independently, and fold workers' snapshots back in through merge.
Building groups by hand
When an aggregation isn't wire-expressible, for instance a filter-wrapped stat that depends on a live lambda, the group can be assembled directly from name-to-stat pairs rather than from a schema. The list-stat builders behind ListStats and its modality variants do exactly this, and BoundStat is the name-plus-stat pairing they collect. This bypasses the schema layer entirely, trading wire portability for the freedom to hold a live stat the spec vocabulary can't describe.
Types
Internal base shared by StatGroup, PairedStatGroup, and VectorStatGroup. Holds the spec list and provides the modality-agnostic read / merge / reset implementations.
StatGroup variant over discrete (Long) inputs.
Marker interface for stats whose result is a GroupResult. Implemented by StatGroup and its modality variants. Used in the group declarator below to enforce that nested-group entries actually produce GroupResult rather than some other Result shape.
StatGroup variant over paired (x, y) inputs.
Fans each update out to a heterogeneous list of SeriesStats and reports their results keyed by name.
Declarative, typed schema for a group of stats, layered on top of com.eignex.skema.Schema<StatSpec> so the entries map is wire-serializable.
Pure-data, serializable form of a StatSchema. The wire field is stats (kumulant convention, customised over skema's default entries). Decode an incoming wire payload into this type and rehydrate a live group via materializeSeries (or one of the modality-specific variants).
StatGroup variant over vector inputs.
Functions
Build a nested-group BoundStat. build is invoked with keys (the sub-schema's key handle) and must return a GroupedStat; typically a StatGroup constructed against that sub-schema. The resulting BoundStat uses a GroupStatKey so dotted lookup result[outerKey][innerKey] compiles.
Materialize every entry, regardless of modality. Caller filters by stat type.
Construct a live DiscreteStat from a DiscreteStatSpec. See SeriesStatSpec.materialize.
Construct a live PairedStat from a PairedStatSpec. See SeriesStatSpec.materialize.
Construct a live RegressionStat from a RegressionStatSpec. See SeriesStatSpec.materialize.
Construct a live SeriesStat from a SeriesStatSpec. One when per modality, one cast at the boundary - sealed-hierarchy exhaustiveness keeps the cast safe.
Construct a live stat from any StatSpec, dispatching on its modality. Useful for code paths (like StatSchemaDef.materialize) that iterate over an erased Map<String, StatSpec> and don't statically know the modality.
Construct a live VectorStat from a VectorStatSpec. See SeriesStatSpec.materialize.
Materialize discrete-modality entries only; throws if any entry isn't discrete.
Materialize paired-modality entries only; throws if any entry isn't paired.
Materialize series-modality entries only; throws if any entry isn't series.
Materialize vector-modality entries only; throws if any entry isn't vector.
Build a BoundStat from an existing StatKey and a live stat. Used when the key was created elsewhere (e.g. via a StatSchema declarator) and you want to pair it with a specific live instance; common in tests and in materializer code paths.
Build a BoundStat from a string name and a live stat. Convenience for ad-hoc groups: BoundStat(StatKey(name), value) with type inference.