-
fix(lcxl3): the driver had never connected anything to SuperCollider · fa22bb85
Resume point 1 was "run lcxl3-driver against a live SuperCollider, because it never has been". It has now, and the run found two defects that no amount of reading could have found — both invisible precisely because the code reported success. DEFECT 1 — the aconnect direction trap. reconcile() resolved its destination with `_clients("-i")`: dst = next((p for p, n in _clients("-i") if "SuperCollider" in n), None) `aconnect -i` lists ports you can read FROM. SuperCollider appears in both directions: in0..in4 are destinations, out0..out6 are sources. So `-i` returned SC's *output* port — 130:5 on this rig — and the driver ran aconnect ParVagues LCXL3:0 130:5 which writes into an output-only port and fails. The CompletedProcess was discarded, so the failure had no way to be seen, and the next line printed "reconcile: src -> dst" as though the link existed. Every previous reasoning step about this driver rested on a connection that was never made. Fixed by resolving destinations from `-o` via a new _sc_input_ports(), and by checking the return code and PRINTING the failure. A reconciler that cannot say "I failed" is decoration — the rig has been bitten by this shape before (feedback_verify_the_plumbing, reference_check_the_instrument_first). DEFECT 2 — reconcile only ever ADDED links, so it could not be correct here. `MIDIIn.connectAll` subscribes SC to every source present at boot. Measured on the live rig: 14:0 Midi Through -> 130:2 20:0 LCXL3 1 MIDI -> 130:3 20:1 LCXL3 1 DAW -> 130:4 So the moment the driver published a correct, translated stream, SC was hearing it ALONGSIDE two raw ones. This is worse than duplication: v3's DAW map collides with the corpus's v2 numbers on 16 indices with different meanings, so row C knob 4 would drive ^52 (right) and ^32 (row B's cell) at once — two orbits moving when one knob turns. And a doubled CC is fatal on a latch: the toggle fires twice and cancels, which reads from the stage as "the button does nothing". Added prune(), which cuts every path into SC that is not our virtual port, re-asserted on the same timer as the connect (the binding must be re-asserted, never assumed — feedback_stale_binding_pattern). Midi Through is cut by default because midi-autoconnect's rule 1 pushes the surface into it, i.e. it is a second copy of the same stream; --keep-thru opts out. Added `--graph`, which prints what actually feeds SC and what prune would do. That is the diagnostic whose absence let defect 1 hide: the driver's own view of the graph was never printable, so it could never be checked against reality. Verified on the live rig (sclang 845344 / scsynth 845718, LCXL3 on client 20): --graph now resolves 130:0-130:4 and correctly classifies 3 WOULD CUT paths and PipeWire-RT-Event's monitor bridge as left alone. Hardware also confirms the translation table's row D: PLN reports fader 1 = CC5, which is V3_ROWS["D"][0].PLN (Algolia) authoredfa22bb85
×