feat(sim): Dev Mode — pool clip picker + live analysis under one toggle

Adds a single "Dev Mode" switch at the bottom of the control panel (off by
default, persisted in localStorage). When on, a panel appears below it with all
dev controls and data; when off the default view is just the experience + the
three control fieldsets.

Dev panel:
- Pool picker: a <select> of the current altitude's pool members — choose any
  clip to override the random landing pick; a "Re-roll random" button re-runs
  the server-canonical pick. Rebuilds on each landing.
- Active clip metadata (id, title, source, license, base_file).
- Annotations & affect: keys with salience/window/tier counts (read-only;
  editing stays in /author.html).
- RenderPlan JSON — relocated here from the always-visible panel.
- Cache & playback: preload status (N/total), memory-vs-network for the active
  clip, live resolution/duration/loop position.

Frontend only — no API changes; everything is read from data the renderer
already has. Factored pickRandomMember() out of landScale() so the initial
landing, ring advances, and the re-roll button share one pick.

Design: docs/superpowers/specs/2026-06-25-simulator-dev-mode-design.md
269 passed, 2 skipped; node --check + server smoke clean. Frontend behavior
to be verified by-eye by the operator (no headless browser in repo yet).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-06-25 08:19:17 -07:00
parent ab2edf77bb
commit a1c9c5c760
4 changed files with 273 additions and 11 deletions
@@ -0,0 +1,64 @@
# Simulator Dev Mode — design
> 2026-06-25 · session 0017 · planning-and-executing
> Anchor: leaf task (simulator dev-tooling — R2b, no upstream design needed)
## Problem
While previewing the experience, the operator can't choose *which* clip in an
altitude's rotating pool is shown — the server picks a random pool member on each
landing (content-pipeline §11.1). For by-eye review they want to step through every
clip in a pool on demand, and to see the engine/clip data behind what's on screen.
The RenderPlan JSON readout is also always visible, cluttering what should read as
an audience-facing preview.
## Goal
A single **Dev Mode** toggle at the bottom of the control panel. Off by default
(persisted in `localStorage`). When on, a panel appears directly below it with all
dev controls and data; when off, the normal view is just the experience + the three
control fieldsets.
## Scope
Frontend only — `simulator/static/{index.html,app.js,style.css}`. No API changes:
every datum Dev Mode shows is already available client-side (`/api/clips`,
`/api/ring`, the `/api/alteration` response, and the in-memory preload cache). The
separate `/author.html` remains the place to *edit* labels; Dev Mode is
inspect-and-select only.
## Dev panel contents (top → bottom)
1. **Pool picker** — a `<select>` of the current altitude's pool members
(`clip_id · title`), its value reflecting the clip currently playing. Choosing a
member sets `activeClipId` to it and reloads the base media, overriding the random
landing pick. A `🎲 Re-roll random` button re-runs the server-canonical random
pick for the current scale. The dropdown is rebuilt on every scale landing, so it
always lists the current pool; single-clip scales (cosmos/orbit) show one entry.
2. **Active clip**`id`, `title`, `source`, `license`, `base_file`.
3. **Annotations & affect** — each annotation key with salience (or legacy
`min_level`), time window (`appear``disappear`), and tier count; each affect key
with `min_level` and tier count. Read-only.
4. **RenderPlan** — the relocated `#readout` (the `/api/alteration` JSON).
5. **Cache & playback** — preload status (`N/total cached`), whether the active
clip is served **from memory** (a preloaded blob) vs the network, and live video
resolution / duration / loop position.
## Behavior
- **Pool override is per-landing.** Changing altitude re-rolls randomly as today;
the operator then picks again from the new scale's pool. The pick is not persisted
across landings (YAGNI — "select any video while on an altitude").
- **Refresh points.** Pool picker + clip metadata + annotations rebuild whenever the
scale lands (hooked into `renderScaleReadout()`) and after a manual pick/re-roll.
The RenderPlan readout updates on each `update()` (knob change) as today. The
cache/playback stats refresh on a light interval (~500 ms) while Dev Mode is on,
and the cache count also ticks up as the preloader fills.
- **`pickRandomMember()`** is factored out of `landScale()` so the initial landing,
ring advances, and the Re-roll button all share the one server-canonical pick.
## Testing
No JS unit harness in the repo and no API change, so verification is: `node --check`
on `app.js`, the Python suite stays green (unaffected), and by-eye review by the
operator (no headless browser yet — §9 E2E policy noted, machinery pending).