Go to file
Byron Gamatos 8d0e270345
refactor(app): carve the JUCE/desktop audio shims out of app.js (R3a) (#893)
* refactor(app): carve resume-session out of app.js (R3a)

static/js/resume-session.js (157) — the snapshot taken when you leave a song and the
pill that offers it back. Bodies VERBATIM. app.js 7,727 → 7,601.
Fifth slice out of the strongly-connected core. ONE hook (playSong) + a
currentFilename getter.

S.pendingResume JOINS THE CONTAINER — on demand, exactly as intended. app.js WRITES
it (playSong({ resume }) arms it; the song:ready listener consumes it) while this
module reads it, so it cannot be a plain export: an imported binding is read-only.
Same reason isPlaying is there. The container grows one field per carve that needs
it, never speculatively.

THE CONTRACT TEST CAUGHT THE MISSING HOOK, again on a path nothing executes:
"playSong is read by a module but never wired by app.js — it would throw at runtime".
Second time it has caught a real wiring gap the moment it appeared.

A REAL TRAP, worth remembering: I first did the S.pendingResume rewrite by feeding
acorn's identifier RANGES from node into python, and it corrupted the file
(`_pS.pendingResume null;`). **Acorn's offsets are UTF-16 code units; Python's string
indices are code points.** static/app.js contains emoji, so every offset past one
drifts. Do an AST-driven rewrite in the SAME language that produced the offsets.
`node --check` caught it; a silent version of that bug is very easy to imagine.

VERIFIED. A/B against origin/main in two browsers, real song: the window API
(resumeLastSession / _snapshotResumeSession / _readResumeSession /
_clearResumeSession), snapshot, read-back, and clear — IDENTICAL, zero page errors.
HONEST LIMIT: my probe never got the snapshot to actually PERSIST (there is a guard
beyond the 3s minimum position that a scripted playSong does not satisfy), so that
path is verified only as identical-to-main, not as observed-working. The real
coverage is tests/browser/resume-session.spec.ts, which drives the flow properly.

Zero harnesses broke. pytest 2396, node 1040/1040, ESLint 0 (no-cycle clean),
tailwind clean, Codex 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(app): carve the JUCE/desktop audio shims out of app.js (R3a)

static/js/juce-audio.js (994) — bodies VERBATIM. app.js 7,603 → 6,643.
THE LARGEST SINGLE SLICE of the whole carve phase: 960 lines, ~13% of what was left.

Three self-installing IIFEs:
  _installJuceEngineRoutingWatcher (444)  routes a song to the JUCE engine or HTML5 as
                                          the desktop output enters/leaves exclusive/ASIO
  _installRendererBusFeeder        (337)  feeds the highway renderer bus from whichever
                                          transport is actually running
  _installJuceAudioElementShim     (156)  patches audio.play/pause so the rest of the app
                                          keeps talking to the <audio> element while JUCE
                                          owns the transport

They EXPORT NOTHING — all three publish through `window.*` (_juceMode,
_reevaluateJuceRouting, _reevaluateRendererBus, …). So app.js needs only a
side-effect import plus the one binding it actually uses
(_resetJuceAudioShimChain, which the shim IIFE assigns).

THE ORDERING QUESTION, CHECKED RATHER THAN ASSUMED. Importing this module runs the
IIFEs EARLIER than before: imports evaluate ahead of app.js's body, and therefore
ahead of configureHost(). A hook read at IIFE-execution time would THROW. So I walked
the AST at IIFE-body depth to see what they actually touch when they run: nothing but
listener registration, and `audio.play`/`audio.pause` patching — and `audio` is itself
an imported module now. Verified in the browser: both are patched on the carved build
exactly as on main, which proves the shim installs correctly at its new, earlier point.
(Had I got this wrong, host.js throws loudly rather than silently misbehaving — which
is the whole reason it has no no-op defaults.)

VERIFIED. A/B against origin/main in two browsers: the entire window.* surface the
IIFEs publish (_juceMode, _juceOutputIsExclusive, _reevaluateJuceRouting,
_reevaluateRendererBus, _clearJuceRerouteMemo), audio.play/pause patched, a real song
loading and togglePlay driving the public mirror — IDENTICAL, zero page errors.

Harnesses: juce_engine_reroute (19 tests) + renderer_bus_feeder (13) slice the IIFEs by
signature — retargeted, and each sandbox gains a `host` object routed at its EXISTING
stubs so every assertion holds unchanged. test_plugin_runtime_idempotence is SPLIT: 3 of
its 4 source-asserts stayed in app.js, the sm.emit('song:resume') one moved.

pytest 2396, node 1040/1040, ESLint 0 (no-cycle clean), tailwind clean, Codex 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 22:44:03 +02:00
.claude/skills Clean release snapshot 2026-06-16 18:47:13 +02:00
.github R0: module-migration rails (src/ serving, live-edit cache, scriptType loading, governance) (#812) 2026-07-08 10:14:40 +02:00
.specify refactor(ui)!: remove the classic v2 shell — v3 is the only UI (R3a) (#871) 2026-07-11 16:33:03 +02:00
content/starter Delete content/starter/beethoven-ode_to_joy.feedpak 2026-07-03 23:42:45 +02:00
data/progression Clean release snapshot 2026-06-16 18:47:13 +02:00
docs refactor(ui)!: remove the classic v2 shell — v3 is the only UI (R3a) (#871) 2026-07-11 16:33:03 +02:00
lib refactor(server): extract the artist routes into routers/artist.py (R3) (#870) 2026-07-11 15:49:37 +02:00
plugins fix(highway_3d): size Butterchurn output canvas buffer to fill the highway (#820) 2026-07-10 13:12:16 +02:00
scripts perf(harness): add 2D-highway frame-time measurement + capture the R3c gate (H0) (#848) 2026-07-10 23:15:07 +02:00
specs rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
static refactor(app): carve the JUCE/desktop audio shims out of app.js (R3a) (#893) 2026-07-11 22:44:03 +02:00
tests refactor(app): carve the JUCE/desktop audio shims out of app.js (R3a) (#893) 2026-07-11 22:44:03 +02:00
.dockerignore fix(build): move appstate.py + routers/ under lib/ so the desktop app ships them (#836) 2026-07-10 17:16:34 +02:00
.gitattributes Purge external-format terminology from code, tests and docs 2026-06-16 19:36:53 +01:00
.gitignore Bundle drum_highway_3d + keys_highway_3d as in-tree core plugins (#693) 2026-07-02 00:25:31 +02:00
build-proxmox-ct.sh rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
CHANGELOG.md refactor(ui)!: remove the classic v2 shell — v3 is the only UI (R3a) (#871) 2026-07-11 16:33:03 +02:00
CLAUDE.md refactor(ui)!: remove the classic v2 shell — v3 is the only UI (R3a) (#871) 2026-07-11 16:33:03 +02:00
CONTRIBUTING.md rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
docker-compose.nas.yml rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
docker-compose.yml fix(build): move appstate.py + routers/ under lib/ so the desktop app ships them (#836) 2026-07-10 17:16:34 +02:00
Dockerfile fix(build): move appstate.py + routers/ under lib/ so the desktop app ships them (#836) 2026-07-10 17:16:34 +02:00
eslint.config.js refactor(app): carve the plugin loader out of app.js into static/js/ (R3a) (#878) 2026-07-11 18:18:00 +02:00
LICENSE Clean release snapshot 2026-06-16 18:47:13 +02:00
main.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
package-lock.json R0: module-migration rails (src/ serving, live-edit cache, scriptType loading, governance) (#812) 2026-07-08 10:14:40 +02:00
package.json R0: module-migration rails (src/ serving, live-edit cache, scriptType loading, governance) (#812) 2026-07-08 10:14:40 +02:00
playwright.config.ts Clean release snapshot 2026-06-16 18:47:13 +02:00
pyproject.toml Clean release snapshot 2026-06-16 18:47:13 +02:00
requirements-test.txt Clean release snapshot 2026-06-16 18:47:13 +02:00
requirements.txt Make the library loader sloppak/loose-only 2026-06-16 19:09:53 +01:00
server.py feat(audio): loopback feeder mode + static no-cache — all app audio under exclusive/ASIO (#877) 2026-07-11 18:22:25 +02:00
SUPPORTERS.md rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
tailwind.config.js refactor(ui)!: remove the classic v2 shell — v3 is the only UI (R3a) (#871) 2026-07-11 16:33:03 +02:00
TODO.md docs: correct plugin URL casing after the feedBack rename (#576) 2026-06-23 11:20:23 +02:00
uv.lock Clean release snapshot 2026-06-16 18:47:13 +02:00
VERSION Bump version to 0.3.0-alpha.1 2026-07-03 13:41:50 +02:00