PURPOSE

Ship Playground tab that drives the sequenced boss tester. Lets a dev spawn each of the 8 bosses in order (or a single boss) under a configured mode, watch the fights play out live in the mission canvas, and read pass/fail plus telemetry per encounter. The tab is the React-side UI; the actual run loop lives in engine/testing/boss-gauntlet-runner.ts.

OWNS

  • BossGauntletTab default-exported React component — top-level Playground tab.
  • Local UI state: mode (BossGauntletMode), progress (live BossGauntletProgress from runner), running boolean, singleBoss selection, finalReport (BossGauntletRunReport), startedAtRef (performance-time anchor).
  • Mode picker constants MODE_ORDER and MODE_COLORS mapping each mode to a swatch (smoke green, balance orange, survival blue, stress red).
  • Per-status color and icon tables STATUS_COLORS, STATUS_ICONS (queued, running, passed, failed, skipped, error).
  • BossRow subcomponent — per-boss status row with icon, name, duration, fail reasons, and one-line telemetry summary.
  • HpSparkline subcomponent — canvas-based sparkline that draws three lines (bar HP red, sharing-count green, player HP blue) from report.telemetry.
  • SummaryCell subcomponent — single labelled big-number cell used in the final-summary grid (passed / failed / skipped / errors).
  • placeholderReport() factory that produces a zeroed BossGauntletReport in queued status for each boss before a run starts.
  • Local style objects labelStyle and selectStyle.
  • Estimated-runtime heuristic: BOSS_GAUNTLET_ORDER.length × cfg.timeoutSec × 0.7.

READS FROM

  • engine/testing/boss-gauntlet-types: BOSS_GAUNTLET_MODE_CONFIGS, BOSS_GAUNTLET_ORDER, and types BossGauntletMode, BossGauntletProgress, BossGauntletReport, BossGauntletRunReport.
  • engine/testing/boss-gauntlet-runner: imports runGauntlet, runSingleBoss, cancel, setMissionRef, setProgressListener to drive and observe the runner.
  • data/bosses (BOSS_DEFS) for each boss’s displayName in dropdown options and row labels.
  • ./PlaygroundShared: BTN_STYLE, Panel, and TabProps (which provides missionRef).
  • Browser performance.now() for the local start-time stamp.

PUSHES TO

  • setMissionRef(missionRef) on mount — hands the runner the live mission ref so it can spawn/cancel against the current canvas.
  • setProgressListener(p => setProgress(p)) on mount; setProgressListener(null) on unmount — installs/removes the progress callback that streams BossGauntletProgress snapshots into local state.
  • runGauntlet(mode) when the big launch button is clicked while idle; awaits the BossGauntletRunReport and stores it in finalReport.
  • runSingleBoss(singleBoss, mode) when the single-boss RUN button is clicked.
  • cancelGauntlet() when the launch button is clicked while a run is active (button doubles as cancel).

DOES NOT

  • Does not spawn bosses, advance time, or run any fight loop itself — all simulation is in the runner.
  • Does not configure player stats, gear, or world state — that is handled inside the runner per BOSS_GAUNTLET_MODE_CONFIGS.
  • Does not own boss definitions, mode configs, or the canonical boss order; only consumes them.
  • Does not persist reports to disk, network, or telemetry — the displayed summary is in-memory for the current session.
  • Does not gate or validate inputs beyond the running flag that disables the mode buttons, single-boss picker, and single-RUN button during a run.
  • Does not expose the __bossGauntlet console hooks — the footer just documents the runner’s console API; the hooks themselves are registered elsewhere.
  • Does not render bosses or the mission canvas — visuals come from the live mission scene the tab is hosted alongside.

Signals

  • progress updates (via setProgressListener) drive the live yellow “current boss” card, the per-boss row highlight, and the progress bar width.
  • progress.reports provides the working list of per-boss reports while a run is in flight; once the run resolves, finalReport.reports takes over.
  • progress.currentBossId, progress.currentIndex, progress.total, and progress.elapsedSec are read directly to render the current-fight banner.
  • report.status is mapped to color and icon via the STATUS_COLORS / STATUS_ICONS tables in BossRow.
  • report.telemetry (≥2 samples) triggers an HpSparkline render; the canvas effect re-draws on every report change.
  • report.failReasons is rendered as a red bullet list under non-passed rows.
  • finalReport.passed / failed / skipped / errors / totalRuntimeSec feed the summary grid and total wall-clock line.
  • running state controls the launch button’s label/colors (RUN GAUNTLET vs CANCEL), disables the mode and single-boss controls, and dims inactive mode buttons.

Entry points

  • Default export BossGauntletTab({ missionRef }: TabProps) is registered as a tab inside the Ship Playground host (see code/screens/playground).
  • Console API documented at the bottom of the panel: __bossGauntlet.run('smoke') and __bossGauntlet.runSingle('killer_croc', 'balance'). The tab itself does not register these handles; they come from the runner module.

Pattern notes

  • The runner is the source of truth. The tab is a thin view + control surface: it subscribes via setProgressListener, fires off runGauntlet / runSingleBoss / cancel, and otherwise just renders whatever snapshots arrive.
  • Effect lifecycle is single-instance: the useEffect keyed on missionRef re-binds the mission ref and progress listener whenever the host swaps the ref. The cleanup nulls the listener so a stale closure cannot keep pushing state into an unmounted tab.
  • The list of BossRow entries renders against progress?.reports ?? finalReport?.reports ?? []. When neither exists, placeholderReport() is mapped over BOSS_GAUNTLET_ORDER so the queue is always visible at full length even before a run starts.
  • currentIdx is computed from progress?.currentIndex ?? -1; the i === currentIdx test in the map sets the row’s yellow highlight without needing the runner to broadcast a separate “active” flag.
  • The single launch button doubles as cancel — its onClick switches between handleRun and handleCancel purely on running, and the visual treatment (red border + ■ glyph vs mode-colored border + ▶ glyph) reflects state.
  • Estimated time is intentionally a rough heuristic (70% of total mode timeout budget) so the label stays informative without claiming false precision.
  • HpSparkline redraws inside a useEffect keyed on the full report object. It uses raw 2D canvas calls (no chart lib) and normalises each series against its own initial-sample max so the three lines share the 28px plot.
  • All sizing, colors, and typography are inlined as style objects — the tab follows the rest of screens/playground in avoiding CSS module overhead. BTN_STYLE and Panel come from PlaygroundShared to keep visual consistency across tabs.
  • Mode and per-boss controls are disabled while running to prevent mid-run config changes; the runner is not expected to handle live re-configuration.