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
BossGauntletTabdefault-exported React component — top-level Playground tab.- Local UI state:
mode(BossGauntletMode),progress(liveBossGauntletProgressfrom runner),runningboolean,singleBossselection,finalReport(BossGauntletRunReport),startedAtRef(performance-time anchor). - Mode picker constants
MODE_ORDERandMODE_COLORSmapping 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). BossRowsubcomponent — per-boss status row with icon, name, duration, fail reasons, and one-line telemetry summary.HpSparklinesubcomponent — canvas-based sparkline that draws three lines (bar HP red, sharing-count green, player HP blue) fromreport.telemetry.SummaryCellsubcomponent — single labelled big-number cell used in the final-summary grid (passed / failed / skipped / errors).placeholderReport()factory that produces a zeroedBossGauntletReportinqueuedstatus for each boss before a run starts.- Local style objects
labelStyleandselectStyle. - 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 typesBossGauntletMode,BossGauntletProgress,BossGauntletReport,BossGauntletRunReport.engine/testing/boss-gauntlet-runner: importsrunGauntlet,runSingleBoss,cancel,setMissionRef,setProgressListenerto drive and observe the runner.data/bosses(BOSS_DEFS) for each boss’sdisplayNamein dropdown options and row labels../PlaygroundShared:BTN_STYLE,Panel, andTabProps(which providesmissionRef).- 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 streamsBossGauntletProgresssnapshots into local state.runGauntlet(mode)when the big launch button is clicked while idle; awaits theBossGauntletRunReportand stores it infinalReport.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
runningflag that disables the mode buttons, single-boss picker, and single-RUN button during a run. - Does not expose the
__bossGauntletconsole 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
progressupdates (viasetProgressListener) drive the live yellow “current boss” card, the per-boss row highlight, and the progress bar width.progress.reportsprovides the working list of per-boss reports while a run is in flight; once the run resolves,finalReport.reportstakes over.progress.currentBossId,progress.currentIndex,progress.total, andprogress.elapsedSecare read directly to render the current-fight banner.report.statusis mapped to color and icon via theSTATUS_COLORS/STATUS_ICONStables inBossRow.report.telemetry(≥2 samples) triggers anHpSparklinerender; the canvas effect re-draws on everyreportchange.report.failReasonsis rendered as a red bullet list under non-passed rows.finalReport.passed / failed / skipped / errors / totalRuntimeSecfeed the summary grid and total wall-clock line.runningstate 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 (seecode/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 offrunGauntlet/runSingleBoss/cancel, and otherwise just renders whatever snapshots arrive. - Effect lifecycle is single-instance: the
useEffectkeyed onmissionRefre-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
BossRowentries renders againstprogress?.reports ?? finalReport?.reports ?? []. When neither exists,placeholderReport()is mapped overBOSS_GAUNTLET_ORDERso the queue is always visible at full length even before a run starts. currentIdxis computed fromprogress?.currentIndex ?? -1; thei === currentIdxtest 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
onClickswitches betweenhandleRunandhandleCancelpurely onrunning, 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.
HpSparklineredraws inside auseEffectkeyed on the fullreportobject. 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/playgroundin avoiding CSS module overhead.BTN_STYLEandPanelcome fromPlaygroundSharedto keep visual consistency across tabs. - Mode and per-boss controls are disabled while
runningto prevent mid-run config changes; the runner is not expected to handle live re-configuration.