PLN named this himself while testing the remap: "i see the hud shows d9 on A8 so
didnt we migrate parvagues HUD to new convention? why 2 sources of truth tbh"
It was five. The CC -> physical-control -> owning-orbit mapping was hardcoded
independently in:
tools/surface-columns.py GRID
tools/migrate-columns.py KNOB_A/B/C, BUTTONS, ARDOUR, label(), and the
destination ARITHMETIC (28 + orbit, 48 + orbit)
tools/lcxl-leds.py 12 + (orbit - 8) for the d9-d12 level knobs
tools/pvlint/rules.py PV008's BT_CCS / BL_CCS / FAMILY_CCS
<hud>/lib/render.js ORBIT_CONVENTION
After the 2026-07-29 remap the Python copies moved and the HUD's did not, so the
topbar drew d9 on A8 while the LED board and the .tidal files both said A1. The
display contradicted the hardware under his hands, mid-test, and NOTHING FAILED —
which is the property that guaranteed it would happen again on the next remap.
NOW
tools/lcxl_grid.py is the only place the grid is written: the row/column table,
the role of every cell (level / fx / fx2 / gate / gate2 / family_filter /
family_mute), the Ardour-owned set, and gPanic. Everything else derives from it,
and the two non-Python consumers read a GENERATED artifact:
tools/lcxl_grid.json for anything outside Python
<hud>/lib/lcxl-grid.generated.js imported by render.js
Same pattern as the fleet colour language (models.py -> gen_tokens -> tokens.css):
author the ontology once in Python, generate for every other language.
Two details worth keeping:
* ROW ALIASES. The consumers had each invented their own names — D/fader,
E/btn1/BT, F/btn2/BL. Forcing one vocabulary would have churned five files
and PLN's own muscle memory for zero benefit, so every row carries all its
names and each tool keeps printing what it always printed.
* ROWS E AND F ARE ONE ROW OF EIGHT, not two of four. They are non-contiguous
on the hardware (41-44 then 57-60) and modelling that as two rows is exactly
what put d6's second button in column 5 in the old map. Asserted directly.
THE TEST IS THE DELIVERABLE
14 new tests. Half assert the authored table is coherent (48 controls, every row
covers columns 1-8, every orbit has a level/fx/gate, no Tidal slot lands on an
Ardour-learned control, d1-d3 own exactly one knob and one button because C1-3
and F1-3 are the family controls). The other half assert every CONSUMER still
agrees, and that regenerating the artifacts is a no-op — so a remap that forgets
one copy fails the suite instead of shipping a lying topbar. One test simply
checks render.js has not re-grown a literal ORBIT_CONVENTION.
VALIDATION — behaviour must be bit-identical, this is a refactor
surface-columns 83/83 aligned, 0 renumbers (unchanged)
migrate-columns --plan: 0 moves, 0 overflow (nothing left to do)
pvlint 13 tracks, 0 errors, 9 pre-existing warnings
pytest 490 passed (was 476 + 14 new)
HUD specs smoke / lcxl-leds / scene-directive all pass
lcxl_grid --check 48 controls, every row 1-8, every orbit housed
Also fixed while here: the LED watcher was still running the process started at
16:39, i.e. code from before the A1-lights-for-d9 feature existed. That is why
PLN saw no A1 LED while d9 was declared — not a mapping bug, a stale daemon.
Restarted. Worth remembering as its own class: for gear that runs as a service,
"I fixed the code" is not "the rig picked it up".
| Name |
Last commit
|
Last update |
|---|---|---|
| .. | ||
| at | Loading commit data... | |
| bridge | Loading commit data... | |
| foundry | Loading commit data... | |
| pulsar-parvagues-hud | Loading commit data... | |
| pvlint | Loading commit data... | |
| tests | Loading commit data... | |
| README.md | Loading commit data... | |
| analyze_samples.py | Loading commit data... | |
| check-boot-blocks.py | Loading commit data... | |
| check-boot.sh | Loading commit data... | |
| check-drift.sh | Loading commit data... | |
| check-mix.py | Loading commit data... | |
| check-tracks.sh | Loading commit data... | |
| control-lens.py | Loading commit data... | |
| deshadow-helpers.py | Loading commit data... | |
| fan_check.sh | Loading commit data... | |
| gen_tokens.py | Loading commit data... | |
| gen_ts_types.py | Loading commit data... | |
| gig-log.py | Loading commit data... | |
| gig_record.sh | Loading commit data... | |
| lcxl-init.py | Loading commit data... | |
| lcxl-leds-watch.service | Loading commit data... | |
| lcxl-leds.py | Loading commit data... | |
| lcxl_grid.json | Loading commit data... | |
| lcxl_grid.py | Loading commit data... | |
| migrate-columns.py | Loading commit data... | |
| mock-lcxl.py | Loading commit data... | |
| mute.tidal | Loading commit data... | |
| orphan-orbits.py | Loading commit data... | |
| probe-chain.py | Loading commit data... | |
| sample_tfidf.py | Loading commit data... | |
| setlist_samples.py | Loading commit data... | |
| shipowiz.py | Loading commit data... | |
| silent-eval.py | Loading commit data... | |
| solo.tidal | Loading commit data... | |
| surface-columns.py | Loading commit data... | |
| tempo-lens.py | Loading commit data... | |
| tidal-remote.py | Loading commit data... | |
| tools.png | Loading commit data... | |
| unmute.tidal | Loading commit data... | |
| unsolo.tidal | Loading commit data... |