- 20 Sep, 2026 2 commits
-
-
5 CC moves the buffer-clobber detector wanted (the gate went NO-GO over it): the track was committed on the pre-remap grid, so its d3/d4/d7 buttons fired across columns into other orbits' controls. Mechanical remap only; the ^44 step keeps its FIXME(#94) note.
PLN (Algolia) authored -
The Bridge's RIG UP ran tools/gig-up.sh --converge, whose converge never starts lcxl3-driver (boot policy manual), and whose LCXL checks grep the v2 name 'Launch Control XL' — so a v3 board plugged in read as absent, painted by nobody, and the gate still said GO. Meanwhile the desktop launcher runs the ROOT gig-up.sh, a second launcher with the leds() the gate lacks — two launchers, each owning half the surface story. - launchers: Ardour launches under pw-jack like gig-up does (bare ardour starts its own jackd and splits the graph from scsynth) - gig-up: 'LCXL present' matches LCXL3 too; 'LCXL LEDs' is generation-aware (LCXL3 -> lcxl3-driver, original -> lcxl-leds-watch) - lcxl-path: follows the v3 path LCXL3 -> driver's 'ParVagues LCXL3' virtual port -> SC, falling back to the Midi Through road for v2
PLN (Algolia) authored
-
- 18 Sep, 2026 1 commit
-
-
Every tray GIG UP click POSTed to the Bridge, which ran 'gig-up.sh --converge --quiet' — flags this script never parsed, so the arg guard rejected it with 'unknown option' and exit 2, output swallowed by the Bridge's capture. Net effect: Pulsar opened (PRE_APPS, before the POST) and nothing else ever booted. The script and its caller had drifted apart on paper only — this is the contract, written down.
PLN (Algolia) authored
-
- 17 Sep, 2026 2 commits
-
-
The double click was the appindicator extension's, not ours: it reserves double-click for SNI Activate (instant menu) and delays single-click by 400 ms, so a single click feels dead. Nothing to fix tray-side; documented in the changelog. - BRIDGE_QSS restyles the Qt-drawn popup on dark desktops: tonal surfaces, hairline borders, hover pill, magenta on the engaged radio; circles for radio groups, squares for boolean toggles. Contrast measured: body 14.7:1, disabled 6.4:1, radio ring 3.3:1 (bumped from 2.3:1 in review). - stat rows and the thermal tooltip read in tabular digits. - every user-visible string loses its em dash (colons, parens, middots); same pass on gearbox.py labels (shared with the web Bridge) and the launcher blurbs; unknown gearbox placeholder is now n/a, not a bare dash. - mnemonics on the action rows (&Open Bridge, &GIG UP, &Restart SuperDirt, R&ig, &Start at login, Cut G&PU, &Quit); verified over DBusMenu that Qt exports them as underscores, so the shell-drawn twin gets them too. Literal ampersand does not survive the export, so the copy avoids it.
PLN (Algolia) authored -
The GUI launcher started SuperDirt, Ardour and Pulsar as raw processes and never touched the systemd --user layer, so a tray that exits 0 when the shell takes its host away (perf-tray, Sep 14) or a user-closed MIDI lens (exit 78) stayed dead: the only convergers were tools/gig-up.sh --converge, rig-doctor and the Bridge watcher, and the Bridge was dead too. Three days of launches "worked" with no tray and no lens. ensure() now refuses to start a unit whose proof process already runs outside it — which is what makes it safe for the launcher to call --ensure --apply after scsynth is up instead of spawning a second SuperDirt through parvagues-sc.service. --status agrees, so a launcher-started SuperDirt is no longer a false problem row. gig-up.sh converges right after the ready-wait, and honours the midiviz close-latch (the tray's MIDI Monitor button is the way back within a session).
PLN (Algolia) authored
-
- 11 Sep, 2026 1 commit
-
-
open_term() launched sclang directly, bypassing parvagues-sc.service and its 10-pipewire-jack.conf drop-in. With jack2 installed, ldconfig hands scsynth the real libjack, it finds no server and starts jackd on hw:sofsoundwire, which takes the card from PipeWire by D-Bus reservation. Meters move, nothing is audible (raw jackd -dalsa bypasses ALSA UCM, so the SOF speaker route is never enabled), and Bluetooth cannot work at all since jackd has no BT backend. Also adds tools/parvagues.desktop so the ordered launcher is one Super-key away; nothing had ever been installed in ~/.local/share/applications.
PLN (Algolia) authored
-
- 07 Sep, 2026 15 commits
-
-
PLN (Algolia) authored
-
PLN (Algolia) authored
-
PLN (Algolia) authored
-
"i see the parvagues tray icon, but doesnt react when i click" — twice, with the unit green both times. Three findings, in the order they mattered: - The menu WAS exported and populated all along (19 rows over com.canonical.dbusmenu). What made it useless is the style: with QT_QPA_PLATFORMTHEME=qt5ct and no qt5ct config under the unit, Qt picks `qt5ct-style` with a bare #efefef palette — a 2009 grey box on a Yaru desktop. Now: Fusion, plus an explicit dark palette when the desktop asks for dark (GNOME's color-scheme is the one setting that knows). - The tray host on GNOME is an EXTENSION, and it registers its StatusNotifierWatcher seconds after login. perf-tray printed "No system tray available", exited 1, and only Restart= saved it 3 s later. A slower login loses that race. Now it waits up to 120 s, polling on a QTimer (availability arrives over D-Bus, so the event loop has to be spinning). - New check `perf-tray reachable`: unit active, item REGISTERED with the live pid (a stale item from an old pid looks identical on screen), and the menu populated over dbusmenu — the only one of the three that a click needs. Negative-tested against a stopped tray. Also: - `wireplumber churn` check: starts per hour, and who asked for them. The existing checks could only see the wreckage (a failed unit, a dummy sink); this sees the cause while audio still works. It FAILs on tonight's 48. - The kwin midiviz-pin check asked KDE questions on a GNOME session, so it warned forever about a file that will never exist. Desktop-aware now: a check that cries wolf is one nobody reads on the day it is right.
PLN (Algolia) authored -
The rig is multichannel by design: orbit k is the pair (out_{2k-1}, out_{2k}), wired to its own Ardour track at a gig. Away from the stage that design means d1 is the only orbit that can reach a sink — and on a freshly booted laptop (2026-09-07) not even that: scsynth had NO links to any sink, so the whole box was silent with every check green. - tools/fold-orbits.py: fold all orbit pairs onto one stereo sink, summed at unity. Idempotent, and it VERIFIES — pw-link exits 0 for "File exists" and for a port that vanished mid-call, so its exit status proves nothing. The channel count is discovered from the graph (28 today, 12 in an older comment, 2 on stock SuperDirt), never assumed. Refuses while Ardour runs, which owns the mix and prunes SC->hardware links every 2 s. - gig-up.sh --headphones: SuperDirt -> fold -> Pulsar. No Ardour, no fader restore; the fold becomes the readiness gate, because it IS the whole monitoring path. - gig-preflight: `monitor path` check — stage (Ardour receiving on N tracks) or headphones (fold present), and it asks fold-orbits rather than re-deriving the port math. - 18 tests, fixtures captured from the real graph: `pw-link -lo` marks links by INDENTATION only, so a grep of it fabricates relationships, and a source port printed with no indented child is the exact state this tool fixes. Measured, not assumed: 14/14 orbits arrive at the sink (bd swept d1-d14, peak per orbit), and the links survive 32 s of autoroute reconciling.PLN (Algolia) authored -
First production run of the failed-units check flipped the whole set- readiness verdict on apport-autoreport.service — honest but wrong: the rig runs entirely in user scope, and system-scope corpses (apport, snapd chores) rarely gate a set. User scope stays a FAIL.
PLN (Algolia) authored -
An scsynth whose pipewire-jack client loses its startup race gets no negotiated format (quantum 0) and busy-spins its data loop at ~95% of a core at RT priority, unit green, process alive. Hours of that starved the SOF DSP's IPC until the firmware wedged and every sink vanished. Same config also boots clean at 1.5% — a probabilistic race, so the durable answer is detection + self-heal: - sc-watchdog spin detector: /proc CPU deltas every poll (zero forks), after 10s sustained >=85% ONE pw-top shot decides — quantum>0 is a musician playing and is never touched; quantum 0 is the spin (restart via the shared rate limit). Suite 15/15 incl driven-guard. - rig-doctor: SOF DSP IPC-timeout check (with the no-root cold-boot cure), failed-units check (start-limit trap, negative-tested), scsynth orphan-spin check on the same quantum oracle. 51 checks. - start_and_midi.scd: mi global effects measured LIGHT (all=1.3% vs off=1.7% idle, 5 boots) — acquitted, kept ON, gated behind PV_MI_FX so the next suspicion is a knob-flip, not a guess. - CHANGELOG Sprint 8 + backlog closure.
PLN (Algolia) authored -
The PRUNE ran unconditionally every 2s. With Ardour closed it deleted SuperCollider's ONLY output links, and a linkless pipewire-jack client self-drives its data loop into a 100%-CPU error spin at RT priority. Hours of that starved the SOF DSP's IPC until the firmware wedged (IPC timeout -110) and every sink vanished (2026-09-07). 'SC must never reach hardware' is Ardour-session policy, not idle-laptop policy: routing and pruning now both require Tidal tracks in the graph.
PLN (Algolia) authored -
Sprint 7 covers the morning: mi-UGens s.sync, the preload pointed at what gets played, three stacked bugs in its freshness check, 198 AppleDouble deletions, s.latency 0.3->0.2, and the wireplumber start-limit outage. Archive entry carries nine learnings, the load-bearing ones being that pgrep costs 53-59ms a call here while the real trick is the cheap prefilter (the regex on every comm is WORSE than the pgrep it replaced); that case-in-a-variable does no alternation; that start-limit-hit is this box's signature trap and retrying is the one action that cannot work; and that a valid measurement can be silent, because mute sits downstream of all DSP while a null sink lies. Memory: verify-the-leg-that-breaks gains the three 2026-09-07 instances plus two rules (ask why two numbers disagree; a tool owns the commands it prints). rig-state records the reset-failed lesson and the BT-by-name detail.
PLN (Algolia) authored -
PLN: 'media keys dont work' + 'tried bluetooth headset couldnt get sound there'. Cause was neither: wireplumber failed (start-limit-hit) Sinks: 100. Dummy Output <- the ONLY sink 0 bluez objects pipewire is the graph; wireplumber is the session manager that puts DEVICES in it. Without it there are no device nodes at all -- every hardware sink vanishes, PipeWire invents Dummy Output, media keys act on nothing, and a Bluetooth headset cannot appear however well it pairs. It reads exactly like dead hardware. The trap is StartLimitBurst=5 / 5min with Restart=on-failure. Five hand-issued restarts inside two minutes exhausted it, and from then on restarting AGAIN cannot work. Cure is reset-failed, then start. Third instance of this exact shape in one session after parvagues-sc twice: on this box, 'X will not start' means check reset-failed first. rig-doctor's check_pipewire tested pipewire on PATH, pw-jack on PATH, and pipewire.service is-active -- and pipewire.service was ACTIVE for the whole outage, so the check was green while the box had no audio at all. Presence versus function again. It now also asserts wireplumber is active and that the graph holds at least one real output (alsa_output/bluez_output, never auto_null/Dummy), FAILs with the reset-failed cure in the fix field, and WARNs on a dummy or unset default. It caught a live regression the first time it ran. The headset itself was only MUTED: bluez_card active profile was already a2dp-sink, and the sink read '[vol: 0.20 MUTED]' -- per-device state wireplumber persists. Unmuted by NAME, because the BT node id moves between calls while the device reconnects. Not caused by the quantum/mute testing, which ran at 00:10 with no BT device connected and left force-quantum verified at 0. latency-lens --print-config used to hand over 'systemctl --user restart pipewire pipewire-pulse wireplumber' with no warning, which repeated is precisely this trap. It now says ONCE, states the consequence, gives the cure, and points at the runtime setting that needs no restart. Doctor: 48 checks, 0 fail, 10 warn, 38 pass.PLN (Algolia) authored -
The comment read 'increase this if you get late messages', and the rig had 136 of them on 2026-09-06, so the arrow only ever pointed one way. Value history: 1 second, then 0.3, never revisited. Those 136 were not a latency shortfall. Two bursts (64 at 19:58, 72 at 20:03) with values DECAYING 8.1s -> 0.65s: a backlog draining, not a trickle of near-misses. Both sit against SuperDirt restarts, and the 20:03 one is the watchdog's spurious restart-on-boot fixed in b9564095 -- SC restarted, its clock reset, Tidal kept its logical clock, the backlog flushed as 'late'. The documented reason to pad this was a misdiagnosis of a bug that no longer exists. 0.2 is SuperCollider's own documented default and this is a direct tax on how a ctrl+enter feels. Lower is fair game; the floor is whatever keeps 'late' at zero under the densest material. Clean boot after the change: 0 lates. latency-lens now counts 'late' per candidate alongside pw-top's ERR, and a late rejects a quantum on its own: ERR is the audio graph missing its deadline, late is the MUSIC being handed over too late to play on time. Different failures, and a quantum decision wants both. Matched as 'late <digits>' -- an earlier hand grep for the bare word scored six hits on metalPlate.scd and inflated the count.
PLN (Algolia) authored -
Deleted 198 AppleDouble resource forks named zz._*.wav, on explicit owner instruction. Every one confirmed by file(1) magic before removal rather than by name pattern -- 198/198 candidates came back 'AppleDouble encoded Macintosh file', zero false positives, 0.3 MB. 69 were in playable banks (rhadamanthe_fx 227->189, _divers 314->293, _vocal 77->72, _melo 32->30); 129 were under Samples/baba/__MACOSX/, whose emptied dirs were rmdir-ed. Sample indices were untouched: the zz. prefix sorted them after every real sample, which is presumably why someone renamed rather than deleted. The boot is now free of the 'WARNING: File reading failed' wall. Then three faults in the freshness path, each hiding the next: 1. preload.scd's mismatch banner said 'whitelist is STALE, regenerate it' when the cause was unreadable files, and regenerating provably changed nothing. Now reports 'N bank(s) DID NOT FULLY LOAD', separates got==0 (whole bank) from 0<got<expected (folder changed, or some files are not audio), and prints the file(1) one-liner that distinguishes them. 2. check-preload.sh compared bank NAME SETS and never counts. The plan said rhadamanthe_vocal=77 against a folder of 72 and the checker printed 'ok' while the boot said 'expected 77 files, got 72' -- and --fix refused to regenerate, because by the name-set test nothing had changed. The everyday way to hit this is dropping a new pack into an existing bank: every new file lazy-loads mid-set under a green check. Now compares name+count pairs and prints 'plan N -> disk M' per drifted bank. 3. The bank-name regex was case-blind. [a-z0-9_]+ truncated rampleA0 to rample and collapsed every vocalOoh1/vocalScatJ into one 'vocal', which is why the checker said 111 banks while SuperDirt loaded 124. 124 was always the truth; the regex was wrong in both directions, undercounting the plan AND inventing two bank names that do not exist. bank_counts() had inherited it, so drift in a camelCase bank stayed invisible even after fault 2 was fixed. Three numbers now agree: checker 124, boot '124/124 banks OK in 5.2 s', independent parse 124/124/no gap. Drift tested both directions on a camelCase bank, plan byte-identical afterwards.
PLN (Algolia) authored -
The documented path did not exist; resolving it cost a lookup while building gen_setlist.py. Also points at the judge_specs ear lists, which is where a gig lives before its tracks.json is built -- the cosmicfest set among them.
PLN (Algolia) authored -
The preload warmed a setlist FILE, and the only one was armada/setlist_opal2026 .txt -- the 16 tracks of one August gig. So the plan was fresh, correct, and aimed at the wrong set. Measured: driving rose_rouge logged 41 'reading soundfile as needed' lines in 180s, every one of them the rose bank, while ZERO of the 60 warmed banks lazy-loaded. That is check-preload.sh's documented failure one level up. There the plan was stale because two tools disagreed about the setlist; here the plan matches its setlist exactly and the SETLIST has drifted from what gets played -- which no plan-vs-setlist freshness check can see, because the two agree. gen_setlist.py computes the list instead of maintaining one, from the canonical records in authority order: <www>/content/lives/<year>/<slug>/tracks.json, which carries both a date and each track's exact repo-relative file; then armada/tide-table/judge_specs/*_setlist_ear.json for gigs whose tracks.json is not built yet -- where the cosmicfest set lives, 'THE ground truth, 14 tracks', with rose_rouge at #5. Ear lists carry no gig date, so they are included and flagged rather than dated by guesswork. 60 banks -> 111, 16 tracks -> 39. 51 banks were one first-play from a disk read. rose is covered. check-preload.sh now computes its own list rather than borrowing set-coherence.setlist_tracks(), because the two ask different questions: a cheat sheet wants tonight's running order, a preload wants anything that might get played, and the cost asymmetry is total. PV_PRELOAD_SETLIST / PV_PRELOAD_MONTHS override. Not a fourth setlist parser -- the same setlist_samples parser, fed a wider list. Cost of over-covering, measured: 122/124 banks OK in 5.0s (was 60/61 in 2.6s), scsynth RSS 4.8G on a box with 62G total and 40G available. Logs two findings from the same boot: 64 AppleDouble resource forks named zz._*.wav in rhadamanthe_fx/divers/vocal, which fail to load AND occupy sample indices (left alone -- the zz. prefix looks like a deliberate rename, so deleting is the owner's call); and preload's COUNT MISMATCH banner, which reports those 2 real load failures with the wrong diagnosis.
PLN (Algolia) authored -
On every clean boot the server said, 84 lines of it: *** ERROR: SynthDef global_mi_verb2 not found FAILURE IN SERVER /s_new SynthDef not found 3 SynthDefs x 14 orbits x 2 lines. So '# verbwet', '# cloudswet' and '# ripplesreson' did nothing on any orbit, for the whole session -- silently, because a missing global effect is an absence of effect rather than a noise you notice. mi-UGens was installed correctly all along (11 .so + 11 classes) and the SynthDefs were defined correctly in start_and_midi.scd:273-296. The bug was ordering: SynthDef(...).add is ASYNCHRONOUS -- it compiles locally, sends /d_recv and returns -- while the very next statement sends /s_new for those names via initNodeTree. The /s_new overtook the /d_recv. One s.sync before the registration, legal inside s.waitForBoot's Routine; the idiom was already commented out twelve lines up. 84 errors before, 0 after. spectral-freeze above deliberately does NOT need it: it replaces a def whose synths are built per event, long after /d_recv has landed. Monitors it as gear, since presence checks could not see any of this: - mi-UGens extension now enumerates the 11 plugins AND 11 classes we use and names whichever is missing. d.is_dir() is true whether the directory holds a working install or a stale README. - SuperDirt boot errors (new) scopes the journal to the CURRENT boot and FAILs on SynthDef-not-found / FAILURE IN SERVER, naming the distinct defs rather than one line per orbit. Generic question, so it catches the next async-ordering bug too. WARNs when parvagues-sc is down, which is the correct on-demand state before a set. Both tested in both directions: FAIL on the recorded pre-fix journal window naming all three defs, PASS on the post-fix boot. That test is what caught 're' never having been imported in rig-doctor.py -- the new code was its first user, so the check would have crashed the doctor the first time it found something. Doctor: 47 checks, 0 fail, 10 warn, 37 pass.PLN (Algolia) authored
-
- 06 Sep, 2026 19 commits
-
-
Went looking for latency to shave in the PipeWire quantum. The quantum was not the problem. preload.scd did not exist on this box. start_and_midi.scd:259 tests for it with File.exists and silently takes the other branch -- 'no preload.scd, lazy-loading samples on demand.' Driving rose_rouge headlessly for three minutes logged 46 'reading soundfile as needed' lines, one every 5-12s, each a disk read landing on the audio thread; the SuperCollider node's pw-top W/Q hit 1.300 at the stock 1024 and 1.390 at 512, i.e. missing its deadline regardless of buffer size. check-preload.sh named it exactly: '60 bank(s) would be read from DISK on first play (crackle, mid-transition)' -- the same sentence its own header attributes to a crackle that debuted at a venue. Generated with check-preload.sh --fix (60 banks, 16 tracks) and verified at the next boot: 'PRELOAD: 60/61 banks OK in 2.6 s'. The mechanism was never broken; gig-up.sh already regenerates the plan and the checker already exits non-zero. gig-up.sh had simply never been run on this box. Re-running the identical load after the fix left 29 lazy reads, ALL of them the rose bank -- because rose_rouge is not in setlist_opal2026.txt. Zero of the 60 preloaded banks lazy-loaded, so the plan works; it warms the OPAL set while the track this box actually plays sits outside it. Logged, with two more findings from the same boot: preload's own COUNT MISMATCH banner firing on a sixty-second-old plan, and three global mi-UGens FX synthdefs failing to load at every boot while rig-doctor's mi-ugens check stays green. Adds tools/latency-lens.py, which implements the documented quantum method (lowest quantum holding zero xrun delta and max W/Q under 0.75) against pw-top, sets clock.force-quantum at runtime and restores it, and never edits a config. It refuses to run unless the default sink is muted and re-checks that every second for the whole run: a valid quantum test needs the real hardware device driving the graph but does not need the speakers.
PLN (Algolia) authored -
parvagues-sc.service is Type=simple, so systemd calls it active the instant sclang execs -- but scsynth is sclang's child and appears ~8s later. The loop acted at MISSES_TO_ACT*POLL_SECS = 6s, so every healthy start earned a restart: 23:17:12 scsynth GONE (3 polls) while parvagues-sc.service is active -- restarting (0 prior in window) 23:17:21 scsynth up after 8s '0 prior in window' on a start where nothing was wrong. The restart was not the damage; the rate limit was. Each clean start spent two of systemd's StartLimitBurst=3, so a second start inside StartLimitIntervalUSec=5min hit the limit and left the unit failed/start-limit-hit -- refusing to start at all until reset-failed. On stage that is indistinguishable from a dead rig. BOOT_GRACE_SECS=30, measured from the unit's own ActiveEnterTimestamp, and it says 'holding off' in the journal rather than waiting silently. Misses keep counting through the grace so an expiring grace acts at once. The per-poll is-active became one show returning both fields: 7.0ms -> 9.4ms, +0.12% of a core, measured not assumed. Also cures the watchdog of the same pgrep -x that cost protect a tenth of a core -- 52.9ms per poll while SC is up (3.0% of a core WHILE PLAYING) against 20.8ms for one stateless /proc/*/comm pass, zero forks. Duplicated from parvagues-protect rather than shared: that script is copied to /usr/local/bin and runs as root, so it must not source anything from a user-writable repo. rig-doctor's check_protect now asks whether protection WORKS: the running daemon's live CapEff from /proc must include cap_dac_override, and the daemon's own --check must pass. It was green all through the day the guard could not write a single oom_score_adj, because it tested that two files existed. Verified end to end on the rig: protect caught pids created 9min after it started (oom:200->-1000 on sclang[257669]), and the watchdog held off at 6s and spent none of the rate limit. Suite 11/11 incl. a new slow-boot regression case; rig-doctor 46 checks, 0 fail.PLN (Algolia) authored -
219.1ms per 2s tick, 6 forks, with NOTHING running to protect. None of it was protection: three pgrep -x at 59ms each (pgrep reads cmdline for every process on the box, and it ran once per target), plus cat, two chrt -p reads and an unconditional prlimit per pid, each in its own command substitution. Now zero forks in the steady state: one bash pass over /proc/*/comm, sched from /proc/<pid>/stat fields 40/41, prlimit only when /proc/<pid>/limits says so. 28.5ms/tick, 1.42% of a core at the unchanged 2s interval. The interval is deliberately untouched -- the 7.7x came from forks alone, so the responsiveness that re-protects scsynth before its first sound was not traded for battery. Verified differentially against the old pgrep path with a decoy fleet (ardour9, ArdourGUI, ardour-8.6 must match; ardour-decoy, sclang-notreally, scsynthx and a bash whose path contains 'ardour' must not) and against chrt -p / cat on live scsynth+sclang. Needs sudo tools/install-protect.sh to take effect. Also logs two findings from the same measurement pass: sc-watchdog restarts SuperDirt on every clean start (6s patience vs 8s boot) and two starts in five minutes hit StartLimitBurst, leaving the rig unstartable; and the other two reconcile loops cost 5.3% between them with real event sources available.
PLN (Algolia) authored -
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.
PLN (Algolia) authored -
Reinstalled after adding CAP_DAC_OVERRIDE and nothing changed: the unit on disk was correct, daemon-reload had run, NeedDaemonReload said no — and the running process still held the old three capabilities, same PID and start time as before the install. `systemctl enable --now` starts a STOPPED unit and does nothing to a RUNNING one. So every re-install silently kept the previous daemon, with the previous unit's capabilities and the previous script's inode, while printing success. - restart instead of enable --now (it holds no ports and makes no sound) - assert the LIVE /proc capability set after restart, and say plainly that protection is decoration until that line reads ok Third layer of one lesson tonight: a correct file on disk is not a correct process in memory.
PLN (Algolia) authored -
Four items closed on the portable-rig front, and one new one opened: no install step fetches the sister repos CLAUDE.md documents, which is why three separate manual fixes were needed tonight for one missing line of setup.
PLN (Algolia) authored -
Installed the protection daemon and it reported 'protected: ardour[...] oom:FAILED(need root, have uid 0)' every two seconds. It runs as root; the message was misdirecting. uid 0 bypasses file permissions via CAP_DAC_OVERRIDE, and the unit's CapabilityBoundingSet listed only CAP_SYS_RESOURCE/SYS_NICE/IPC_LOCK — so the bypass was gone. /proc/<pid>/oom_score_adj is mode 0644 owned by the process owner, so every write returned EACCES while id -u still said 0. scsynth and sclang looked fine only because they were already at the target and no write was attempted. The daemon had never successfully protected anything. - unit: add CAP_DAC_OVERRIDE to bounding + ambient sets - failure messages print the effective capabilities, not 'need root' - a FAILED sweep logs once per distinct message instead of 43200 times a day, which is what the file header already said it refuses to do
PLN (Algolia) authored -
Sprint 2 (the board says what is playing), archive entry with the night's learnings, and the backlog corrected where the fader feed was listed as unbuilt. Records the one leg still unverified: Ardour's own echo has not been observed arriving, only proven linked.
PLN (Algolia) authored -
The driver's own startup line said the gap out loud: "row A stays absolute: A1-A4 are Ardour's, we hold no truth for them". So those four rings sat at a flat rest colour while every other cell painted its value. Two sources close it, and they are complementary rather than redundant: 1. A forwarded CC is an observation. When PLN moves A1-A4 the driver TRANSLATES that value on its way to Ardour, so at that instant it knows what the fader holds. It always recorded it in self.values; only ring_colour's Ardour-owned branch threw it away. Now it also marks the cc observed, and the ring breathes by audibility from the first touch. 2. Ardour echoes what it did NOT get from us. `<Protocol name="Generic MIDI" feedback="1" motorized="1" active="1">` was already on, and Ardour will not echo a change back to the surface that caused it — so source 1 covers the hand on the knob and source 2 covers the GUI, the mouse, automation and a session load, which is exactly where source 1 goes stale. The feedback leg needed its own port. The driver already sends into Midi Through (14:0) and Ardour's Generic MIDI reads from there, so listening on that loopback would have fed the driver its own CCs back: no loop is possible now because the send path and the echo path share nothing. And it is the one binding here that lives in JACK rather than ALSA, because Ardour's control port is a JACK port and only PipeWire's bridge makes ours visible there — so the bridged name, which carries a playback index nobody should guess, is DISCOVERED by suffix on every pass and re-asserted like every other binding on this rig. Verified on the live rig, Ardour closed then reopened: the port publishes (client 132 'ParVagues LCXL3 FB'), the bridge exposes it, the reconciler finds and links it unaided, and it re-links within 5 s of a driver restart. Absent Ardour it stays silent and returns False, costing nothing but the rest colour.
PLN (Algolia) authored -
'The refs work as designed' held only on the box that authored the image. resolve() never tried the derived twin when the absolute source was absent, so every ref resolved to null on the XPS24 with the mp4 already synced. 0 of 5 targets resolved before the HUD fix, 5 of 5 after.
PLN (Algolia) authored -
The visuals entry was wrong in its premise, and the correction matters more than the task: xps22 came up and showed that `visuals/scenes/` already exists (14 mp4s, 197 M), that the HUD's scenesDir already points at it, that the gifs were already compressed on 2026-08-29, and that committing them was DELIBERATELY rejected in .gitignore with the reason written in. The "16 dangling url refs" are working as designed — scenes.js:_preferDerived() documents that the header points at the SSOT source and the HUD substitutes the derived mp4. So the plan "one home, compress, rewrite refs repo-relative" proposed doing three things that were already done or already decided against. The remaining work is a 197 M copy, and the entry now says so. Kicked a fence before reading why it was there. Jig decision 1 answered: ONE STABLE JIG, swept every time. Recorded with the two consequences that follow — ARDOUR_SESSION stays fixed, and "self-contained per gig" must now come from take metadata, which folds question 2 into it. Also opened: the Ardour fader feed's verified findings (Ardour has already learned CCs 13-16; only "midi-feedback"=0 withholds the values; Midi Through is a loopback so the feedback leg needs its own port), ../tidal-ears not being cloned here, and pytest being absent from the very interpreter the rig runs.
PLN (Algolia) authored -
PLN, asked whether a `Tidal 12` parked at -inf was a mistake or intent: "tidal 12 -inf is random i might turn on/off any fader its not signal throughout perf it mioves always and i save random". So the question was wrong. Whatever gain Ardour saved is wherever a hand left it mid-set; a session's at-rest mix is noise, and there is nothing to diagnose. Which is why the readiness gate could only ever REPORT it. Measured just now, the drift is not one fader but NINE of twelve, five of them at -inf: 02 03 04 05 08 12 fully silent, 06 07 10 down 40+ dB. A mix that scatters every set needs a mechanism, not a warning. So restore it, in the window the source sweep already owns — Ardour closed. fader-baseline REFUSES while Ardour runs (writing under a live session desyncs it from the desk, and the desk wins on next touch), which makes "post-close" and "pre-launch" the same safe moment. Full restore to the 2026-08-02 baseline per PLN's call, not just a rescue of dead faders: it keeps a .prefader.bak, is idempotent, and holds a 0.5 dB tolerance so it never churns. Verified both branches against the live rig: --check reports the 9-fader drift, and --restore correctly refuses with Ardour open.
PLN (Algolia) authored -
Motion on the board now means one thing: THIS IS MAKING SOUND. It used to mean only "a DJF sits at its zero mark", because animated() hard-returned False for every role but family_filter. Three laws, all at bar rate (the slow breath, not the beat one): fx / fx2, every orbit breathe whenever the knob is off zero level, d9-d12 only breathe whenever the level is not off. Only those four, because d1-d8 levels live on row D and FADERS HAVE NO LEDS — painting them was already called a no-op lie in paint_cell, and asking for it here would have been the same lie one layer up boolean cells dark until 127, breathing at 127 That last law needs to know what a control IS, so parse_kinds() reads it off the track: a cc reached through `# effect (... "^NN" ...)` is a dial, a cc reached through midiOn/midiOff is a switch. Tidal applies midiOn's function while the CC reads high, so such a control is boolean however many steps the hardware sends — and a ring fading up across a range the music ignores is a ring that lies about the sound. When one cc is used both ways, continuous wins: the value demonstrably matters somewhere, so fading is the honest paint. The breath's CEILING is the value (_breathe_to): the brightest instant equals _lightness(value) and never exceeds it, so "lightness carries VALUE" still holds while motion carries engagement. A knob at 30% breathes dim. Levels stay honest. Ardour owns cc 13-16 and has not told us their values, so ring_colour(value_known=False) keeps the flat rest colour rather than breathing on a guess — the oldest rule here is that the LEDs do not lie. The feedback feed that fills ardour_seen comes next; until it does, those four rings look exactly as they did. Also folds two defaults that had drifted: paint used 0 for an unseen control and the glow tick used 64. value_of() settles it — an unseen DJF is at its centre detent, because that is where the knob physically rests; anything else unseen is OFF, because engagement we have never observed must not be claimed. Checked against rose_rouge: B8+C8 (d8's stacks, the ones asked about) classify bool; A6/A7/A8's delay and squiz knobs classify cont; breath peak equals the static value at every level; DJF behaviour unchanged; unmapped still black.PLN (Algolia) authored -
tools/analyze_samples.py is a COMMITTED symlink into ../tidal-ears, which is not cloned on the XPS24. rglob could see the path and read_text could not open it, and _offenders() caught only SyntaxError — so a FileNotFoundError escaped at import time and the whole module failed COLLECTION. That is the exact failure this file exists to prevent, turned on itself: the guard reported nothing rather than reporting a gap, and a guard that cannot run guards nothing. Skip unreadable paths explicitly. Verified standalone on the rig interpreter: 3 tests pass, 256 files scanned.
PLN (Algolia) authored -
PLN (Algolia) authored
-
The night rose_rouge first played on the new laptop, this cost an hour and presented as three unrelated faults. Both jackd2 and pipewire-jack are installed, and ldconfig resolves libjack.so.0 to JACKD2's — nothing in ld.so.conf.d prefers PipeWire's. So a plain `ardour` on the JACK backend finds no jackd server, STARTS one, and jackd claims a card through the D-Bus device reservation. Observed: jackd came up on `hw:NVidia,3` and reserved Audio0, so Ardour's audio left by an HDMI port with nothing plugged into it, while scsynth sat in PipeWire's graph on the speakers. Two graphs that could not see each other — qjackctl listed Ardour and no SuperCollider, and nothing was audible from anywhere. And the ALSA backend is not the way out, it is the worse failure: it reserves BOTH cards, PipeWire releases them, and the laptop drops to a single "Dummy Output". No Tidal, and no Zoom either, on the machine that has to do both. That is the state this box was found in, and the reason all three symptoms had one cause. pw-jack only sets LD_LIBRARY_PATH to PipeWire's jack dir — precisely how the SC unit already arrives there (scsynth maps pipewire-0.3/jack/libjack.so.0). Same mechanism, same reason: ONE server, so the orbits and Ardour share a graph and the desktop keeps its cards. launch_bin gains an optional LAUNCH_WRAPPER, empty for every other app. Verified end to end: Tidal -> SuperDirt :57120 -> scsynth (28 outs, PipeWire JACK) -> ardour:Tidal 01..12 -> Master -> Speaker playback_FL/FR, with no stray jackd, both cards held by WirePlumber, no double path on d1, and check-audio-graph.sh green but for "no UMC on the bus" (it is not a venue).
PLN (Algolia) authored -
The Missing File modal is one dialog per dead source, cannot be suppressed by a flag, and sits between a click and a playable rig (#136). On 2026-09-06 it cost a launch outright — and invisibly, because launch_bin throws app output at /dev/null: Ardour sat for 13 minutes with 0.08s of CPU and no window at all, while the only thing that could have said why went to the bit bucket. So gig-up sweeps the session before opening it, never while it is open (Ardour saves its in-memory session over the edit on quit and would undo it). Idempotent: a clean session prints "nothing to do" and writes nothing. The mount check IS the safety story. --allow-archived overrides the tool's "this survives elsewhere, restore it instead of dropping the reference" refusal — the right answer for an accident, the wrong one for deliberate archival, and the only thing telling those apart is whether the archive is actually there. With the mirror unmounted the tool cannot distinguish a filed take from a lost one, so it is never handed the flag. A reference is the last breadcrumb pointing at a take; dropping one blind turns a restore into a forensics job. Neither branch ever blocks the gig. Also writes down what tonight cost, in the cheatsheet where it will be reread at setup rather than in a log nobody opens mid-gig: Ardour's per-port Incoming MIDI gates the faders no matter how right everything else is; which of the three LCXL ports is the correct one and why the other two are wrong; that no-sound-no- highlight is a stale GHCi (tidalcycles:reboot) and not the rig; and that boot-superdirt would start a second SuperDirt to fight parvagues-sc for :57120.
PLN (Algolia) authored -
PLN, setting up: "ill often unplug replug another port a controler when setting up, it needs to resist that, atm i unplugged replugged lost the led" — and then, with the surface dark, "movign faders dont move sound for now". A replug destroys the kernel's sequencer client and rebuilds it, dropping every subscription on it. rtmidi is never told, so the driver kept its handles, kept printing "translating", and kept painting into a void: 129:0 had no "Connecting To" and the input leg no "Connected From", while the board sat there dark and deaf. reconcile() reported healthy all the way through, because the leg it watches — the virtual port into SuperCollider — is virtual-to-virtual and survives the event untouched. The half that breaks was the half nobody checked. Names cannot detect this and that is the trap: the board came back as client 24, the SAME number, so find_ports() answered, in_name still matched, and every string looked right. Only the subscription knows. So the reconcile tick now checks the board link first and relinks on loss: reopen the Surface, re-assert DAW mode and the relative rows, repaint, relabel. Board before SC, because a dark surface is the louder failure. Two things learned by testing it rather than trusting it: - ANY-subscription is not a health signal. The first cut asked "does the DAW port have a subscriber?" and answered True on the exact state it exists to catch — a replug leaves DEBRIS, and aconnect went on printing the board wired to clients 132/133 that no longer existed while our own ports sat detached. It now matches aconnect's pid= against os.getpid() and requires OUR client on both ends: no input is a dead controller, no output is a dark one. - A relink does not re-seed. Seeding pushes values downstream, and downstream is the one path that never broke — SC already holds them. Verified by cutting both legs with `aconnect -d` (what the replug does to the subscriptions) and watching it come back within one tick.
PLN (Algolia) authored -
Tonight's launch died on the Missing File modal, so the references got counted properly: 294 missing sources, 270 of them alive on the Freebox mirror (53G, no file under 1MB), and 24 that exist nowhere — all of Take101, 12 orbits x L/R, already written off in 041 as "tests at best". The plan promotes the drop-missing-sources doctrine from a one-off repair to a standing invariant: the session holds tracks, routing and levels, and zero audio sources at rest. Structure comes from a template (correct by construction, rather than mutating a session back to empty and having to be right about what to remove); state is kept by an idempotent sweep that runs at LAUNCH as well as close, because a close-only hook is bound to the least reliable moment there is and a crashed gig would either strand stems or eat them. Also names the tension it has to live with: the tool refuses to drop a reference whose audio survives elsewhere, which is exactly the state deliberate archival creates. --allow-archived is the answer, and the rule is that only a caller which has itself verified the copy may pass it. A human running it blind stays refused. And a correction 043 could not have known it needed: the one-liner recorded there now refuses, because the eleven other takes moved to the mirror after it was written. The doc was right when written and wrong now — drift a plan should catch instead of a gig.
PLN (Algolia) authored
-