perf-test-mode

PURPOSE

Hidden stress-test gauntlet activated by ?perftest URL param. Boots the full engine under cranked entity counts and an indefinite auto-restart loop, then ships periodic perf snapshots to Supabase player_events for overnight soak testing, A/B render-config comparison, and memory-leak / context-loss detection.

OWNS

  • Module-scoped session state: _sessionId, _snapshotIdx, _intervalHandle, _runCount, _runStartMs, _deviceCtx, _activeFlags.
  • SNAPSHOT_INTERVAL_MS constant (telemetry cadence).
  • DeviceContext interface (UA, screen, dpr, effectiveDpr, cores, memGB, isMobile).
  • The stress-tuned RunDefinition factory buildPerfTestRunDef().
  • Internal helpers captureDeviceContext(), captureActiveFlags(), sendSnapshot().

READS FROM

  • ../engine/core/configPERF_FLAGS (incl. perftest, isMobile, dprOverride, render-disable toggles), PERF_VERSION_TAG, BUILD_VERSION.
  • ../engine/core/render-diagdiagGetPerfSnapshot(), diagDrainSpikes().
  • ../data/run-configRunDefinition type, DEFAULT_RUN baseline (spread + overridden).
  • Browser globals: navigator.userAgent, navigator.hardwareConcurrency, (navigator as any).deviceMemory, window.innerWidth/innerHeight, window.devicePixelRatio, performance.now(), (performance as any).memory (Chrome/Edge only).

PUSHES TO

  • ../../metagame/services/analyticstrackEvent() with three event types:
    • perftest_start — once at session boot; includes device + active flags snapshot.
    • perftest_snapshot — every 30 s; includes sessionId, snapshotIdx, runNumber, runElapsedSec, version tags, device, flags, full perf snapshot, spikeCount, top-5 worstSpikes by frameMs, and JS heap stats when available.
    • perftest_end — on stopPerfTestSession(); includes totals + a final snapshot flush.

DOES NOT

  • Does not read or write game state directly (no engine ECS, no Zustand stores).
  • Does not own the auto-restart loop — that lives in the screen/runner that calls notifyPerfTestRunStart().
  • Does not own the URL-param parsing — PERF_FLAGS.perftest is set elsewhere in engine/core/config.
  • Does not gate itself: isPerfTestMode() only reports the flag; callers decide whether to wire start/stop.
  • Does not retry, batch, or buffer telemetry — pure fire-and-forget through trackEvent.
  • Does not throttle or cap session length; runs indefinitely until stopPerfTestSession().
  • Does not pin or tag the run with a player/user id (Supabase wiring is upstream of trackEvent).

Signals

  • _intervalHandle is the re-entrancy guard for startPerfTestSession() — second call is a no-op.
  • Memory block under (performance as any).memory is Chrome/Edge-only; the snapshot ships memory: null on other engines (no try/catch — the guard is the truthy check).
  • Spike sort is destructive (mutates the array returned by diagDrainSpikes()); drain semantics mean spikes are consumed per snapshot.
  • worstSpikes is sliced to 5 entries to keep payload size bounded.
  • runElapsedSec is per-run (resets on notifyPerfTestRunStart()), not per-session; session age must be derived from snapshotIdx * 30.
  • _sessionId format: perftest_<epoch-ms>_<6-char-base36> — groups all snapshots from one boot.

Entry points

  • startPerfTestSession(): void — boots session id, device/flag capture, fires perftest_start, starts 30 s interval. Idempotent via _intervalHandle guard.
  • notifyPerfTestRunStart(): void — increments _runCount, resets _runStartMs. Caller invokes on each auto-restart after death.
  • stopPerfTestSession(): void — clears interval, flushes a final sendSnapshot(), emits perftest_end. Component-unmount hook.
  • isPerfTestMode(): boolean — thin wrapper over PERF_FLAGS.perftest.
  • buildPerfTestRunDef(): RunDefinition — returns a DEFAULT_RUN-spread RunDefinition with stress overrides (enemy multiplier, fast spawn ramp, infinite survive timer, beefed ship). Callers feed this into the normal run pipeline instead of a real mission def.

Pattern notes

  • Module-singleton state pattern: all session state is file-scoped let variables, not a class. Reflects single-active-session assumption — only one perftest runs per tab.
  • Snapshot is the unit of telemetry; the device + flag context is captured once at session start and re-attached to every snapshot for A/B grouping at query time.
  • RunDefinition overrides use the spread-merge pattern (...DEFAULT_RUN, ...DEFAULT_RUN.node, etc.) — depends on DEFAULT_RUN shape staying stable; adding new required fields to RunDefinition requires no change here, removing or renaming does.
  • version and versionTag are stamped on every event so cross-build comparisons survive a BUILD_VERSION bump mid-session in the dataset.
  • Curve shape (quantity/quality) is hand-tuned for fast stress ramp, not balance — kept in code, not the data tables, because perftest is a diagnostic surface, not a player-facing mission.

EXTRACT-CANDIDATE

  • The capture-once / attach-per-event pattern (device context + active flags stamped on every telemetry event in a session) recurs anywhere a long-running diagnostic stream needs grouping. If a second long-soak telemetry surface appears, lift captureDeviceContext() + captureActiveFlags() into a shared engine/core/diag-context.ts.
  • The (performance as any).memory JS-heap probe is a generic Chrome-only diagnostic; if any other module starts shipping heap stats, extract into engine/core/render-diag alongside the existing perf snapshot helpers.