feat(surface): REMAP PHASE 2 — the button rows column-align, and the board is now one sentence
96 substitutions across all 13 OPAL tracks. "Column N is orbit N" is now true for
the WHOLE surface, not just the knobs:
BT 41-44/57-60 d1..d8 gate
BL 73-76/89-92 gMute1/2/3 | d4..d8 gate2
A 13-20 d9..d12 level | d9..d12 fx
B 29-36 d1..d8 fx
C 49-56 gF1/2/3 | d4..d8 fx2
D 77-84 d1..d8 level
surface-columns: 23/84 aligned, 127 renumbers -> 82/82 aligned, 0 renumbers
PLN drove the key decision. My first attempt left over-budget controls in place and
reached only 11/13 tracks, which he rejected outright: "we need finish the job. why
half moved? cant you move all-but then comment out with fixme?" He was right on both
counts, and leaving them was not the conservative choice — it was a bug generator.
THE CASCADE, AND WHY COMMENTING OUT DISSOLVES IT
The board has 8 BT + 5 BL = 13 per-orbit buttons for 8 orbits, because BL1-3 are the
family mutes. So d1..d3 get exactly ONE button each, and bombe_dj's kick — a 3-state
gesture on TWO buttons —
$ midiOn ("^41"-"^42") (<| "k(3,8) ~")
$ midiOff ("^42"+"^41") (<| "k*<1!7 2> ~ ...")
$ midiOn "^42" (<| "k k k*<1!8 2!8> ...")
is over budget by design. Left in place, d1's ^42 blocked d2, which blocked d3, then
d4, then d5: 4/8 aligned. Commented out, the control is free and the row aligns.
Nothing is lost — the original sits one line above under two dashes, restorable the
moment gSel (#54) can fold three states onto d1's own control.
THREE BUGS THE GATES CAUGHT BEFORE THIS SHIPPED — all in my own tool
1. LEAVING OVERFLOW IN PLACE IS A CROSS-ORBIT TRIGGER. Once ^42 becomes d2's button,
any surviving d1 reference means pressing d2's gate also fires d1's kick variant.
Measured, not theorised: perfect.tidal ended up with ^42 driving BOTH d1 and d2.
A new gate now asserts no button drives two orbits — 0 across the setlist.
2. THE CLASH GUARD HAD A HOLE. A clashed control also stays put, so it too becomes an
obstacle — but I computed clashes in the same sweep that consumed them. bombe_dj:
d2 was left on ^43 by a clash while d3 was still planned to move ONTO ^43. Fixed by
iterating pass 2 to a fixed point, growing the sticky set until a sweep finds
nothing new, with a non-convergence bail-out.
3. COMMENTING A LINE ORPHANS ITS CLOSING PARENS. A chain step spans several lines, so
commenting only the line carrying the CC left the body live and the `)` dangling —
`perfect: parse error on input ')'`, and desire went from building to not building.
The unit is a `$`-chain SEGMENT; it is expanded to one, then paren-balance-checked,
and the tool refuses rather than emit broken Haskell.
4. MULTI-BLOCK ORBITS WERE SILENTLY DROPPED. perfect.tidal declares `d1` SIX times as
section alternates. Keying the plan by orbit number meant each later block
overwrote the earlier one's plan, so the first block's ^42 -> ^41 vanished — the
real reason gate 1 kept failing after I "fixed" it. An orbit's control set is the
UNION across its blocks. Recovered 3 more moves (perfect 7->8, electric_hammer 3->5).
VALIDATION
no button drives 2+ orbits 0 tracks (was 1)
silent-eval --seeded OK, every declared orbit emits events
build failures 4, identical to before — no new breakage (#93)
surface-columns 82/82 aligned, 0 renumbers
pvlint 0 errors, 9 pre-existing warnings (#70, dup d5)
pytest 471 passed
check-boot / -blocks OK
9 overflow controls are now commented with a FIXME naming the orbit, its slots, and
the restore path. That is the honest total: the surface is fully aligned, and every
control that could not fit says so in the file rather than in someone's memory.
Showing
This diff is collapsed.
Click to expand it.