feedBack/static/capabilities
Byron Gamatos 4b4c156fce
refactor(ui): defer every classic script, keep boot() on DOMContentLoaded (R3a) (#872)
Puts every external `<script>` in the v3 shell into the deferred queue, and
keeps each script's boot() firing at DOMContentLoaded exactly as it does today.
Behaviourally a no-op; it is what makes the ES-module flips safe.

WHY. `type="module"` defers execution to after HTML parse. Classic-`defer` and
module scripts share ONE "execute after parsing" list and run in DOCUMENT ORDER,
but a plain classic script runs DURING parse — ahead of all of them. So the
moment capabilities.js becomes a module while app.js is still plain, app.js runs
FIRST, and its 11 top-level `window.feedBack.on(...)` calls (app.js:6245-6722)
hit a bare `{}` — `_ensureFeedBackEventBus()` (capabilities.js:33), which
attaches .on/.emit/.off, would not have run yet. TypeError, app.js dies
mid-parse. Deferring everything now keeps document order == execution order
through the rest of the migration.

THE CATCH (Codex preflight caught this — a real ordering change). 22 scripts
guard their boot with `if (document.readyState === 'loading')`. A deferred
script runs at readyState 'interactive', so that test is FALSE and the else-branch
fires boot() immediately, at the script's position in document order — instead of
at DOMContentLoaded, after every script has evaluated.

That matters far more than one call site: a scan of the shell's scripts found
**43 forward references** where a script's boot() reads a global that a LATER
script defines (shell.js -> profile.js's window.v3Onboarding, songs.js ->
settings.js's window._confirmDialog, badges.js -> songs.js's
window.displayTuningName, ...). Every one of them resolves today only because
all boots happen at DOMContentLoaded. So the guards now treat 'interactive' as
not-ready (`!== 'complete'`), restoring that exactly.

Codex's specific finding (first-run onboarding silently skipped) did NOT
reproduce — shell.js's boot() awaits /api/profile, and that yield lets the
remaining deferred scripts run first. But the race it described is real, the
guard is silent when it fails (`&& window.v3Onboarding`), and the other 42
forward refs have no such await protecting them. Fixed at the root rather than
at the one site.

VERIFIED. A/B against origin/main on a fresh profile, 13 probes (onboarding
overlay, v3Onboarding/v3Songs/v3Profile/fbNotify/v3Badges/uiPrompt/showScreen,
bus, capabilities.version, createHighway, plugin scripts, mounted screens):
IDENTICAL, zero console/page errors on both. pytest 2396, node 1028/1028,
ESLint 0 errors, Codex 0.

New guard: test_every_external_script_defers_so_document_order_is_execution_order
fails if any external tag is plain classic — verified to fail on a single
reverted tag, so it actually bites.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 16:57:04 +02:00
..
audio-effects.js fix(audio-effects): accept pre-rebrand chain plan schema as alias (#816) 2026-07-09 22:17:57 +02:00
audio-session.js rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
interface-scale.js refactor(ui): defer every classic script, keep boot() on DOMContentLoaded (R3a) (#872) 2026-07-11 16:57:04 +02:00
library-card-actions.js rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
library.js rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
midi-input.js rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
note-detection.js rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
playback.js rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
tuning.js rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
visualization.js rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
working-tuning.js settings: add host instrument profiles (#753) 2026-07-05 00:16:42 +02:00