- 05 Sep, 2026 40 commits
-
-
Audited what could have consumed the phantom SOUNDCHECK entry positionally. Answer: nothing did. The two real consumers of setlist.entries() are safe -- take-segments.py builds a dict keyed by path stem (a phantom adds one unused key) and migrate-columns.py calls tracks(), not entries(). No shipped artifact was off by one. But the audit found a different positional coupling, live and undocumented. opal_title_map() joins tracks.json's 1..N performance order against segments_v4.json's perf_track. OPAL was played as 15 tracks; the release cut Desire, and segments_v4 renumbered around the hole -- its perf_track keys are 1..13 and 15, with no 14. So the join lines up only while Desire still holds performance slot 14. Measured both ways rather than argued: as-performed yields 14 mappings including REVOLUTION; drop Desire to make the page match the release and it falls to 13. REVOLUTION's title is LOST -- silently, with nothing mislabelled, which is what would have made it hard to notice. PLN chose as-performed today as a documentary call about the page; this join quietly depended on the same answer, and nobody knew the two were connected. So: the assumption is written down where the join lives, and three tests hold it -- every released track gets a title, the encore by name, Desire maps to nothing rather than borrowing a neighbour's, and the hole at perf_track 14 is still really there. That last one matters: if segments_v4 is ever renumbered densely the reasoning stops holding, and the right outcome is a red test rather than a quietly missing track.
PLN (Algolia) authored -
PLN: "needs basic menu or at least top right X to close, and its too sticky atm if i close i wanna close it i guess maybe X and a [x] where x goes and comes and is the 'stick above' feature, but it anyway is all desks always." No menu -- two glyph targets in the header's right edge, in the same micro font as everything else: "[x]" toggles always-on-top (the x IS the state) and "X" closes. Dim by default, brighter under the pointer, which is the only affordance a frameless window can offer. T toggles the pin from the keyboard; Q and Escape already closed it and still do. Controls win over the drag. The whole surface is a drag handle, so without an explicit hit-test first the X would only ever have moved the window. Targets are laid out from the right edge with the glyphs centred inside them, not sized to fit the glyphs. Sizing to the glyph gave an 8px-wide X -- measured -- which is fine to look at and unhittable mid-set, and at 0.6 scale the two targets then had to either overlap or shrink below usable. Deciding targets first makes "disjoint and at least 18px" true by construction at every zoom, asserted across four scales and three widths. All-desktops is deliberately untouched: it is a KWin rule keyed on the app id, it is unconditional as PLN said, and toggling the pin must not disturb it. THE STICKINESS WAS NOT THE WINDOW. A clean exit is not a failure so systemd never restarted it -- but rig_units.ensure() starts every non-manual unit that is not active, and both gig-up and the Bridge watcher converge, so the lens came straight back with nothing in the output admitting why. Marking the unit "manual" would have answered the complaint by deleting the feature he asked for last week. So a deliberate close is recorded in $XDG_RUNTIME_DIR, ensure() honours the latch and says so, and anything that starts the lens on purpose clears it. That directory is wiped at logout, which is exactly the right lifetime: "always open" is about how a session starts, "closed means closed" is about what happens after he acts, and the two only looked contradictory. Closed for this session, back at next login, no state to remember to undo. Only a close the human asked for latches -- a compositor teardown is not an opinion. The latch path is a contract between three files that deliberately do not import each other, so a test asserts they agree rather than trusting three copies of an f-string. That test immediately earned its keep: rig_units.py used Path without importing it, on a line only reached once a latch existed. Also fixes a shadow of my own making: the new painter was called _paint_chrome, which is already a method on this widget, so it silently replaced the window-chrome painter and the selftest died on the signature. Renamed _paint_controls.
PLN (Algolia) authored -
Two unrelated failing tests, investigated in order. test_agreement_distribution_within_bounds asserted tracks_total == 73, a snapshot from before yesterday's catalog rebuild (73 -> 81 tracks). Bumping it to 81 would just re-freeze the same brittleness. Replaced it with test_tracks_total_matches_the_generated_catalog: assert the live view's tracks_total equals the CHECKED-IN catalog.generated.json's n_tracks. That file is a 1:1 derivation of this same view, so the two can only disagree on a real regression (corpus changed but catalog not regenerated, or the pipeline dropped/duplicated a row) — never on honest growth. Verified it still fails by corrupting n_tracks and re-running. test_real_backlog_coverage_does_not_regress: opal-festival-2024's recovered tracks fell from 13/14 to 3. The obvious suspect was e8f7066a (yesterday's tools/setlist.py soundcheck filter) — ruled out by inspection (e8f7066a touches only tools/setlist.py + its test, nothing under armada/tide-table) and by bisection: replaying backlog_setlists.py's ANCHORS against every historical backlog.md since the anchors were authored (5acf72f7, 2026-06-06) shows n_resolved holding at 14 across ten intervening commits and only breaking at 49e1b78e ("update: post cosmic", 2026-08-23). The real bug: ANCHORS keyed gigs by a raw 1-based LINE NUMBER pinned once against a single backlog.md snapshot. backlog.md is PLN's running journal, not a stable document, so every one of those ten commits that inserted lines earlier in the file silently shifted every anchor below it. Opal 2024's anchor drifted through blank lines and a lucky near-miss for months (the collected span still happened to contain the same real content), then 49e1b78e pushed the drift past the header entirely into the tail of the PREVIOUS gig's block, collecting 3 unrelated lines instead of the setlist. Fix: anchor by the header's exact TEXT, resolved fresh against the live file on every build() (resolve_anchors()), instead of a line number frozen at authoring time. Self-healing against drift elsewhere in the file; raises loudly if a heading is ever actually renamed or duplicated, rather than silently mis-anchoring. The three anchors that carried an explicit end-of-block bound (opal-2025/Latin-Heritage bleed, mephisteuf's commented block, the 38c3-toilet cross-check) now store that bound as a line-count SPAN measured from the anchor, since that span lives inside the block and doesn't move when the anchor does. tools/setlist.py's soundcheck filter (e8f7066a) is untouched and still yields 15 entries for OPAL 2026 — never the culprit here, just an unrelated same-day change that made a plausible but wrong prime suspect. Full suite: 468 passed, 2 skipped (465 baseline + 2 fixed + 1 new regression test), up from 2 failing.
PLN (Algolia) authored -
Eight commits of rig work that had accumulated on the branch while the shared checkout sat on it. Reviewed as a diff, not taken on faith: perf.sh stop DWIM-launching Pulsar into /usr/local/sbin -- 183 SIGABRTs between 08-19 and 09-05 because BASH_SOURCE resolved to the deploy path, and the perf watcher re-entered every 30s. rig_units.py neither LCXL painter may be auto-started blind; gig-up.sh's generation-aware chooser owns that decision. midiviz its own Wayland app id (desktopFileName, not applicationName), so focus and KWin rules can name this window and no other; plus the CC readout PLN asked for while wiring. logs 040/041 the phantom port, and the GIG UP rewire parked behind the Ardour double-launch bug. The four other branches were already upstream by patch-id. # Conflicts: # TASKS_DUMP.mdPLN (Algolia) authored -
PLN ran the root install at 20:06. Verified rather than assumed: the deployed /usr/local/sbin/perf-audio md5 matches the repo copy, the bug's fingerprints (nohup pulsar, SCRIPT_DIR) are absent from the deployed script, and prioritization is retained. Behavioural confirmation is the part worth keeping: the Bridge's perf watcher still reasserts every cycle, but now only prioritizes -- no launch attempts in the journal -- and the last Pulsar SIGABRT in coredumpctl is 15:58:44, before the deploy. The counter that ran 183 deep since 2026-08-19 has stopped.
PLN (Algolia) authored -
Structured archive entry. The keeper: two bugs hid each other for a month. One file's module-level sys.exit aborted pytest collection for the whole directory, so the obvious command ran zero tests while 273 passed file-by-file — and that invisibility concealed the file's own month-old red check, whose NAME was a complete bug report about a soundcheck being counted as OPAL's first track. A test nobody can see failing is worse than one that does not exist, because its name reads as reassurance. Fixing the harness surfaced a real correctness bug at no extra cost.
PLN (Algolia) authored -
Refreshes the resume state. The R1-R5 block from the first pre-compact is now partly stale — R2 closed (PLN raised the faders and saved; check-mix reports all 12 orbits reaching master), both R5 test items fixed, and the suffix question answered by evidence rather than taste. Three things landed after those resume points that a cold reader needs: pytest tools/tests/ had been running ZERO tests because one file's module-level sys.exit aborts collection, and behind that invisibility the OPAL setlist's first track was a soundcheck, so every tool reading it believed the set had 16 tracks opening on Quand on décolle. Both fixed; suite is 274 green in one command. Separated what is genuinely PLN's from what is merely undone, because that distinction is the whole value of this block. Four decisions block everything (--go on the 14, /desire specifically, CosmicFest tone, gig page as-performed vs as-released) plus one ears question about nine faders moving where four were broken. Four next moves need nobody, and the first is a 15-minute audit of whether anything consumed the phantom setlist positionally.
PLN (Algolia) authored -
Two bugs that were hiding each other, which is why both survived a month. test_setlist.py ended in a module-level sys.exit(), pytest raises that during collection, so 'pytest tools/tests/' aborted the whole directory and ran NOTHING while 273 tests passed file-by-file. And what that invisibility concealed was test_setlist's own failure, on a check whose name states the bug: OPAL's backlog opens with '## SOUNDCHECK / -- Quand on decolle', entries() took every item under the gig heading, and so every tool reading the setlist believed the set had 16 tracks opening on Quand on décolle. The recording has 15, opening on Ceci n'est pas Une Bombe. A test nobody can see failing is worse than a test that does not exist, because its name is a correct bug report that everyone reads as reassurance. Whole suite now runs in one command: 274 passed, 0 failed.
PLN (Algolia) authored -
Two bugs that were hiding each other, which is why both survived a month. THE SUITE RAN ZERO TESTS. test_setlist.py was a plain script ending in a bare module-level sys.exit(), and pytest raises that during COLLECTION — so 'pytest tools/tests/' aborted the whole directory with an INTERNALERROR and ran nothing, while 273 tests passed when invoked file-by-file. Every test in this repo was invisible to the obvious command. Guarded the exit under __main__ (the script still works run directly) and added one assertion so pytest actually sees the checks. Whole suite in one command now: 274 passed, 2 skipped, 0 failed. AND THE THING IT WAS HIDING: test_setlist had been failing all along, on a check whose name says exactly what is wrong — 'a deliberation note is not a track (the quand_on_decolle phantom)'. OPAL 2026's backlog section opens with ## SOUNDCHECK -- Quand on decolle and entries() takes every list item under the gig heading, so every one of the tools reading this setlist believed the set had 16 tracks opening on Quand on décolle. The recorded set has 15 and opens on Ceci n'est pas Une Bombe (segments_v3/v4 track 1). The parser now yields 15, in the right order. The fix is deliberately NOT the section whitelist entries() refuses to build — that one tried to enumerate which of PLN's prose headings are part of the set and died on '## Livecoding Techno DNB Nu-jazz'. This is the inverse and far narrower: naming the one phase that is definitionally not a performance. Three patterns, and skips print to stderr, because a dropped track and a skipped soundcheck must never look alike. Fixed the parser, never backlog.md — the backlog is PLN's own file and it was right. A soundcheck IS in his set list; it just isn't in his set. Verified by breaking it: neutered _NOT_THE_SET_RE, watched test_backlog_setlist_checks_all_pass fail naming the quand_on_decolle phantom, restored, 274 green. the first track was never the first track. sixteen tools agreed with each other and none of them agreed with the recording.PLN (Algolia) authored -
Both were pre-existing, unrelated to today's check-mix.py/gig-log.py work (spotted while running the suite for that change). test_orphan_orbits.py: 9 of 41 tests failed (16-row setlist vs a frozen "13", and 6 HAND_MEASURED orbit sets vs the parser). Root cause confirmed by git log + grep, not a parser bug: the setlist grew from 13 to 16 tracks since the fixture was written, and commit 2376e431 (2026-08-01, "d10 is the riser") added a d10 safe-riser idiom to 5 of the hand-measured tracks (bombe_dj, wap, you_my_sunshine, mafia_sans_serif, desire) after the 2026-07-28 hand measurement date; piment_bresilien went the other way (d10->d9, PLN's own request, commit 71bb9bc2/5e5a37ad). Fix: the setlist-length assertions are now derived from an independent sed-based row count of the setlist file (same pipeline check-tracks.sh uses) instead of a literal that rots every time PLN adds a track; the HAND_MEASURED dict and the vague_de_crime->bombe_dj orphan assertion are re-measured by hand (grep on each file's dN lines, not by copying the parser's own output) against the corpus as it stands today. No assertion loosened — 41/41 pass, same invariants, current facts. test_lcxl3_display.py: pytest collected 0 items. Cause: this was written as a plain script (module-level `check()` calls, run via `python3 tools/tests/test_lcxl3_display.py`), like test_setlist.py, with no `test_*` function pytest could find — a file that cannot fail under pytest, i.e. silent coverage loss. Converted to 30 ordinary pytest test_* functions (one assert group per logical check), same assertions, nothing dropped or loosened. Verified live: broke rec_line's hour rollover (divmod 3600->3601) and watched test_rec_line_formats_hmmss_past_an_hour fail with the exact wrong value (REC 1:02:02 vs 1:02:03), then reverted. Suite: 234 passed/9 failed/2 skipped -> 273 passed/2 skipped (ignoring test_setlist.py, a separate plain-script file, out of scope here — it already breaks whole-directory pytest collection via a module-level sys.exit and predates this fix). test_gig_log.py untouched: 169 passed before and after.
PLN (Algolia) authored -
Ran the new generation lens proactively against Cosmic26_master, before that release goes anywhere: 12 OK, zero stale, zero strays. The exact opposite of OPAL, for two reasons that are lessons rather than luck — only one render generation exists on disk, and every permalink is gig-scoped via --permalink-suffix, so no CosmicFest upload could collide with the OPAL cut of the same score. Four of these titles exist at both gigs. That answers an open board decision with evidence instead of taste. It asked whether to keep OPAL unsuffixed for consistency; the suffixed release has 0 permalink pathologies and the unsuffixed one has 14. Consistency with a broken scheme is not a virtue, so new OPAL uploads get suffixed. The 6 correct unsuffixed tracks stay — they are right, and renaming spends a permalink for nothing. Found in passing: CosmicFest is missing exactly 2 tracks, and they are almost certainly the two already-known vanishers that logged '✓ live at', passed an API check, then 404'd. A plain idempotent re-run converges. Which is the standing lesson landing on its feet: verify the shelf AFTER a run, not only during it — 'N uploaded, 0 failed' was never evidence that N tracks exist, and it took a lens three weeks later to notice two were gone.
PLN (Algolia) authored -
check-mix reports OK, all 12 orbits reach master. Third recurrence of the saved--inf bug, closed by PLN's hands. Two things fell out of verifying it. check-mix resolves the saved session with Ardour CLOSED — it reads the file, not the process, so the fader check can run in a pre-gig script before anything is launched. And the diff is bigger than the fix: orbits 02-06 were in a coherent -8 to -10 dB trim set and are now all near unity, so nine faders moved where four were broken. Master is still -0.2 so nothing clips, but the balance between orbits shifted ~9 dB on five of them. Recorded as a QUESTION, not a finding. The LCXL3's faders are absolute-position controls and touching one snaps the DAW value to the pot — which also means the board's 'v3 retires pot-pickup' claim covers the endless ENCODERS only; the eight faders are still absolute and that whole class survives the upgrade. But PLN may simply have dragged them on purpose, and a cause nobody asked about is not a cause.
PLN (Algolia) authored -
Found while shutting down: unplugging the LCXL took midiviz's ALSA source with it, the process exited cleanly, and Restart=on-failure did not fire. The permanent lens is only permanent while the board stays plugged -- which is the same resolve-once stale-binding shape as the zombie driver this session was opened to fix. lcxl-leds.py already has the fix pattern (re-resolve per send, cache invalidated on failure).
PLN (Algolia) authored -
The rewire (tray Launch Gig -> POST /api/rig, arm-then-confirm) is written and loads clean, but testing it end-to-end killed Ardour: launchers.launch('ardour') reported 'launched' while Ardour was already running, a second instance collided with the first over the session lock, and both exited -- taking all 12 orbit links with them. A false negative in is_running() wired to a tray button is worse than the mislabeled button it replaces, so it does not ship until launch() fails closed. Parked as a patch beside the note rather than committed (unvalidated) or left loose in the worktree (this checkout is shared and another session commits into it by pathspec). Carries the rest of the session's open ends too: the perf.sh redeploy that is committed but not live, gig-up's four pre-existing NO-GO blockers, perf-tray's 2.6GB RSS, and why gig-down is blocked on the Freebox being down.PLN (Algolia) authored -
Captain's log for the gear-failure pass: one zombie lcxl3-driver holding a stale binding to an unplugged mk3 explained three of PLN's five symptoms, the fourth was a mislabeled tray button, and the fifth was tooling that had been running invisibly all along. Written for mining later — the sharp bit is that name-based identity is the actual bug, and it caught the investigation as well as the rig: a subagent grepped aconnect for LCXL3, hit the driver's own phantom virtual port, and reported the hardware was back.
PLN (Algolia) authored -
TASKS_DUMP has accreted amendments since 2026-08-16 and the oldest sections are now the least true — but a reader arrives at the top, where A3 still says /take-five-drops uploaded '24s short', that every other track 'matched to the second', that 8 of 15 are up, and that no artwork exists. All four are wrong, and the first two are the exact reasoning that let six stale uploads pass a duration check in August. So: a READ THIS FIRST banner naming what is superseded and sending the reader to the resume points at the end, plus an inline stop sign at A3's 'TWO DEFECTS' where someone reading that epic top-down actually hits it. The wrong diagnosis is kept rather than deleted, because the reasoning being auditable is the point — it just no longer gets to be the first thing anyone reads. A board whose front page is a year of stale confidence is worse than no board. Nobody rewrites 1700 lines; they act on line 90.
PLN (Algolia) authored -
lcxl3-driver was policy "login", so rig_units.ensure() started it whenever it was not already active -- and ensure() runs inside gig-up.sh --converge, which is what the Bridge's RIG UP calls. With the CLASSIC LCXL plugged that is actively destructive: the v3 driver has Conflicts=lcxl-leds-watch, so a converge would stop the painter that was correctly lighting the classic board, then fail to bind (there is no LCXL3 port) and crash-loop at RestartSec=5. That is exactly what happened between 11:26 and 14:36 on 2026-09-05 -- 86 restarts, the board dark the whole time -- and today's repair would have been undone by the next press of GIG UP. lcxl-leds-watch was already "manual" for the mirror-image reason, stated in the comment right below this one. The rule generalises: neither painter may be auto-started blind, because "which painter" is a question only the plugged hardware can answer. gig-up.sh's leds() is the generation-aware chooser -- it greps aconnect for LCXL3 and restarts the right unit -- and it now owns the decision outright. Verified: `rig_units.py --ensure` (dry) reports "nothing to do" with the driver inactive and the v2 painter running, instead of queueing a start that would have gone dark. Trade-off worth knowing: on a mk3 rig the driver no longer comes up at login, so faders reach Ardour only after gig-up runs. That is the price of not guessing the generation, and gig-up is the documented bring-up.
PLN (Algolia) authored -
Structured archive entry for the session, written as standalone learning rather than a board line. Fourteen findings, of which the four that will outlive the task: SoundCloud accumulates generations and never replaces (so a stale upload is permanent until deleted); the wrong cut wore the cover art, which is why PLN's ear beat every tool to it; the 'matches to the second' tolerance was the bug, because full_duration is millisecond-exact and a loose window made two different cuts identical; and roll call cannot find a stray, because a check that only looks where it expects a thing never discovers it elsewhere. Written for a reader months out with no memory of any of it.
PLN (Algolia) authored -
Captain's log 040 and the pre-compact resume state. The log's finding, in one line: OPAL-26 sounded broken on SoundCloud while every rendered file was correct to the millisecond, because SoundCloud will not replace audio behind a permalink and our uploader is idempotent — so every re-upload under a fresh slug ADDED a copy. The whole Aug-12 first cut was still up beside the six correct v4 ones. And the tell PLN was actually reacting to: eight of the fourteen stale uploads wear cover art and not one of the correct six does. The superseded audio looked finished. His ear was reading the artwork. Two lens lessons in it, both recurring. 'Matches to the second' was a tolerance, not a verdict — SoundCloud reproduces full_duration to the MILLISECOND, so at the ±1.5s a transcode seems to deserve, the Aug-12 and v4 cuts of /wap are the same track, which is how six stale uploads passed a duration check in August. And roll call cannot find a stray, because a check that only looks where it expects a thing can never find that thing somewhere else. Plus the git one, which cost four commits: this is a shared checkout, another session moved HEAD between two of my commits, and 'git push origin master' from a non-master HEAD is a silent no-op that exits 0. The PUSHED in my own log was something I wrote myself. The board now carries R1-R5 written for a reader with zero context: exact commands with paths, the artifacts already on disk so nothing is regenerated, the three decisions that are PLN's and not a machine's, and the shared-checkout hazard spelled out before anyone commits into it again.
PLN (Algolia) authored -
183 Pulsar SIGABRTs between 2026-08-19 and 2026-09-05, 80 of them inside one 70-minute window, all from this one block. perf.sh launched Pulsar in "the script's directory", $(dirname "${BASH_SOURCE[0]}") -- but perf.sh is deployed root-owned as /usr/local/sbin/perf-audio (perf-audio.sudoers, for scoped NOPASSWD), so BASH_SOURCE resolved to /usr/local/sbin and Pulsar got /usr/local/sbin as its project directory. Coredump argv: /opt/Pulsar/pulsar --executed-from=/home/pln --no-sandbox /usr/local/sbin The Bridge's perf watcher (bridge.py:41 -> perf.py:run_watcher -> reconcile -> set_mode) shells out here every ~30s, and every mode flag routes through set_priorities, so the retry was unbounded: crash -> pgrep finds nothing -> next tick tries again. No backoff, no lock, no exit-status check. And the "✓ Pulsar launched" line proved nothing: the verification was a 0.5s pgrep presence poll that caught the doomed Electron process alive during startup and returned before it aborted. Fixed by removing the launch, not by resolving the directory better. Setting priorities is this script's job; starting applications is not. Ardour, two branches up, has always been prioritize-if-present with no launch branch -- Pulsar is now symmetric with it. Both the tray and the Bridge have real Pulsar launchers that pass the right project directory and surface failures. #116 also wants the hot path unable to start things by accident, and a root script spawning a GUI editor through `sudo -u $USER env ...` every 30s is the opposite of that. NOT LIVE YET: /usr/local/sbin/perf-audio is a root-owned copy and still carries the bug. It needs sudo install -m755 -o root -g root perf.sh /usr/local/sbin/perf-audio which is outside the sudoers whitelist, so PLN has to run it. Dormant until then -- the block only fires when no Pulsar is running.PLN (Algolia) authored -
The argv-only bug is fixed by extracting the 3-rung ladder into tools/ardour_session.py, imported by both gig-log and check-mix. gig-log's 'keep check-mix in sync' comment is gone because there is nothing left to keep in sync — one parser per concept satisfied by construction rather than by somebody remembering. 169 gig-log tests pass unchanged. Then it ran for real and reported Tidal 07, 08, 10 and 12 at -inf dB in the saved session. Third recurrence of that bug (04-08+12, then 02+10, now these four). PLN's parked two-minute check went from unrunnable straight to failing, which is the whole argument for wiring a check into a caller: it gets run under conditions the unit test never sees. Also logged: two pre-existing test failures the suite run made visible (test_orphan_orbits 16 rows vs 13 expected; test_lcxl3_display collects zero tests, and a test file that cannot fail is not a passing test), and the uncommitted duplicates left sitting on claude/rig-streamline so whoever rebases it does not double-apply them. the fader was down in the file, not in the room. nobody could hear the difference because a closed fader and a silent instrument look identical on a post-fader meter.PLN (Algolia) authored -
PLN, after the first look at it: "the midi mon is beautiful! so lets streamline, now it should always oppen, with always on top, all desktops displayed when i switch from desk 1 to 2 3 4" -- and then "would help to see the chan num: 33 or 53 etc so as i press i can wire". Always-on-top needed no change: the WindowStaysOnTopHint at :293 is unconditional and KWin honours it. The other three did. IDENTITY. The Wayland app_id comes from desktopFileName, not applicationName, so KWin saw resourceClass "python3" -- verified by querying workspace.windowList(), not assumed. Two consequences: a window rule keyed on the app id would have matched EVERY python GUI on the box, and launchers.focus_window("midimon") could never match, so clicking the Bridge/tray button on an already-running lens silently did nothing. Set desktopFileName + ship a .desktop (which also silences the portal's "App info not found" warning), and point the launcher's focus at "midiviz". ALL DESKTOPS is a compositor property, not a Qt flag, so it lives in a KWin rule (~/.config/kwinrulesrc, wmclass=midiviz, desktops= empty with desktopsrule=2/Force). Declarative on purpose: it applies at map time, every time, with no post-launch retry racing the window's existence. Verified on the live window rather than inferred from the key names -- onAllDesktops=true keepAbove=true. CC NUMBERS, two layers. Per-cell dim decimals are the map you consult while wiring; they render in their own pass because f_micro is already current there, and a per-cell setFont would add 96 font switches a frame to a path this file keeps to a counted number of drawText calls. They are skipped when a cell is too narrow to hold them beside the value. And the header now prints the last-touched control as ^53=127 -- decimal, with the corpus's caret, so it is the literal token to type into a pattern instead of a hex value to convert mid-set. Autostart as a systemd --user unit bound to graphical-session.target, not default.target: it needs WAYLAND_DISPLAY/XDG_RUNTIME_DIR imported into the user manager first. Registered "login" in rig_units.py. Paint path exercised by the offscreen selftest: 116 paints, PASS.PLN (Algolia) authored -
check-mix.py resolved the live session from argv only, so any Ardour launched from the GUI's recent-sessions list (bare argv) went unresolved and check-mix silently fell back to a guessed path. gig-log.py's FilesLens already solved this with a 3-rung evidence ladder (argv > cwd > open fds, commit abad7515) and had a sync comment flagging check-mix as needing it. Extracted the ladder into tools/ardour_session.py — one resolver instead of two, per feedback_one_parser_per_concept — and made both gig-log.py and check-mix.py import it. gig-log's FilesLens keeps its instance-level caching/logging but delegates all evidence-gathering to the shared module; the now-unnecessary "keep in sync" comment is gone. Verified: gig-log's 169 pytest tests still pass unchanged (they exercise all 3 rungs against synthetic /proc fixtures). Live: Ardour was running ("Tidal Live" session, launched with argv), so check-mix.py resolved it for real and reported per-orbit gains — surfacing the same recurring bug this fix was meant to catch: Tidal 07/08/10/12 sit at -inf dB in the currently SAVED session. Rungs 2 (cwd) and 3 (open-fds) were proven against synthetic fixtures matching the live-rig gap, since the running process's argv already carried the session path.
PLN (Algolia) authored -
master sc delete --manifest exists: dry-run by default, identity verified in BOTH directions (a permalink that now resolves to a different sc_id is a refusal, not a warning), public tracks refused, a reason required per entry because a deletion without provenance is not allowed, batch capped before any API call, and a receipt that keeps every field plus the on-disk paths of the archived artwork and the source FLAC. The old test-namespace gate was not widened — a second gate was added beside it, and a test proves the old one is unreachable from the new path. Dry run: 14 deletable, 0 refused. All private, all uploaded in the same minute on 2026-08-12, and every one still has its FLAC in Opal26_master/tracks/, so deleting them loses a URL and not a recording. --go is NOT approved yet. Two caveats recorded loudly rather than buried. The delete TRANSPORT is unproven: no DELETE has ever been issued from this codebase against api-v2, and a clean dry run is not evidence the deletes will land. And the generator's first version lined stale uploads against the newest cut BY TRACK NUMBER — dropping Desire renumbered REVOLUTION from 15 to 14, so the manifest confidently compared Desire against REVOLUTION's duration. An unnamed reference, again. Also logged: four of my own commits landed on a concurrent session's branch because the shared checkout moved under me, and 'git push origin master' from a non-master HEAD is a silent no-op that exits 0. Recovered via worktree cherry-pick without touching their branch or the live tree. The worktree rule in CLAUDE.md has no orchestrator exemption, which is what I had assumed. two sessions, one working tree, and a push that said yes to a question it had never actually been asked.PLN (Algolia) authored -
The regen moved timecodes by up to 21s (Piment +21.0, REVOLUTION +19.0, Gimme Acid +16.7). That file is not just website furniture — Slopmotion cuts visuals to it, so the release videos were pointed a third of a minute off the music on a third of the record. The more interesting finding is a parser conflict the regen walked into. build_gig_tracksjson.py drafts NEW gig pages and never computes the styleDistribution that LiveEvent.js renders; gig-metadata.py regenerates existing ones and does. They also disagree outright on REVOLUTION's style (jazz vs nujazz). Two parsers, one concept — so the board's line that build_gig_tracksjson 'is now the way to make any gig page' is too strong and is corrected here rather than left to bite the next regen. Left explicitly to PLN: whether the OPAL page documents the set as PERFORMED (15 tracks, Desire included) or as RELEASED (14). Desire has no v4 boundary because it was cut, so its v3 timecode rides through unverified. A page can honestly be either thing; only he knows which one he wants a visitor to get.
PLN (Algolia) authored -
The first version of this amendment said one OPAL track on SoundCloud was stale. Then I read the account instead of reasoning about it, and it is fourteen: the entire Aug-12 first cut is up, thirteen of it under junk permalinks (/opal26-wap, /12-mafia-12, /ceci-nest-pas-une-bombe) that roll call could never have found, because roll call only looks where it expects a track to be. Same error the board made before me, one size smaller. The finding that explains PLN's ear exactly: eight of the fourteen stale uploads carry cover art and not one of the six correct v4 tracks does. The wrong cut is the one that looks finished. Also corrected: 'every other track matches to the second' was never a verdict, it was a tolerance. SoundCloud reproduces full_duration to the MILLISECOND, so at the ±1.5s a transcode seems to deserve, the Aug-12 and v4 cuts of /wap (361.0 vs 361.6) and /perfect (315.0 vs 314.6) are the same track. That is how six stale uploads passed a duration check in August. And two coincidences caught before they could become deletions: /saria-demo (2025) and /au-revoir-lord-toyota (2020) match OPAL to 50ms by chance on a 132-track account. Duration is a strong discriminator and still not provenance. the ones with the cover art were the wrong ones. six months of a set played once, and the tell was never in the sound -- it was in which of the twenty ghosts looked ready.PLN (Algolia) authored -
CLAUDE.md pointed at ../../Web/www/next/content/lives/, which has not existed since www hoisted the app out of next/ in 6e51fff. The correct path is ../../Web/www/content/lives/{year}/{slug}.md. This is a worse bug than a typo in docs. The instruction it sits inside is 'Mastering != metadata. Never invent gig metadata... Verify there and cite the path — if a fact isn't in the canonical source, ask.' A reader who follows the documented path gets a false 'not found' from the one source they were told to trust, and the honest next step from there — asking, or worse, filling the gap from an intermediate script — is exactly the failure the rule was written to prevent. A stale read is how a Write becomes a delete. The tide-table scripts already had it right (build_catalog_view.py's LIVES carries a comment naming the hoist commit); only the instruction was behind.PLN (Algolia) authored -
- process-palette.json wires the editor's command palette to the rig's own entrypoints (Init MIDI Channels -> init_midi.sh, and the rest). The whole 'one entrypoint, fewer files' goal is only real if the editor knows where the entrypoints are, so this is rig config, not editor preference. - sketch.properties points the Processing sketch at its main .pde. - Logo.xcf is the editable source of the ParVagues mark, from 2025-05. The armada design system is documented down to its type stack and its reserved brand magenta, and the one file you cannot regenerate from any of that is the layered original. 1.2 MB to never have to redraw it. Deliberately still untracked: TODO.md. It is a paused 2026-06-22 perf/thermal board, and TASKS_DUMP.md has been the board since 2026-08-16. Committing it would put a second answer to 'what is next' in the tree, which is the shape that keeps biting us. PLN's call: fold its two live items into TASKS_DUMP and delete it, or keep it as a scratch file outside git.
PLN (Algolia) authored -
`sc_generation_lens` already knows which OPAL-26 uploads are the Aug-12 first cut; nothing turned that verdict into something the new `sc delete --manifest` could execute. This does, and it is the last checkpoint before a destructive call, so it refuses rather than warns. In: verdicts starting STALE/ORPHAN (right permalink, wrong cut) plus every stray (an older cut under another slug). Out, and asserted out: verdict OK (that IS the ear-verified cut), NOT UPLOADED (nothing to delete), anything without an sc_id, and everything in `coincidences_discarded` — the lens finds strays by duration and on a 132-track account two unrelated tracks will share a length to 50 ms, which is how /au-revoir-lord-toyota (2020, and PUBLIC) and /saria-demo (2025) both matched an OPAL render. The date floor is re-applied here instead of trusting the file's own bucket names: two chances to catch a mistake whose cost is permanent. Every entry carries a reason citing the evidence, the artwork the pull saved, and the local render the upload came from — all fourteen resolve to a FLAC in Opal26_master/tracks/, so deleting these loses a URL and not a recording. One bug found while writing it, and it is the reason the reason strings matter: the first version lined a stale upload up against the newest cut BY TRACK NUMBER, and the Aug-12 cut had fifteen tracks where v4 has fourteen. Dropping Desire renumbered REVOLUTION from 15 to 14, so the manifest claimed "#14 Desire, 388.1s vs v4 205.6s" — 205.6s is REVOLUTION's length. A measurement is a comparison; an unnamed reference cites the wrong track. The lookup is keyed on the title slug now, and a track absent from the newest cut says so instead of borrowing a neighbour's duration. Dry-run against the live account: 14/14 resolved, both directions, all private, nothing refused. Nothing deleted — that is PLN's call.
PLN (Algolia) authored -
The regen moved timecodes by up to 21s (Piment +21.0, REVOLUTION +19.0, Gimme Acid +16.7). That file is not just website furniture — Slopmotion cuts visuals to it, so the release videos were pointed a third of a minute off the music on a third of the record. The more interesting finding is a parser conflict the regen walked into. build_gig_tracksjson.py drafts NEW gig pages and never computes the styleDistribution that LiveEvent.js renders; gig-metadata.py regenerates existing ones and does. They also disagree outright on REVOLUTION's style (jazz vs nujazz). Two parsers, one concept — so the board's line that build_gig_tracksjson 'is now the way to make any gig page' is too strong and is corrected here rather than left to bite the next regen. Left explicitly to PLN: whether the OPAL page documents the set as PERFORMED (15 tracks, Desire included) or as RELEASED (14). Desire has no v4 boundary because it was cut, so its v3 timecode rides through unverified. A page can honestly be either thing; only he knows which one he wants a visitor to get.
PLN (Algolia) authored -
SuperDirt's boot runs MIDIIn.connectAll (start_and_midi.scd:19), so SC subscribes to every hardware MIDI input it can see -- the LCXL included. The surface then fed SC twice: once raw, once through Midi Through. And MIDIFunc.cc takes `src` and never checks it (start_and_midi.scd:51), so every CC fired the handler twice and every button noteOn double-fired into ~lcxlChordCheck. A double noteOn on a toggle is a no-op: that is how a mute button silently stops working. lcxl3-driver's prune() handled this for the v3 board, but only while it runs -- and it was stopped today (stale binding to an unplugged mk3, Conflicts=-killing the mk1 painter). A hand-run `aconnect -d` fixed it live and then SC restarted and re-armed it, which is the whole argument for putting it in the reconcile loop that already owns the wiring. Rule 3 matches the RAW board on type=kernel, never on name alone: lcxl3-driver publishes a virtual port called 'ParVagues LCXL3' that matches the same name candidates, and cutting the driver's translated feed would kill the path we actually want. Only surface->SuperCollider links are cut; -> Midi Through and the aseqdump taps that gig-log and the Pulsar HUD read all survive, verified live. Tested by arming the bug (aconnect 20:0 130:3, 20:1 130:4) and watching the loop cut both and settle: lcxl-path.py went from "LCXL -> SC direct" back to "LCXL -> Midi Through -> SC", logged once, no per-tick spam, and gig-log kept counting touched controls. Also logs PLN's ear-feedback on the classic-LCXL play-test (rose_rouge works great; midiviz landed) to the archivist's notes.
PLN (Algolia) authored -
The first version of this amendment said one OPAL track on SoundCloud was stale. Then I read the account instead of reasoning about it, and it is fourteen: the entire Aug-12 first cut is up, thirteen of it under junk permalinks (/opal26-wap, /12-mafia-12, /ceci-nest-pas-une-bombe) that roll call could never have found, because roll call only looks where it expects a track to be. Same error the board made before me, one size smaller. The finding that explains PLN's ear exactly: eight of the fourteen stale uploads carry cover art and not one of the six correct v4 tracks does. The wrong cut is the one that looks finished. Also corrected: 'every other track matches to the second' was never a verdict, it was a tolerance. SoundCloud reproduces full_duration to the MILLISECOND, so at the ±1.5s a transcode seems to deserve, the Aug-12 and v4 cuts of /wap (361.0 vs 361.6) and /perfect (315.0 vs 314.6) are the same track. That is how six stale uploads passed a duration check in August. And two coincidences caught before they could become deletions: /saria-demo (2025) and /au-revoir-lord-toyota (2020) match OPAL to 50ms by chance on a 132-track account. Duration is a strong discriminator and still not provenance. the ones with the cover art were the wrong ones. six months of a set played once, and the tell was never in the sound -- it was in which of the twenty ghosts looked ready.PLN (Algolia) authored -
CLAUDE.md pointed at ../../Web/www/next/content/lives/, which has not existed since www hoisted the app out of next/ in 6e51fff. The correct path is ../../Web/www/content/lives/{year}/{slug}.md. This is a worse bug than a typo in docs. The instruction it sits inside is 'Mastering != metadata. Never invent gig metadata... Verify there and cite the path — if a fact isn't in the canonical source, ask.' A reader who follows the documented path gets a false 'not found' from the one source they were told to trust, and the honest next step from there — asking, or worse, filling the gap from an intermediate script — is exactly the failure the rule was written to prevent. A stale read is how a Write becomes a delete. The tide-table scripts already had it right (build_catalog_view.py's LIVES carries a comment naming the hoist commit); only the instruction was behind.PLN (Algolia) authored -
- process-palette.json wires the editor's command palette to the rig's own entrypoints (Init MIDI Channels -> init_midi.sh, and the rest). The whole 'one entrypoint, fewer files' goal is only real if the editor knows where the entrypoints are, so this is rig config, not editor preference. - sketch.properties points the Processing sketch at its main .pde. - Logo.xcf is the editable source of the ParVagues mark, from 2025-05. The armada design system is documented down to its type stack and its reserved brand magenta, and the one file you cannot regenerate from any of that is the layered original. 1.2 MB to never have to redraw it. Deliberately still untracked: TODO.md. It is a paused 2026-06-22 perf/thermal board, and TASKS_DUMP.md has been the board since 2026-08-16. Committing it would put a second answer to 'what is next' in the tree, which is the shape that keeps biting us. PLN's call: fold its two live items into TASKS_DUMP and delete it, or keep it as a scratch file outside git.
PLN (Algolia) authored -
Written earlier today by the gear-repair session and left uncommitted, which for this particular file is the one failure mode that matters: performance_notes IS the archive, so an uncommitted entry is not a note, it is a memory. Two signals, both about the rig rather than the audio: - *"ok just played rose_rouge works great!"* — first confirmation that the CLASSIC LCXL (mk1, 1235:0061) is a fully viable performing surface once lcxl-leds-watch owns the paint. Worth recording precisely because the rig had drifted into treating the mk3 as the only real board; the classic is a fallback, not a downgrade. - *"and the midi mon is beautiful!"* — on first sight of tools/bridge/midiviz.py. A taste signal about the rig's own UI, and the follow-up ask (always-open, always-on-top, every desktop) says it plainly: midiviz is an instrument of the rig, not a debug tool. CLAUDE.md asks for every reaction captured durably because this corpus feeds sampling, effect choices and the auto-mastering heuristics. Durably means committed.
PLN (Algolia) authored -
Four kinds of orphan, all real source, none of it generated: - tools/sweep_state.py — 'the surface needs a sweep', as a fact the rig can act on. Companion to the LCXL3 driver thread. - armada/tide-table/kick_bass.py — the deterministic kick-vs-bass tiebreaker, the one split CLAP cannot do reliably because both live low. Exactly the 'validate by analysis, never infer role from the name' rule in code. - armada/tide-table/calibration.stem.json — joins calibration.json and calibration.whole-mix.json, which were already tracked. A calibration set missing one of its three members is a trap for whoever reads the other two. - install_superdirt.sclang — pins SuperDirt v1.7.2 and recompiles. The boot path is exactly where 'simple, non-brittle, one entrypoint' matters. - live/collab/benoit/tous_ensemble.tidal and live/midi/nova/nujazz/ nouvelle_couleur.tidal — two scores. nouvelle_couleur carries a '-- url:' header naming its backdrop source, per the scene standard. - visuals/{maze,nightly_repairs,there,there0}.js — four hydra sketches. Python entrypoints staged 100755 via --chmod, not left to the working tree. the drawer is empty now. everything in it was something, which is why it took three weeks to look.PLN (Algolia) authored -
Ten shell tools plus FORMAT.md and MIX.md, written 2026-07-29 and untracked since: stem-stats, energy-profile, onset-detect, silence-detect, transition-detect, mix-meta, mix-render, mix-normalize, mix-master, lufs-check, and lib/progress.sh. FORMAT.md defines a <project>.mix.json metadata contract whose tools 'read existing metadata and augment (don't overwrite unless told)'. Committed with the exec bit set IN THE INDEX, which is the part that bites: 3fdf5b97 had to restore it on 63 scripts, and MIX.md invokes every one of these as ./stem-stats. A file that is executable in the working tree and 100644 in git is executable exactly until someone else clones it. Note also that 'git commit -- path' silently drops a staged mode flip (the trap 1667bb02 documented), so this went in without a pathspec after proving nothing else was staged. mix/take94's 833 MB stayed out (a5a5b37). The scripts are the artifact; the take is the input.
⚠ ️ Flagged, not resolved: this overlaps ../tidal-ears' mastering CLI, which is the canonical recipe per MASTERING.md. Two toolboxes that both master a set is the shape feedback_one_parser_per_concept warns about. I am not merging them blind — committing first so the overlap is visible in one tree instead of half-invisible in an untracked directory. PLN's call which one is the spine. ten small knives in a drawer nobody opened, sharp the whole time.PLN (Algolia) authored -
Written 2026-08-14, uncommitted for three weeks: the -inf fader forensics on Tidal Live.ardour, the corpus triage that found '51 broken tracks are actually 22 — the harness was the biggest bug', and the 192 kHz master-format audit ('probe, never assume'). All three are marked shareable: true, which is the whole point — these logs are the source material for the posts and videos, and they were sitting one bad rm away from not existing. The rule in armada/tasks/README.md is 'write the entry when the task lands, while it's fresh'. It worked: the writing happened same-day. It was the committing that didn't, because git status had become unreadable (see a5a5b37). Documentation discipline is not one habit, it is two, and the second one is the one that survives a hard drive. Left for PLN, one line: all three carry 'log: 025' in their frontmatter, which 025-fifteen-intros-are-not-a-set.md already owns. They also use dated filenames like 2026-08-03-j5-audio-dropout.md rather than the README's NNN-slug.md. Renumbering is a call about his own archive's spine, not mine — and the dates are 2026-08-14, which lands them BEFORE 039, so the next free numbers would read out of order. the log was kept. the log was not carried. a ship's record that never leaves the desk is a memory, not a record.PLN (Algolia) authored -
Three weeks of untracked working tree, and the reason nothing was committed is that 'untracked' had stopped meaning anything: 957 MB of take audio and scene mp4s sat in the same status output as three finished captain's logs, so the whole list read as noise and got skipped as one. Sorted by what each thing actually is, against the precedent already in this file and in the index: mix/'s ten scripts are source and go in, mix/take94's 833 MB does not. The .js scene sketches are source, the 124 MB of rendered mp4 is not. calibration.json and calibration.whole-mix.json are tracked, so calibration.stem.json belongs with them; bounds_*.json is generated output whose builder is already tracked, so it does not. No new policy here — just applying the one written at the top of this file to a pile that had grown too mixed to read. the tide went out three weeks ago and left everything on the sand together: charts, driftwood, a gigabyte of water. sorting is not cleaning. sorting is deciding what was ever yours.PLN (Algolia) authored -
PLN opened the day hearing it: 'our private cuts of OPAL are badly cut begin/end unaligned'. The board had one theory (a 24s truncation) and it was wrong. This lens found the real shape: the ENTIRE Aug-12 first cut is still on SoundCloud — all 15 tracks — living beside the 6 correct v4 ones under a litter of ad-hoc permalinks (/opal26-wap, /12-mafia-12, /ceci-nest-pas-une- bombe), and eight of the stale ones are the ones wearing artwork. So the wrong cut is the one that looks finished. Of course he heard it. Duration is the discriminator, and it is far sharper than assumed: measured on this account, SoundCloud's full_duration reproduces the source to the millisecond (477.600 uploaded, 477.600 reported). Tolerance is 50ms, not the seconds you would allow for a transcode — a loose tolerance here buys no safety, it only invents ambiguity between cuts that differ plainly. Three things I got wrong first, each left in as a guard: - filenames are not authoritative. 'Perfect <3' uploads to /perfect-3, so a filename-derived slug reported two live tracks as NOT UPLOADED. Titles come from the plan now, which is what the uploader actually used. - streaming and club masters share every duration, so offering both as rival candidates marked all 14 AMBIGUOUS. Identical generations collapse. - a duration collision is not provenance. /au-revoir-lord-toyota (2020) and /saria-demo (2025) both matched OPAL to 50ms. Anything uploaded before the oldest render on disk cannot be a cut of it, and a coincidence must never reach a delete manifest — so the date floor is a hard gate and the discards are printed, not silently dropped. The second pass is the one that earned its keep. Roll call only looks where it expects a track to be, so it can never see a stale cut hiding under a different slug. Strays scans the whole account. Absence needs proof. fifteen ghosts of a set played once, each wearing the cover art of the real thing -- the lens does not listen, it only counts seconds, and seconds were enough.PLN (Algolia) authored
-