Telemetry
Each frame the UI polls a telemetry snapshot from the simulation. The shape of that snapshot is a contract shared between Rust (which produces it) and TypeScript (which consumes it), and it is locked by a Vitest test.
EXEPERT has two independent telemetry paths that both drive the brain:
- Simulation telemetry (this page): per-frame snapshots from the Rust
World.telemetry_snapshot(), polled by the UI for activity meters, response indices, and region bars. - Observability telemetry (Plan 007, see
Frontend modules): trace/span data from
Supabase, mapped into
TelemetrySnapshotsignals bysrc/map/trace-to-brain.tsand replayed through the brain bysrc/map/live-brain.ts(Realtime bridge),map/session-rollup.ts(replaySession), and the compare-mode diff grid. Region health is additionally tinted by eval annotations (annotationsToRegionScores).
Both paths feed into the same TelemetrySnapshot shape so the brain UI
doesn't need to distinguish the source.
The snapshot contract
The TypeScript type is the source of truth for the shape:
export interface TelemetrySnapshot {
activeSignals: number
firedThisFrame: number
releasedSignalsThisFrame: number
signalVelocityMean: number
corticalResponseIndex: number
onsetResponse: number
sustainedActivation: number
focusDrift: number
regionActivity: Record<RegionKey, number>
rolling: {
oneSec: RollingWindow
threeSec: RollingWindow
fifteenSec: RollingWindow
}
}
export interface RollingWindow {
activeSignalsAvg: number
firedNeurons: number
releasedSignals: number
signalVelocityMean: number
regionActivity: Record<RegionKey, number>
}
The Rust telemetry_snapshot() serializes a matching structure with
serde-wasm-bindgen, so the JavaScript side receives a plain object that
satisfies TelemetrySnapshot:
getTelemetrySnapshot(): TelemetrySnapshot {
return this.world.telemetry_snapshot() as TelemetrySnapshot
}
src/__tests__/telemetry-contract.test.ts asserts the snapshot shape. If the
Rust serialization and the TypeScript type drift apart, pnpm test fails. Keep
both in sync when adding a field.
Fields
Instantaneous (this frame):
| Field | Meaning |
|---|---|
activeSignals | Currently live signals. |
firedThisFrame | Neurons that fired this frame. |
releasedSignalsThisFrame | Signals released this frame. |
signalVelocityMean | Mean travel speed of live signals. |
regionActivity | Per-region activity counts. |
Derived response metrics:
| Field | Meaning |
|---|---|
corticalResponseIndex | Headline "responsiveness" score. |
onsetResponse | Sharpness of response right after a stimulus. |
sustainedActivation | Activity held over time. |
focusDrift | How much the dominant region wanders. |
Rolling windows
Three rolling windows: oneSec, threeSec, fifteenSec: accumulate averages
over their respective time spans. Each RollingWindow carries an average active
signal count, fired-neuron and released-signal counts, mean velocity, and
per-region activity. The UI uses these to draw the activity-window bars.
Active load and the response index
The response index is built partly from "active load": how full the pool is relative to the live cap:
let active_load =
(self.pool.active_count() as f32
/ self.settings.current_max_signals.max(1) as f32) * 100.0;
Because this normalizes against current_max_signals (default 16_000), the
load percentage moves as you change the max-signals setting. The UI's activity
bars normalize against the configured cap as well, so they read as a fraction of
capacity rather than an absolute count.