Commit ed2515b9 by PLN (Algolia)

docs: Sprint 9 — travel rig, the BT restart storm, and the tray's real fix

parent 440d086c
...@@ -4,6 +4,56 @@ Sprint entries, newest first. Player-facing: what changed about *playing*, not ...@@ -4,6 +4,56 @@ Sprint entries, newest first. Player-facing: what changed about *playing*, not
about the code. Task IDs reference the L'Armada board; `n/a` where the work was about the code. Task IDs reference the L'Armada board; `n/a` where the work was
unplanned (which, on a gig night, is most of it). unplanned (which, on a gig night, is most of it).
## Sprint 9 — 2026-09-07 (evening) · the travel rig
A readiness question ("can we leave tomorrow and play on the trip?") found two
things: the rig had no monitoring path away from Ardour, and a paired phone had
been taking all audio off the box roughly once a minute.
### Added
- **`gig-up.sh --headphones`** — the travel rig: SuperDirt, the orbit fold,
Pulsar. No Ardour, no fader restore, and the fold becomes the readiness gate
because it IS the whole monitoring path.
- **`tools/fold-orbits.py`** — sums all 14 orbit pairs onto one stereo sink at
unity. Idempotent, refuses while Ardour runs (which owns the mix), discovers
the channel count from the graph, and VERIFIES: `pw-link` exits 0 both for
"File exists" and for a port that vanished mid-call. `--check` answers "will
I hear anything?" in one line; `--undo` puts it back.
- **`gig-preflight`: `monitor path`** — stage (Ardour receiving on N Tidal
tracks) or headphones (the fold present), asking fold-orbits rather than
re-deriving the port math.
- **`rig-doctor`: `wireplumber churn`** (starts per hour, and who asked for
them) and **`perf-tray reachable`** (registered with the LIVE pid, menu
populated over dbusmenu — the only part a click needs). 53 checks.
### Fixed
- **A phone and a BLE lamp were restarting WirePlumber 39 times in 40 minutes.**
`bt-audio-fix` reacted to every BlueZ 'Connected' announcement; the phone
advertises A2DP *source* and the lamp has no audio profile, so neither can
ever have a sink. Two outcomes, both seen: `failed (start-limit-hit)` — no
session manager, no devices, `Dummy Output`, looks like dead hardware while
`pipewire.service` stays green — and, every single time, the destruction of
every link into the device nodes, SuperDirt's 28 orbit links included.
- **perf-tray's menu was there all along.** Under the unit,
`QT_QPA_PLATFORMTHEME=qt5ct` with no qt5ct config picks `qt5ct-style` and a
bare #efefef palette: a 2009 grey box. Fusion now, dark when GNOME asks.
- **perf-tray lost a race with its own tray host every login** — on GNOME the
host is an extension that registers seconds late, and the tray exited 1 on
it, saved only by `Restart=`. It waits up to 120 s now.
- **The kwin `[midiviz-pin]` check asked KDE questions on a GNOME session**, so
it warned forever about a file that will never exist.
### Doctrine
- **A restart is a link massacre.** Any WirePlumber restart recreates the device
nodes, and every link into them dies silently. Re-run the fold after one, and
after plugging headphones in (the default sink changes).
- **"Heal only when broken" is not enough when broken is the correct state.**
For a phone, "no A2DP sink" is permanent and right. Ask whether the device
could ever HAVE a sink, not whether it has one now.
- **The instrument lies too.** `pw-record` on a sink monitor read exactly
0.0000 on channel 0 in every capture — including the control run with nothing
playing. Without that control, it "proved" a dead left channel.
## Sprint 8 — 2026-09-07 (afternoon) · the undead server ## Sprint 8 — 2026-09-07 (afternoon) · the undead server
The glitchy media keys were never about media keys. An scsynth whose The glitchy media keys were never about media keys. An scsynth whose
......
...@@ -7,6 +7,54 @@ snapshot in `board-archive.md`. Newest first. ...@@ -7,6 +7,54 @@ snapshot in `board-archive.md`. Newest first.
--- ---
## n/a — the travel rig, and the Bluetooth device that kept taking audio out
**Description.** "Can we leave tomorrow morning and play music on the trip, or
do we need something from xps22?" — a readiness question that turned into a
monitoring path that did not exist and a restart storm that explained two
outages.
**Done.**
- **Nothing is on xps22.** Verified rather than remembered: samples (38 G / 46
banks), GLITCHWAVE (3.1 G), tidal-ears, visuals/scenes, the `Tidal Live`
Ardour session — all local, 406 G free.
- **`gig-up.sh --headphones` + `tools/fold-orbits.py`**: all 14 orbit pairs
summed onto one sink, no Ardour. 18 tests, and a `bd` swept d1-d14 arrived on
every orbit. `gig-preflight` gained a `monitor path` check.
- **`bt-audio-fix` restarted WirePlumber 39 times in 40 minutes** — once per
BlueZ 'Connected' announcement, from a phone (A2DP *source*) and a BLE lamp
(no audio profile). That is how the box kept ending up with no session
manager and `Dummy Output`. Now gated on the A2DP Sink UUID + a missing sink
+ reset-failed + 3 heals per 10 min. Restarts since: zero.
- **perf-tray**: the menu was exported and populated all along; `qt5ct-style`
with no qt5ct config was drawing it as a 2009 grey box (now Fusion, dark when
the desktop asks). The login race that made it exit(1) is now a 120 s wait.
- **rig-doctor 51 → 53 checks**: `perf-tray reachable` (registered with the
LIVE pid + menu populated over dbusmenu) and `wireplumber churn` (starts per
hour, and who asked). The kwin midiviz check stopped asking KDE questions on
a GNOME session.
**Learnings.**
- **A restart is a link massacre.** Every WirePlumber restart recreates the
device nodes, so every link into them dies silently — SuperDirt's 28 included.
A "heal" on a 20 s cadence is indistinguishable from an outage generator.
- **"Only heal when broken" is not enough if broken is the correct state.** For
a phone and a lamp, "no A2DP sink" is permanent and right; the gate has to ask
whether the device could ever HAVE a sink (UUID 0000110b), not whether it has
one now.
- **The measuring instrument lies too.** `pw-record` on a sink monitor read
exactly 0.0000 on channel 0 in every capture, control included — a one-eared
recorder that would have "proven" a dead left channel. A control run is what
caught it.
- **My own cleanup caused an outage.** Destroying a null-sink node by an id
scraped out of `pw-cli ls` with grep -B8 killed the session manager. Parse, or
do not touch: the doctrine was already written down.
- Three lcxl3 tests fail under the pyenv interpreter (no `mido` there) and
cannot run under `/usr/bin/python3` (no `pytest` there). The suite has no
single interpreter that can run all of it — the pyenv-vs-system trap again.
---
## n/a — the rig stops lying about itself ## n/a — the rig stops lying about itself
**Description.** "Fix parvagues-protect burning 10.7% of a core, then do a pass **Description.** "Fix parvagues-protect burning 10.7% of a core, then do a pass
......
...@@ -2355,6 +2355,44 @@ scattered through this file resolve locally. ...@@ -2355,6 +2355,44 @@ scattered through this file resolve locally.
where swept stems land (`take-master.py`/`take-lens.py` should own it, not a new tool); where swept stems land (`take-master.py`/`take-lens.py` should own it, not a new tool);
whether the local 53G ever comes back or the mirror stays the archive of record. whether the local 53G ever comes back or the mirror stays the archive of record.
## Audio session — opened 2026-09-07 (evening, travel rig)
- **`perf-tray` should be drawn by the SHELL, not by Qt.** Domovoy's tray is
the cleaner system and the reason is architectural, not cosmetic: it uses
**pystray**, which on this session picks the AppIndicator/Ayatana backend
(its bus path is `/org/ayatana/NotificationItem/domovoy`), so GNOME draws the
menu from the exported DBusMenu — native look, native left-click, no palette
to maintain. Qt5's `QSystemTrayIcon` hardcodes StatusNotifierItem
`ItemIsMenu=false`, so the host will NEVER open our menu; the left-click
handler has to pop a Qt window at the cursor, and that window is ours to
style forever. System `python3` already has `gi` +
`AyatanaAppIndicator3-0.1` + `Notify-0.7` typelibs, so the port needs **no
new dependency**: AppIndicator3 for the icon (set_icon_full wants a file, so
the PIL sparkline gets written to a temp PNG per refresh — which is what
pystray does underneath anyway), Gtk.RadioMenuItem for the two axes,
GLib.timeout_add for the 2 s refresh, Notify for showMessage. ~200 of the
833 lines are the Qt shell; the gearbox logic is untouched. Do it on a quiet
day, not the night before a trip — 2026-09-07 shipped Fusion + a login-race
fix instead, which makes the current menu presentable rather than native.
- **The tools test suite has no interpreter that can run all of it.** Under the
pyenv `python3` (the shell default) three `test_lcxl3_display` tests fail
because `mido` is not installed there; `/usr/bin/python3`, which HAS mido and
is what every systemd unit runs, has no `pytest`. So the suite is green only
by looking away. Fix: `sudo apt install python3-pytest` (or a venv that uses
the system interpreter), then make the failure loud if the interpreter is the
wrong one — the pyenv-vs-system trap has now cost this rig three separate
wrong reports.
- **`fold-orbits` sums at unity, with no trim.** 14 orbits playing at once is
14x one orbit's level, so a full arrangement on headphones can clip where the
same set through Ardour's Master does not. Deliberate for now (no hidden gain
to explain later); if it bites, the honest fix is a `--trim` that says what it
did, not a silent -12 dB.
- **The fold does not follow the sink.** Plug headphones in and the default sink
changes; the links stay on the old node and the ears get silence. `--check`
catches it in one line and a re-run fixes it. A `--watch` mode that reconciles
on graph change would remove the step — but it is another daemon on a laptop
that already runs three reconcile loops, so measure the annoyance first.
## Audio session — opened 2026-09-07 ## Audio session — opened 2026-09-07
### Afternoon — the undead server (closed 2026-09-07) ### Afternoon — the undead server (closed 2026-09-07)
......
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