-
feat(surface): the mutes group by ROLE, the filters by BLOC — two maps, one author (#105) · 72943be9
PLN settled the gM question on 2026-07-30, and the answer was not the one this task had assumed for a day: "I want the kick, now on d1/fader1, to have its mute on F1 botrow button. I want the C1 knob to djf all percs, C2 only bass djf, C3 melodies DJF. however i want the mutes consistent across all all tracks: m1 d1 / m2 other percs / m3 all bass+melodics" ## The thing I had wrong #105 was framed as "align gM<N> to gF<N>" — make the mute index mirror the filter index so an orbit's knob and button share a column. I measured 35 mismatches in the set under that invariant and was ready to fix them. That invariant is WRONG, and the measurement was answering the wrong question. The filters and the mutes are two different gestures: FILTERS want the rhythm section as ONE BLOC — you sweep the drums together MUTES want the kick SPLIT OUT on its own button — you drop the kick alone So gF groups {d1,d2,d3,d8} and gM splits d1 from {d2,d3,d8}. Under the real map the edit count is 55, not 35 — and `d4: gF2+gM3`, which the mirror-invariant called a mismatch in 11 tracks, is CORRECT. Had I shipped the "alignment" I would have moved 11 correct lines and left the kick sharing a button with the hats. Recorded as tools/lcxl_grid.py `_FILTER_FAMILY` / `_MUTE_FAMILY` — in the ONE authored grid (#97), with his words above it, so the asymmetry reads as deliberate rather than as drift. `as_dict()` now exports `orbit_family` so the HUD can say which knob AND which button own an orbit; `--generate` rewrote tools/lcxl_grid.json and the HUD package's own copy (outside this repo, in Tools/pulsar-parvagues-hud). ## tools/fix-mute-roles.py Convergent migrator: `--apply` rewrites, `--check` exits 1 on drift (a pre-gig gate), `--fill` gives a mute to a block that has none, `--report-missing` writes a markdown report instead. Reads the map from lcxl_grid rather than restating it. ## PV012 / PV013, so it cannot re-drift PV012 flags an orbit whose gM does not match its ROLE — deliberately NOT "gM index != gF index", and there is a regression test asserting `d2 $ gF1 $ gM2` is clean, because a rule written to the mirror invariant would flag that valid line. PV013 flags an orbit with no family mute at all: PLN's "no mute is oversight!" — nothing on the surface can drop such an orbit, so taking it out means editing live. ## PV013 immediately caught a bug in the migrator that wrote it Minutes after PV013 existed, it flagged `d5` in you_my_sunshine — an orbit the migrator had walked straight past. Cause: fix-mute-roles had its OWN orbit regex requiring `^dN $`, and that file's head line is `d5 -- The Voice of Love`, no `$`, with the gates on a commented continuation. pvlint's regex allows `dN` followed by `$`, `--`, or EOL. Two parsers disagreeing about what an orbit IS is the same class of bug as two copies of the grid, so the migrator now imports pvlint's parser and owns none of its own. Re-running found 14 more rewrites and 7 more missing mutes it had silently skipped — the miss was under-application, not corruption, but it would have left exactly the inconsistency this task exists to remove. Also fixes #109: the orphan-orbits hand-measurement for you_my_sunshine still expected d7, which PLN moved to d6 in ce887b78. Re-derived by reading the file's column-0 declarations, not by pasting the parser's output — a hand measurement that quotes the thing it checks is a tautology. Suite: 222 passed, 0 failed (8 new PV012/PV013 tests).PLN (Algolia) authored72943be9
| Name |
Last commit
|
Last update |
|---|---|---|
| .. | ||
| tests | Loading commit data... | |
| __init__.py | Loading commit data... | |
| __main__.py | Loading commit data... | |
| core.py | Loading commit data... | |
| rules.py | Loading commit data... |