# Session 0023.0 — Transcript

> App: human-experience-filter-art
> Start: 2026-06-26T09-05 (PST)
> End: 2026-06-26T09-20 (PST)
> Type: executing-plans (operator-driven debugging — "still can't hear" / "video didn't come on")
> Posture: yolo
> Claude-Session: e0cc053a-41fa-416d-a4f5-493f14984e75
> Status: FINALIZED

## Launch prompt (the thread)

> "I still can't hear" → (after diagnostics) operator screenshot: `audio: on, PAUSED
> (readyState 0)` and "the audio check works" with `ring=server · url=NONE` → "the
> video didn't come on when I turned it on" → after the fix: **"yes, both"** (video
> and audio confirmed working).

## Pre-state

- Sessions 0020–0022 had shipped the audio/video feature + three "Safari autoplay"
  fixes that were verified only in headless browsers and **did not work** for the
  operator. Audio was silent; this session set out to find the real cause.

## Session arc — the debugging

1. **Stop guessing; instrument.** Three blind fixes had failed because headless
   Playwright (Chromium *and* WebKit) relaxes autoplay, so the bug was never
   reproducible. Added a **live `#audio-status` readout** (surfacing the previously
   *swallowed* `play()` errors + `aud.error`) and a **native `<audio controls>` test
   player** that plays the file with zero app code involved. Confirmed the server
   already honors HTTP Range (`206`) + `audio/mpeg` — not a serving problem.
2. **The decisive readout.** Operator reported: the **native player works** (file +
   codec + browser audio all fine), but the toggle showed
   **`ring=server · url=NONE`** with `readyState 0` and no media error — meaning my
   code never set a source because `soundtrackUrl()` returned null.
3. **Root cause = a STALE uvicorn server.** The operator's Python process predated the
   0020 audio/video work, so it (a) returns `/api/ring` without the per-scale `audio`
   field, and (b) requires the old 7-way `content` — it **422s** the new
   `{visual,audio}` payload and returns `{content:{video}}` instead of
   `{render:{video:{shown}}}`. So audio had no url AND the Video toggle's POST failed
   (→ "video didn't come on"). Reloading never fixes it (only refreshes static assets,
   not the Python process).
4. **Fix = make the client resilient to a stale server** (so no restart is needed):
   - `soundtrackUrl()` falls back to a scale-id→file map (`SCALE_AUDIO_FALLBACK`,
     mirrors `build_pool_manifest.SCALE_AUDIO`).
   - `controls()` also sends a back-compat `content` field (current server ignores it;
     an old one ignores visual/audio).
   - `update()` reads "video shown" from **either** response shape.
   Verified end-to-end against a **simulated fully-stale server** (routed old
   `/api/alteration` shape + stripped ring audio) in Chromium **and** WebKit: Video on
   → video shows + soundtrack plays, zero errors. **Operator confirmed "yes, both".**
5. **Cleanup.** Tucked the audio diagnostics (status readout + native player) under the
   **Dev Mode** panel — kept for future debugging, out of the installation UI.

## Deferred decisions

_No low-confidence calls. One judgment worth noting: I added permanent client
back-compat for a pre-release internal API to unblock the operator without a server
restart. The clean long-term fix is restarting the dev server; the shim is harmless and
removes the dependency. Flagging in case a future cleanup wants to drop it once servers
are reliably current._

## Cut state

- `main` @ `6427ab4`, clean, pushed. **292 tests + 7 Playwright E2E pass** (2 skipped).
- Audio + Video experience **works and is operator-confirmed** on the real device.
- New E2E regressions: stale-ring soundtrack fallback, audio-then-video ordering.
- The operator's actual server is still stale — recommended (not required) they restart
  it so the API is current; the client no longer depends on it.

## Operator plate / lesson

- **The big lesson (recorded in memory):** headless Playwright relaxes BOTH autoplay and
  GPU compositing, so it cannot reproduce real-browser/real-server issues. When a fix
  "passes headless" but the operator still sees the bug, **instrument with on-screen
  readouts + a native control** to get their real data — don't ship another blind guess.
  Three rounds were lost to this before instrumenting.

## Next /goal

```
/goal source/compose the deferred per-altitude music layer (the reserved Audio dial position) per docs/audio-candidate-pool.md — the Audio+Video experience is working & operator-confirmed; OR re-enable white-noise as a 3rd Audio position if wanted
```
