The current commit's EngineTelemetry-shaped
snapshot. Read-only; the predicate does not mutate.
The merged threshold object for the active engine instance (typically DEFAULT_THRESHOLDS merged with per-engine and env-var overrides).
Snapshots from prior commits, oldest first. The predicate inspects only the tail (the last HYSTERESIS_TRIP_COUNT entries for the consecutive-trip gate, plus the full sequence for the node-count EWMA gate).
Optional rolling window of recent per-commit
wall-times (ms), oldest first. This is the #1048 / Option B
surface for the commit-shape axis: the auto-adapt wrapper's
commit() shim (landing later when createCausl({ backend: 'auto' }) integration ships per #685 / #687) records
performance.now() - commitStart after each commit and passes a
bounded slice (length ≤ thresholds.rollingCommitWindow, default
100) into this parameter. When commitTimings.length > 0 its
median takes precedence over stats.medianCommitMs (which remains
accepted for backends that pre-compute the median internally —
e.g. a future WASM backend that surfaces it directly via
EngineTelemetry). Default [] for callers that have not
yet wired the wrapper, in which case the commit-shape axis
falls back to stats.medianCommitMs ?? 0 and degrades cleanly to
the pre-#1048 behaviour.
true iff the active backend SHOULD migrate to the WASM
engine on the next commit boundary; false otherwise.
The body is two gates AND'd:
Consecutive-trip gate: the multi-axis OR (see
AdaptThresholds) must fire on each of the last
HYSTERESIS_TRIP_COUNT commits — the current stats plus
the HYSTERESIS_TRIP_COUNT - 1 most recent entries in
history. With fewer than 3 historical snapshots the predicate
short-circuits to false.
EWMA gate: the EWMA of inputs + deriveds (alpha=0.1) over
the entire history (with stats appended as the newest
observation) must exceed thresholds.nodeCount. This is the
spike-rejection band — a single 60k-node commit followed by a
30k-node steady state has consecutive-trip count 1 and EWMA
well below 50k, so it does not migrate.
Pure: no I/O, no clock reads, no process.env. The success path
allocates one array slice for the consecutive-trip window and (when
commitTimings is non-empty) one slice-and-sort for the median —
still O(n log n) on a bounded window of typically 100. Safe to call
from commit() once the wiring lands in #687.
The wrapper-side capture of commitTimings (the actual commit()
shim that records performance.now() - commitStart and bounds the
ring) is intentionally deferred to the integration PR that ships
createCausl({ backend: 'auto' }) per #685 / #687. This module
lands the decision-side signature only; the production capture
lives in the wrapper layer alongside the backend-selection state.
The pure auto-adapt decision predicate.