Commit Graph
1 Commits
Author SHA1 Message Date
12eb73aee9 refactor(highway): carve the STATEFUL primitives, threading hwState explicitly (R3c) (#916)
fretX, fillTextReadable, _noteState, _paintGemGlow -> static/js/highway-state-primitives.js.
50 call sites rewritten. highway.js 4,105 -> 3,965.

The first slice that changes signatures. Each of these four gains hwState as an explicit
FIRST PARAMETER.

━━━ hwState IS A PARAMETER, NOT AN IMPORT ━━━

createHighway() is a FACTORY. The constitution publishes window.createHighway so a plugin can
build a SECOND highway for its own panel, and highway.js says so itself. Import hwState as a
module singleton and two panels silently share one clock, one render scale, one string
palette — each driving the other. Nothing throws. The picture is just wrong, in a way no test
would catch.

(The exact opposite of the app.js carve, where player-state.js and library-state.js ARE
module singletons — correctly, because there is exactly one app. Same epic, same language,
opposite answer, decided entirely by whether the thing is a factory.)

━━━ THE PLUGIN BUNDLE NEARLY BROKE, SILENTLY ━━━

The renderer bundle hands two of these STRAIGHT TO PLUGINS:

    b.fretX = fretX;
    b.getNoteState = _noteState;   // stable reference

highway_3d calls both EVERY FRAME, with the old arity. Handing out the new 3-arg versions
would have passed `note` where hwState belongs — no throw, no error, just wrong geometry and
wrong judgment state INSIDE A PLUGIN, which no core test would ever see. Green CI, broken 3D
highway.

So hwState is bound ONCE per instance, in the factory, and the bundle hands out those views.
A per-frame arrow would have fixed the arity and reintroduced exactly the per-frame allocation
the bundle's stable-reference contract (feedBack#254) exists to prevent. b.project needs none
of this — project() is pure and its arity never changed.

VERIFIED IN A BROWSER, against the real bundle, on both builds:

    fretX arity                      3    3     (NOT 4 — the bound view preserves it)
    getNoteState arity               2    2
    fretX(5,1,800) in 0..800      True True
    getNoteState null w/o provider True True
    getNoteState honours provider  True True
    fretX is a stable reference    True True

IDENTICAL. Without the bound views fretX would have reported arity 4 and computed garbage.

Also caught on the way: my generated module imported STRING_BRIGHT_FALLBACK, a name
highway-constants.js does not export. ESLint does not flag that — but importing a name a
module does not export is a runtime SyntaxError that kills the WHOLE module. These four need
no constants at all; the import is gone.

TESTS. highway_note_state pins the signature AND the stable-reference contract — it caught the
bundle break. Retargeted at the module and the new arity; both contracts still asserted, and
the "no fresh arrow per frame" rule is now asserted explicitly rather than implied by
`getNoteState: _noteState`.

PERF GATE PASSES AT 1.94ms against its 12ms budget — fretX and _noteState are now CROSS-MODULE
calls, per note, per frame. It costs nothing measurable.

node 1045, pytest 2416, ESLint 0, Codex 0.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 12:42:38 +02:00