73 bare `highway.x` references -> `window.highway.x`, across app.js and 10 other files.
Provably a NO-OP today. It is the precondition for flipping highway.js to a module.
━━━ WHY THIS HAS TO LAND FIRST ━━━
highway.js is a CLASSIC script. Its top-level `const highway = createHighway()` therefore
creates a GLOBAL LEXICAL BINDING — visible as a bare name to every other classic script AND
to every ES module. 73 call sites quietly rely on that.
The moment highway.js becomes a module, that binding is gone. `const` in a module is
module-scoped, not global. Every one of those 73 sites becomes a ReferenceError, and the
flip is impossible until they say what they mean.
`window.highway = highway` is already set, to the same object, on the same line. So this is
an identity rewrite — verified in the browser below.
━━━ THE REWRITE BIT ME THREE TIMES. REGEX IS NOT ENOUGH FOR THIS. ━━━
1. A SHADOWED LOCAL. capabilities/note-detection.js does `const highway = window.highway`.
Its 9 bare uses are LOCAL and already correct; a blind rewrite would have emitted
`const window.highway = window.highway`. Excluded.
2. HALF-CONVERTED GUARDS — the dangerous one. Six sites read
`typeof highway !== 'undefined' && highway && typeof highway.setTime === 'function'`.
The regex converted the CONSEQUENT and left the TEST, which is WORSE than not touching
them: after the flip `typeof highway` is 'undefined', so each guard is PERMANENTLY FALSE
and the code behind it silently never runs. transport.js's was the seek->setTime sync:
the chart clock would have quietly desynced after every seek, with nothing failing.
All six now test window.highway.
3. TWO MORE BARE REFERENCES, found by Codex [P2] and confirmed by an AST scan: app.js:3114
and :3176 use `highway && typeof window.highway.getSections === 'function'`. My grep
searched for `typeof highway`, not `highway &&`. After the flip these throw, the catch
swallows it, and the editor silently falls back to a ±4s edit window and arrangement 0.
Regex missed a shadow, a half-conversion, and two bare reads. The final check is an
AST pass that resolves scopes and reports every `highway` identifier not bound locally.
It now reports ZERO.
VERIFIED. A/B against origin/main in two browsers, 15 probes, IDENTICAL, zero page errors:
window.highway is the same object as the bare global, the whole API surface resolves, a real
song plays, the chart clock advances, getPerf().drawMs > 0 — and `seek syncs chart` passes,
which is the exact guard I nearly broke in (2).
node 1045, pytest 2416, ESLint 0, Codex 0.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
static/js/highway-colors.js (601 lines) — bodies VERBATIM.
app.js 10,415 → 9,837. Under 10k.
DECOMPOSED, not sliced. The "settings" blob measured 47 fns / 19 inbound and was
not carvable as-is. Seeding the closure from a FUNCTION (initHighwayColors) found
only 18 fns and left 4 HWC_* constants used outside it — i.e. the seed was wrong,
not the cluster. Re-seeding from the STATE (every function touching HWC_*/`_hwc*`)
found the true cluster: 45 top-level nodes, lines 2751-3331, CONTIGUOUS, with ZERO
foreign nodes inside the span.
INBOUND: 0. EXPORTS: 2 (initHighwayColors, hwcInitSettingsUI).
The other 43 symbols — the HWC_* tables, the 12 presets, the theme store, the
share codec, the picker handlers, the window.feedBack.highwayColors facade — are
used nowhere else in core and stay private. No inline on*= handlers here (the
Settings buttons are wired by addEventListener inside hwcInitSettingsUI), so
nothing needed re-exposing on window. The three bus listeners register inside
initHighwayColors, which app.js calls — not at module top level — so no ordering
change.
THE no-undef GATE EARNED ITS KEEP. My closure said INBOUND=0; the module actually
uses `uiPrompt` (the "name this theme" prompt). It was missed because uiPrompt is
no longer an app.js DECLARATION — it's an IMPORT BINDING (from #882's dom.js), and
I was collecting declarations only. `no-undef` with typeof:true caught it.
=> Lesson for the next carve: seed `tops` from ImportDeclaration bindings too.
=> And it VALIDATES carving dom.js early: this module just imports uiPrompt from
it. Had dom.js still been stranded in app.js, this carve would have needed a
host seam.
app.js -> { plugin-loader, viz, diagnostics-export, dom, highway-colors }
plugin-loader -> viz
highway-colors -> dom
viz, diagnostics-export, dom -> (leaves)
VERIFIED BY DRIVING THE FACADE. A/B against origin/main in two browsers:
window.feedBack.highwayColors installed, identical method surface, 12 presets,
identical default slot colours, and a share-code encode→decode round-trip
returning #112233 — IDENTICAL on both, zero console/page errors either side.
Harnesses: highway_colors_facade + highway_string_colors retargeted (both
brace-extract blocks out of the source by signature).
pytest 2396, node 1038/1038, ESLint 0, tailwind-fresh clean, Codex 0.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>