Commit bc0aee3b by PLN (Algolia)

docs(gig): the launcher had never launched, and Thursday morning's order

Corrects the tooling-pass section's acceptance note: 'window closes silently =
GO' described the bug, not the check. Adds the morning sequence (start the
surface driver, press the icon once, read check-audio, then the 2h arc) and
three learnings -- the positive-marker rule for any wrapper that closes on
success, the wrong-port-draws-a-confident-wrong-picture class, and the fence
that stops a profile repair becoming a silence.
parent bc721c6e
...@@ -305,6 +305,69 @@ Measured: **16.5s full, 3.4s `--fast`** (what the launcher uses), 0.3s machine l ...@@ -305,6 +305,69 @@ Measured: **16.5s full, 3.4s `--fast`** (what the launcher uses), 0.3s machine l
the first time). If either fires on the first launch, suspect the probe's assumption the first time). If either fires on the first launch, suspect the probe's assumption
before the rig. before the rig.
## Tue 22 — late. The launcher had never launched.
The correction to the section above, and it matters more than anything else on
this page: **"window closes silently = GO" was wrong.** The only thing
`~/.cache/parvagues/gig-up.log` had ever contained was gig-up's own usage text —
`--help` prints the options and exits 0, and the window script closed on that
zero exactly as it closes on a good launch. So the rig had never been started by
the icon at all, which is why the LCXL stayed dark: no launch, no paint step.
Fixed in `tools/gig-up-window.sh`: the fence is now "did the launcher reach its
own last line" (`gig-up done`, printed on every real path), not the exit code.
A zero with no marker holds the window, names the symptom and exits 2. Tests on
both ends of that coupling, so rewording gig-up's last line fails loudly instead
of silently holding every launch.
### Thursday morning, in this order
1. **`systemctl --user start lcxl3-driver`** — the one thing left undone
tonight; the sandbox refused to start a unit. The board IS on the bus
(`client 24: 'LCXL3 1' [type=kernel]`), so it will come up this time. Then:
- `aconnect -l | grep ParVagues` → the translated port exists
- `ls ~/.cache/parvagues/surface-state.json` → **has never existed on real
hardware**; this is the state wire's first live render
- look at C1/C2/C3 in midimon: the three DJF strips, detent at 64
`gig-up.sh` already restarts this unit when the board is present, so a real
launch covers it — but a real launch has not happened yet.
2. **Press the launcher icon once** and watch what it does now. A window that
holds is now information, not a failure.
3. **`tools/check-audio.py`** — reads the box, changes nothing. Four warns
expected; `docs/2026-09-22-audio-profile-stuck.md` has the whole story and
the ordered repair. Do NOT restart wireplumber before the rehearsal.
4. Then the 2h arc with Ardour recording, which is still the gate.
### midimon — what was actually wrong
Nothing, in the drawing. With `lcxl3-driver` down, midiviz falls through
`WATCH_PREFERENCE` to the raw `LCXL3 1 DAW` port and reads v3 numbering: row A
lands on row A by coincidence, row C lands on row **B**, row F's buttons land on
row **C**, and the faders (v3 CC 5-12) are in no cell at all — so they became
the matrix rain, which only ever fired for CCs the grid does not own. Exactly
"only moves on abc, D dead, EF wrong, rain on the faders only". Table and
reasoning in `docs/2026-09-22-midiviz-state-wire.md`.
Shipped anyway, because both were real gaps: the state layer now covers all 48
cells (E latches show sticky, faders show their resting position before they are
touched) and the gutter rains corpus tokens (`^42`) for gridded controls.
### Audio — diagnosed, tooled, one step held back
`pavucontrol` offering only Pro Audio is two unrelated causes: a **saved pin** in
`~/.local/state/wireplumber/default-profile` (which WirePlumber restores before
its own pick — the reason it never self-corrects), and the laptop codec's
**profile list built without its UCM verbs** (fixable only by re-probing the
device, i.e. a wireplumber restart). `tools/check-audio.py` reports both, is in
the gate as `audio profiles` (ADVISE), and `--fix` **refuses** the repair that
would leave the box with no sink at all — which is what the textbook fix would
have done tonight.
Held back deliberately: no wireplumber restart, no live profile change. Ardour
is up, the four Pro Audio outputs are the only sinks on the box, and a silent
machine found in the morning by someone who did not make the change is worse
than a wrong profile.
## Learnings (Sun 21 night) ## Learnings (Sun 21 night)
- LCXL3 protocol has no LED/OLED readback — ghost file (`/tmp/lcxl3-oled-ghost.txt`) is the - LCXL3 protocol has no LED/OLED readback — ghost file (`/tmp/lcxl3-oled-ghost.txt`) is the
......
...@@ -22,5 +22,14 @@ ...@@ -22,5 +22,14 @@
}, },
{ {
"learning": "A generated boot file is only as good as the list it is generated FROM. gig-up.sh rewrote preload.scd on every launch from a default naming the PREVIOUS gig (setlist_opal2026.txt): measured 132 banks -> 45, leaving 41 of Thursday's banks to be read off disk mid-set — the exact 'crackle at the venue' failure the check was built for, re-armed by a stale default. Two structural lessons. (1) Name the set ONCE and have every consumer read that; the plan generator and the gate disagreeing about 'the set' is the whole bug. (2) An unconditional write is destructive in the quiet direction: the plan in place was a deliberate 132-bank UNION and regeneration could only narrow it, so gen_preload now asks check-preload first and leaves a covering plan alone. Corollary from the same pass: merging two tools that share a name means diffing what they DO, not just what they check — the root launcher span raw sclang while the other started parvagues-sc.service, so repointing the Bridge at 'one launcher' would have silently taken it off systemd (losing rlimits, the journal, supervision and OOM protection)." "learning": "A generated boot file is only as good as the list it is generated FROM. gig-up.sh rewrote preload.scd on every launch from a default naming the PREVIOUS gig (setlist_opal2026.txt): measured 132 banks -> 45, leaving 41 of Thursday's banks to be read off disk mid-set — the exact 'crackle at the venue' failure the check was built for, re-armed by a stale default. Two structural lessons. (1) Name the set ONCE and have every consumer read that; the plan generator and the gate disagreeing about 'the set' is the whole bug. (2) An unconditional write is destructive in the quiet direction: the plan in place was a deliberate 132-bank UNION and regeneration could only narrow it, so gen_preload now asks check-preload first and leaves a covering plan alone. Corollary from the same pass: merging two tools that share a name means diffing what they DO, not just what they check — the root launcher span raw sclang while the other started parvagues-sc.service, so repointing the Bridge at 'one launcher' would have silently taken it off systemd (losing rlimits, the journal, supervision and OOM protection)."
},
{
"learning": "A launcher's exit code cannot tell 'it worked' from 'it did nothing'. gig-up.sh --help prints its usage and exits 0, and the window wrapper closed on that zero exactly as it closes on a good launch -- so the rig had never once been started from the icon, and the only evidence was the LCXL staying dark. The log had been written faithfully all along and held nothing but the usage text. Structural lesson: for any wrapper that closes on success, the success test must be a POSITIVE marker the wrapped thing prints on its real path ('gig-up done'), never the absence of an error. And hold the coupling from both ends in tests, because the day someone rewords that last line, every real launch starts reporting a failure."
},
{
"learning": "A viewer reading the wrong port draws a coherent, confident, WRONG picture. With lcxl3-driver down, midiviz fell through its port preference to the raw v3 DAW stream, where row C's knobs carry row B's v2 numbers and the faders carry CCs in no cell at all -- so four separate 'bugs' got reported (D dead, EF misplaced, only ABC moving, rain only on the faders) and every one of them was the same missing translation. The only thing on screen that said so was the port label in the header. When a translated stream and a raw stream can both satisfy a name match, the viewer owes the user a pip for WHICH ONE it is reading, next to the paused and stream-broken pips."
},
{
"learning": "A fix that trades a wrong setting for no function is not a fix, and the fence belongs in the tool rather than in the judgement of whoever runs it. check-audio's textbook repair -- clear the stale pin, take WirePlumber's own pick -- would have switched off the only four sinks on the box, because the laptop codec was pinned off and the USB interface unplugged. So --fix counts the sinks it would leave behind and refuses at zero. Also: a config fragment in a neighbouring version's format (0.5 .conf.d on an 0.4.17 install) is worse than a missing one, because it looks exactly like protection that is in force -- the July DMIC guard had never applied, and what had kept the box safe was the card being switched off."
} }
] ]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment