handoff: the seams after the module split, what the split left alone, the harness modes

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dhu3iHseRewQ2WpBSVLokG
This commit is contained in:
teahubxyzandClaude Fable 5.1 committed 2026-10-21 22:35:15 +00:00
1 parent 2b2936a831
commit 4aa94d8315
1 file changed
+17 -5
+17 -5
View File
@@ -5,10 +5,20 @@ do. Read ARCHITECTURE.md first.
## Where the seams are ## Where the seams are
- `index.html` is ~1,900 lines in one script. It grew feature by feature in one long session. The sections are - The script is split into ES modules (ARCHITECTURE.md › File map) by moving lines, not rewriting them: state is still
commented and ordered sensibly, but state is one global `state` object and functions reach into each other freely. one `state` object (`core.js`) and functions still reach into each other, now through imports. `player.js` is the
only file that touches `state.audio`, so it is the only file that needs the dev-branch-and-iPhone treatment
(exceptions, deliberately left: the station sheet's volume slider sets `state.audio.volume`; the trail/tick/reaction
code in `ui/player.js` reads `currentTime`/`duration` and seeks on a tick tap; `playTape` and `step` pause before
`playJob`; brain.js pauses from the taste view).
- Modules are strict mode; the old script was sloppy. Nothing relied on it (no undeclared assignments), but a new
`x = 1` without a declaration now throws instead of making a global.
- brain.js plugs into the page through `hooks` (`djPrompt`, `jobMade`, `stationOpened`, `reacted`); it no longer - brain.js plugs into the page through `hooks` (`djPrompt`, `jobMade`, `stationOpened`, `reacted`); it no longer
overrides page functions by assignment. Anything else loaded after the page should do the same. overrides page functions by assignment. It is a classic script loaded with `defer` after the module, and the names
it uses are put on `window` by `app.js` (`state`, `hooks`, `$`, `db`, `playJob`, `drawCover`, `hash32`, `prng`,
`keyHue`, `KEYS`, `reactionWeight`, `drawTaste`, `openTaste`, `reactionEmoji`, and `seq` as an accessor onto
`core.ids.seq`, the job counter). Anything else loaded after the page should do the same, or go through `window.g`.
- `venice()` goes through an injectable fetcher (`render.setFetcher`); the harness fragments stub it there.
- Playback is one small state machine (`state.phase`: idle → cooking → holding → playing → ended) with named - Playback is one small state machine (`state.phase`: idle → cooking → holding → playing → ended) with named
transitions; see ARCHITECTURE.md › The player's state machine. `state.holding` is a read-only view of the phase. transitions; see ARCHITECTURE.md › The player's state machine. `state.holding` is a read-only view of the phase.
- The crackle exists twice: as the side element (room tone) and as the player's own source (the hold). That is - The crackle exists twice: as the side element (room tone) and as the player's own source (the hold). That is
@@ -53,7 +63,7 @@ do. Read ARCHITECTURE.md first.
## What a refactor should keep ## What a refactor should keep
- Every invariant in ARCHITECTURE.md › Playback invariants, and the harness green (both modes) before and after. - Every invariant in ARCHITECTURE.md › Playback invariants, and the harness green (all three modes) before and after.
- The sheet as the unit (everything derives from `spec`), covers as a pure function of it. - The sheet as the unit (everything derives from `spec`), covers as a pure function of it.
- One static page, no build step, no framework, no keys in the page. - One static page, no build step, no framework, no keys in the page.
- STORAGE.md's contract (chai's DB adapter targets it). - STORAGE.md's contract (chai's DB adapter targets it).
@@ -64,7 +74,9 @@ do. Read ARCHITECTURE.md first.
Anything touching `startNow / toHolding / toPlaying / bed / crackleOn / prime / setSource` counts. Features that Anything touching `startNow / toHolding / toPlaying / bed / crackleOn / prime / setSource` counts. Features that
don't touch the player (modes, DJ sheet, stats, covers) may go straight to main. don't touch the player (modes, DJ sheet, stats, covers) may go straight to main.
- Run `sh tools/harness.sh safari` as well as the Chrome modes: WebKit is the iPhone's engine (play() policy, one - Run `sh tools/harness.sh safari` as well as the Chrome modes: WebKit is the iPhone's engine (play() policy, one
audio session) and the Chrome loop mode currently hangs on hb-5. audio session). (The Chrome loop mode used to hang on hb-5: that was `--dump-dom`; it now runs over the DevTools
protocol like resume, in real time, so its lines from "track landed" on differ from the fragment's `expect` text —
compare runs against each other, not against the text.)
- iOS ignores `audio.volume`. A level is baked into the sample (`makeCrackle(seconds, gain)`), never set on the element. - iOS ignores `audio.volume`. A level is baked into the sample (`makeCrackle(seconds, gain)`), never set on the element.
- Never set `state.playing = true` before `play()` resolves. The flag follows the promise (`playNow`); a refusal shows - Never set `state.playing = true` before `play()` resolves. The flag follows the promise (`playNow`); a refusal shows
TAP TO PLAY. Setting it early gives a "playing" UI with no sound and a play button that needs two taps. TAP TO PLAY. Setting it early gives a "playing" UI with no sound and a play button that needs two taps.