Commit 9150a200 by PLN (Algolia)

Pre-compact cleanup: CHANGELOG + archive + memory

Sprint 3 (the box plays alone, and the guard now guards), the archive entry with
its five learnings, and three deferred items: rig-doctor should call
parvagues-protect --check instead of testing for files, an install step should
fetch the sister repos, and the protect daemon's 10.7%-of-a-core poll needs
fewer forks.
parent 4f125e14
......@@ -4,6 +4,61 @@ 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
unplanned (which, on a gig night, is most of it).
## Sprint 3 — 2026-09-06 (late) · the box plays alone, and the guard now guards
Sprint 2 made the surface tell the truth. This one makes the *laptop* self-
sufficient — nothing it needs lives on another machine — and discovers that the
process protection installed to keep the music alive had never protected
anything, through four consecutive green lights.
### Added
- **`rig-doctor` answers `CAN THIS BOX PLAY A SET? yes`** (n/a) — 45 checks, 0
fail, 10 warn, 35 pass. `perf-audio` + its sudoers rule are deployed, so
gig-up can actually leave `powersave`; `parvagues-protect` is installed and
verified. The ten remaining warns are all non-blocking, and two of them are
just "the LCXL3 and the UMC202HD are not plugged in right now".
- **The box authors as well as plays** (n/a). `../tidal-ears/` is cloned (1.4 M
— the 3.2 G it occupies on xps22 is untracked analysis output, so clone it,
never rsync it), which also un-dangles the committed `analyze_samples.py`
symlink. `~/Work/Art/GLITCHWAVE` (3.1 G) is synced, against the earlier
judgement that authoring belongs on xps22 — overruled: "glitchwave cant hurt".
Every GIF path scattered through `backlog.md` now resolves locally.
- **The protect installer verifies itself** (n/a) — it reads the *live* `/proc`
capability set after restarting and says plainly that "protection is
decoration" until that line reads ok.
### Fixed
- **The OOM guard could not open the file it guards** (n/a). It ran as root and
every write to `/proc/<pid>/oom_score_adj` returned EACCES, under an error
message reading `need root, have uid 0`. uid 0 bypasses file permissions via
`CAP_DAC_OVERRIDE`; the unit's `CapabilityBoundingSet` had dropped it, and the
target is mode 0644 owned by `pln`. Now verified end to end: a restarted
SuperDirt is pulled from systemd's `oom_score_adj=200` — the most killable
process on the machine — to `-1000` in about seven seconds, unaided.
- **The installer was not idempotent, so the fix did not take** (n/a).
`systemctl enable --now` starts a *stopped* unit and does nothing to a
*running* one, so a re-install kept the previous daemon with the previous
unit's capabilities and the previous script's inode, while printing success.
Restart, always: this daemon holds no ports and makes no sound.
- **A stuck fault no longer floods the journal** (n/a) — a FAILED sweep logged
every 2 s, 43200 identical lines a day, which the file's own header already
said it refuses to do. One line per distinct failure; the pid is in it, so a
restart re-arms the message.
### Doctrine
- **Four green lights lied in one evening, and they were the same light.** The
guard reported `ok` on scsynth *because it never attempted a write there*; its
error blamed the wrong cause; the installer wrote a correct file and left a
stale process; and `rig-doctor`'s `parvagues-protect: PASS` checked `bin` and
`unit` presence — the exact pair that stayed true all night while nothing
worked. **A correct file on disk is not a correct process in memory.** Check
the live `/proc`, compare the PID and start time, and prefer a check that
shells out to the tool's own `--check` over one that tests for a file.
- **Raw output pasted back is the highest-signal debugging artifact there is.**
Each of the four was caught by reading an unabridged terminal dump — the
`Main PID` and start time that disproved the reinstall were sitting in the
first paste, before anyone knew to look for them.
## Sprint 2 — 2026-09-06 (night) · the board says what is playing
Sprint 1 got sound out of a new laptop. This one makes the surface and the
......
......@@ -7,6 +7,59 @@ snapshot in `board-archive.md`. Newest first.
---
## n/a — the box plays alone, and the guard that said so was lying
**Description.** Answer "are we ready to take off with only this laptop?" —
close the two root installs `rig-doctor` was failing on, and fetch the sister
trees that still lived only on xps22, so nothing the rig needs is on another
machine.
**Done.**
- `perf-audio` + sudoers and `parvagues-protect` installed (PLN's own sudo).
`rig-doctor`: 45 checks, 0 fail, 10 warn, 35 pass — `CAN THIS BOX PLAY A SET?
yes`.
- `../tidal-ears/` cloned from `git.nech.pl:pln/tidal-ears.git` (`main`, 1.4 M);
the committed `tools/analyze_samples.py` symlink resolves again.
- `~/Work/Art/GLITCHWAVE` synced, 3.22 GB via `rsync -aH`; all five GIFs the
backlog references verified present. 264 G free afterwards.
- Three repos pushed: `Tidal``master` (`4f125e1`), `pulsar-parvagues-hud`
`main` (`78b1755`, on explicit request), `tidal-ears` in sync.
- **`parvagues-protect` fixed twice and then actually verified.** Journal
evidence: `protected: sclang[3495520] oom:200->-1000
sched:SCHED_OTHER/0->FIFO/85`, on a pid created after the daemon started.
**Learnings.**
- **A process running as root is not a process with root's powers.** uid 0
bypasses file permissions through `CAP_DAC_OVERRIDE` specifically; a hardened
unit that lists only the capabilities it "needs" silently loses the bypass,
and writes to another user's 0644 file fail with EACCES while `id -u` says 0.
Any daemon that edits `/proc/<pid>/*` of processes it does not own needs it
explicitly.
- **`systemctl enable --now` is not `restart`.** It starts a stopped unit and
no-ops on a running one, so a re-install after editing a unit leaves the old
process alive — old capabilities, old script inode — while the installer
prints success and `NeedDaemonReload` says `no`. The tell is `Main PID` and
the `Active: since` timestamp, both unchanged across the install.
- **The green rows were the untested path.** scsynth and sclang read `ok`
because they were already at target, so no write was attempted; the one
process that needed a write was the only one that failed. A check that only
reports deviations reports nothing when it cannot act.
- **A dangling committed symlink can break a test suite far from itself** — the
`analyze_samples.py` link into the uncloned sister repo took down the parser
ratchet at COLLECTION. Neither `rig-install.sh` nor `rig-doctor` fetches or
even notices the sister repos CLAUDE.md documents: three separate manual fixes
tonight for one missing line of setup.
- **Reuse-value beats size for keep/delete calls.** 3.1 G of GLITCHWAVE source
is worth keeping at 264 G free — it is authoring input, not litter — where the
same 3.2 G of derived analysis output on xps22 was correctly left behind.
**Deps.** Followed Sprint 2 (the surface/feedback work). Unblocks: authoring
scenes and post-prod mastering on the gig laptop. Left open: teach `rig-doctor`
to shell out to `parvagues-protect --check`; observe the Ardour GUI-echo leg;
have an install step fetch the sister repos.
---
## n/a — the board says what is playing (and three corrections)
**Description.** Make the LCXL3 and the Ardour session report the truth about
......
......@@ -2129,6 +2129,27 @@ scattered through this file resolve locally.
## Housekeeping
- **`rig-doctor` does not check LV2 plugins** — the same gap class as the PySide6 miss.
- **`parvagues-protect` burns 10.7% of a core, forever, on a battery laptop.**
Measured 2026-09-06: 35.7 s CPU over 333 s uptime. It is a system unit enabled
at boot, so this runs whether or not anything is playing — which cuts against
the whole reason rig units are on-demand here (see the box's boot policy in
`tools/rig_units.py`). Cause is fork count, not work: every 2 s tick forks
`pgrep` per target, then **two** `chrt -p` calls per pid merely to READ the
policy and priority, plus an unconditional `prlimit`. Roughly 12 processes
every two seconds to discover that nothing changed.
Fixes, cheapest first: read policy/priority from `/proc/<pid>/stat` fields 18
and 19 instead of forking `chrt -p` twice; call `prlimit` once per pid rather
than every tick; raise the interval (2 s is far below anything a human or an
OOM event notices — 10 s would be 5× cheaper on its own). Do not "fix" it by
disabling the unit: the protection has to be up BEFORE the audio starts.
- **`rig-doctor`'s `parvagues-protect: PASS` tests for files, not for function.**
It checks that `bin` and `unit` exist — the exact pair that stayed true all
through 2026-09-06 while the daemon could not write a single `oom_score_adj`
(missing `CAP_DAC_OVERRIDE`, see the install entry above). Should shell out to
`/usr/local/bin/parvagues-protect --check`, which already reports per-process
truth and exits non-zero, and additionally assert the running daemon holds
`cap_dac_override`. Same gap class as the LV2 and PySide6 misses, but this one
guards the thing that keeps the music alive under memory pressure.
The missing `lsp-plugins-lv2` / `zynaddsubfx-lv2` only surfaced via Ardour's own dialog.
- **The rig now carries two Qt bindings** (PyQt5 for perf-tray, PySide6 for midiviz).
Documented in the doctor rather than unified; a PyQt5 port of midiviz is ~30 refs but
......
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