1. 25 Jul, 2026 15 commits
    • docs(gig): riser audit correction — found the 12th track, and it's a THIRD idiom · 5a010b11
      Second pass on the audit. "Am i Doing it Right?" was reported as file-not-located;
      it exists as live/techno/do_it_right.tidal. No slug guess matched the title — it
      was identified by `setcps (89/60/4)` = 89 BPM against the backlog's [89*] marker.
      Recording that as a method: when title-to-slug guessing fails on this repo, the
      tempo line is the reliable identifier, not the filename.
      
      It changes the finding in a way that strengthens the conclusion rather than
      weakening it. The track DOES have a riser, so the count is 3/12 not 2/12 — but it
      is a third, mutually incompatible idiom:
      
        d10 $ mask "<f!4 t f!3>" $ loopAt 4 $ "risers:8" # gain 1 # cut 10
      
      An 8-cycle period firing on cycle 5, but loopAt 4 stretches the sample over 4
      cycles while the mask only opens for 1 — so what plays is a slice of a slowed
      riser, not a complete build. Could be deliberate (at 89 BPM a full 4-cycle build
      is very long) or a leftover; flagged for PLN either way.
      
      So the real headline is not the absence of risers, it's the inconsistency of the
      ones that exist. Three tracks, three idioms, disagreeing on all three axes:
      - phase: fires-last-8 (Take Five) vs fires-first-1 (Vague de CRIME) vs
        fires-cycle-5-of-8 (Do It Right);
      - gain: 0.8 vs unset vs 1 — the riser will not read consistently to the room
        across three tempos;
      - wrapping: Take Five AND Do It Right both lack gF/gM, so they are the only
        streams in the set the global filters and mutes cannot touch. Reach for gF1
        during either build and everything ducks except the riser.
      
      That third point is the one that could actually bite live, and it now affects two
      tracks rather than one. It also makes the gRiser proposal stronger: harmonizing is
      no longer "add the missing 9", it's "reconcile 3 disagreeing implementations and
      then add the missing 9", which is exactly the kind of thing worth doing once in a
      helper instead of nine times by hand at J-10.
      PLN (Algolia) authored
    • docs(gig): riser audit of the OPAL 2026 set — only 2 of 12 tracks have one · 1ae6f369
      Task #33 asked "does every OPAL track have a riser, and are they harmonized?".
      Answering it needed the setlist (backlog.md § OPAL 2026, 12 tracks with BPMs)
      resolved to actual .tidal files, then every d10 / riser / sweep / impact hit read
      in context. Result is worse than expected and worth having in writing before
      rehearsal rather than discovering it on stage.
      
      Findings:
      - Only 2 of 12 tracks have a riser: Take Five Drops and Vague de CRIME.
      - 5 tracks have d10 completely free (Bombe, WAP, Mafia, Sunshine, Desire) — a
        riser drops straight in.
      - 4 tracks have d10 already occupied by non-riser material (Piment's "Menace",
        Perfect's euclidian arps, Gimme Acid's "GIMME THAT", Revolution's sweet
        trigger) — those need a different stream, not a d10 paste.
      - Track 3, "Am i Doing it Right?", has NO .tidal file matching any slug I tried.
        Per the repo convention it must exist under a name I didn't guess. Flagged as
        an open question — the set is incomplete until it's found.
      
      The two existing risers disagree with each other, which is the actual #33 problem:
      Take Five sleeps 24 cycles and builds over the final 8 (a build-up INTO a drop),
      while Vague de CRIME fires on cycle 1 of 8 at the sample's natural length (an
      accent OUT of a downbeat). Same folder, opposite musical function — exactly the
      muscle-memory hazard when you're moving track to track mid-set. Also caught: Take
      Five's d10 is the only stream in the whole set with no gF/gM wrapper, so global
      filters and mutes cannot touch it — reach for gF1 during that build and everything
      ducks except the riser.
      
      Proposed one house helper, `gRiser period build`, derived verbatim from PLN's own
      working Take Five block rather than invented. loopAt is the load-bearing trick: it
      makes the riser's length independent of the sample's natural length, so one helper
      lands correctly from 120 to 166 BPM.
      
      Validated the proposal two ways before recommending it, per build-the-katana-first:
      - compiles clean against the real tidal-1.9.5 (ghc -fno-code, no stream started);
      - the generated mask is BIT-IDENTICAL to the hand-written "<f!24 t!8>" — queryArc
        over cycles 0-32 gives exactly 24 False then 8 True for both, compared
        elementwise, mine == theirs -> True. It's a faithful refactor, not a lookalike.
      
      Deliberately NOT committed to BootTidal.hs and NOT applied to any track: what the
      grep cannot answer is whether stock gain 0.8 / room 0.4 sits right under a 166 BPM
      liquid dnb track. That's ears. Suggested trial order is Desire (the FINALE, where a
      missing build costs most), then Mafia and Sunshine (the set's peak).
      
      Ties to #13: with default-xfade dN now baked in (98d073f2), every track launch
      already blends over 4 cycles. A riser is the musical counterpart to that structural
      crossfade — the xfade stops the seam being abrupt, the riser tells the room a
      change is coming. Together they're what turn "ctrl+enter and pray" into a transition.
      PLN (Algolia) authored
    • fix(boot): bake the live-validated prelude into BootTidal.hs — disarm the gF mute-bomb permanently · 98d073f2
      The problem: `gF1/gF2/gF3` in BootTidal.hs sent a value into `djfbus`, which is
      NOT a registered SuperDirt effect. The LCXL faders REST AT 0, so every gF-wrapped
      stream shipped an extreme value into a missing bus the moment it was launched —
      sound muted mid-set with no way back. Every mature ParVagues track wraps its
      streams in gF1/gF2/gF3, so this was armed on essentially the whole set.
      
      It was fixed and live-confirmed on 2026-07-25 ("f123 djfs seem to work great
      overall") — but only as a shadow-eval from live/lib/prelude.tidal. That fix lived
      in the running ghci and nowhere else: one SuperCollider reboot before OPAL and the
      footgun rearmed itself silently. J-10 is the wrong time to depend on remembering
      to eval a file.
      
      Approach — three blocks promoted from prelude.tidal into the boot file:
      
      1. SAFE-AT-REST global filters. Swap the phantom djfbus for CORE params that are
         always registered, mapped so fader-rest(0) == BYPASS:
           gHPF = range 20 8000   (rest ⇒ hpf 20, effectively open)
           gLPF = range 20000 180 (rest ⇒ lpf 20k, effectively open)
         gF3 is now literally `id` — a spare fader that cannot touch sound at all.
         Untouched ⇒ no effect, ever. Fader up ⇒ the filter sweeps in. Cannot mute you.
      
      2. DEFAULT-XFADE dN launches. d1-d12 become `xfade N . (|< orbit N-1)`. The orbit
         composition is the load-bearing part: a naive `xfade N` DROPS the orbit that dN
         bakes in, so transitions landed on the wrong LCXL column and the wrong Ardour
         stem. Added an orbit-correct `jumpTo N` for hard cuts. d13-d16 deliberately
         stay plain `p` — orbit 13 is the tidal-ears capture vsink and must not blend.
      
      3. gPanic — the ^93 gate the start_and_midi.scd panic-chord (73+74+91+92) flips.
      
      Validation: type-checked in a throwaway ghci against the real tidal-1.9.5 install.
      All signatures resolve as intended — gF1/gF2 :: Pattern ValueMap -> Pattern ValueMap,
      gF3 :: a -> a (proving the safe identity), d1 keeps its original
      Pattern ValueMap -> IO () shape so no track file needs an edit, d13 stays
      ControlPattern -> IO (), jumpTo :: ID -> Pattern ValueMap -> IO ().
      
      prelude.tidal is demoted from "source of truth we forgot to promote" to an
      explicit RESCUE SHEET: it now documents the stale-boot symptom (push fader 49,
      sound mutes instead of sweeping) and stays evaluable as an in-session hot-patch.
      One source of truth, one less thing to remember mid-set.
      PLN (Algolia) authored
    • feat(gig): panic-chord + LED feedback, always-on lean recorder, freebox archive complete · 29ce1039
      Three independent pieces landing together from tonight's live-debug/build session:
      
      1. Panic chord (task #34): holding LCXL push-buttons 73+74+91+92 together now
         flips a persistent silence toggle. These are momentary Note buttons (not
         latching CC), so a simultaneous hold is a natural chord — detected with an
         edge-triggered flip-flop in start_and_midi.scd (the only layer that sees raw
         per-button press/release state cleanly). Flips are echoed to Tidal as a new
         virtual control "^93", and to the LCXL's own LEDs via the Note-On method
         already validated in this project (reference_lcxl_led_stall memory). Tidal
         side is a plain gPanic gate in live/lib/prelude.tidal — PLN wires it into
         whichever track he's actively composing/testing, not applied globally.
         Not yet live-verified (needs a fresh boot) — tracked as task #34.
      
      2. tools/gig_record.sh: a lean, reusable, always-on stereo-master capture,
         answering "I want to always record all play, compressed." Auto-resolves
         whatever the current default PipeWire sink is at start time (so it follows
         the rig from onboard SOF SoundWire onto the UMC202HD arriving tomorrow with
         zero config changes) and encodes to Opus at 160kbps in hourly segments
         (~72MB/hr — 188GB free buys thousands of hours). start/stop/status/slice
         subcommands; slice does a fast stream-copy excerpt for quick auditioning or
         feeding into Slopmotion once that's available. Smoke-tested end to end
         (start, 8s live capture, ffprobe-verified valid opus, stop) before wiring
         into anything. NOT yet wired into gig-up.sh's auto-launch — PLN wants
         silent-by-default on every boot; that wiring is the next step.
      
      3. Local disk freed from 28GB (98%) to 188GB (81%) free. Root-caused why the
         session had crept there: the 436GB Ardour "Tidal Multi" session (deprecated
         in favor of the lean "Tidal Live" the gig now boots) was still sitting
         locally despite the freebox mirror already holding it byte-identical.
         Verified via fbk --dry-run (0 audiofiles needing transfer; the 1530-file,
         449,260,749,448-byte audiofiles tree matched exactly on both sides) before
         asking PLN to bless the delete — freed via `rm -rf` of audiofiles/export/
         peaks, keeping the tiny .ardour session file + midifiles as structural
         reference. (Real gain was 160GB not the naive 436GB sum — this machine's
         root is Btrfs with zstd compression, so du-reported sizes don't equal freed
         space 1:1; 188GB real free per df is still the number that matters.)
      
      Also: visuals/slop/README.md — documented the staged first test clip
      (Opal2024's 12_Mauerpark.flac, loudest 60s window at 2:53-3:53, chosen because
      it's the same track lineage as ete_a_mauerpark.tidal currently being edited)
      while the actual Slopmotion repo remains unlocated (org has 0 repos; asked
      PLN to get the real URL from Kevin/Shipow).
      PLN (Algolia) authored
    • feat(live): safe-at-rest gF filters + default-xfade dN prelude, and Slop-visuals scaffold · c71ff2d5
      #21 gF footgun fixed and #13 xfade confirmed live this session:
      - live/lib/prelude.tidal: nucleus of the ParVagues lib (#23). Two eval-blocks PLN
        can shadow into a running session (no reboot):
        * gF1/gF2/gF3 rebuilt on CORE hpf/lpf mapped so fader-at-rest (CC=0) == BYPASS.
          The old  (# djfbus N …)  referenced an UNregistered SuperDirt effect and rested
          at an extreme → muted the orbit with no recovery. New form structurally cannot
          mute: untouched = transparent, push fader = sweep in. PLN confirmed 'work great'.
        * d1..d12 = xfade N . (|< orbit N-1): every track LAUNCH crossfades 4 cycles AND
          keeps its orbit (old bare  xfade N  lost the orbit → wrong LCXL column/stem).
          Plus orbit-correct  jumpTo i  for hard cuts. PLN confirmed 'no more abrupt cut boom!'.
      - visuals/slop/: scaffold for Kevin's Slopmotion — README (NOW=generate shareable
        MP4s, LATER=gig visual engine) + FEEDBACK.md log to hand back to him.
      
      Not yet baked into BootTidal.hs — kept as evaluable prelude until fully road-tested.
      PLN (Algolia) authored
    • feat(gig): auto-launch is now perf-aware + preloads the set's samples · c35c9020
      Two live-play problems, both auto-handled by gig-up.sh so nothing lives in
      PLN's head at gig time:
      
      1. MIDI-burst CRACKS — root cause was NOT samples or RT priority (rtprio 95 is
         fine): the CPU was capped at max_perf_pct=55 (the quiet/cool desk regime),
         so a control sweep's CPU burst starved the audio thread -> xrun. gig-up now
         ASSERTS Standard (uncapped, governor performance) on launch via
         perf.set_mode('standard') — which also writes the desired-state file so the
         Bridge watcher holds it. Revert to Cool in the tray when done; skip with
         GIG_PERF=off. Confirmed live: toggling to Standard killed the cracks.
      
      2. LAZY-LOAD cracks — gen_preload() runs setlist_samples.py --last N --emit-sc
         into preload.scd before boot; start_and_midi.scd loads it after
         SCLOrkSynths.load, warming just the recent set's samples (last 15 tracks =
         55 folders vs the whole library). setlist_samples.py also gained bare-name
         track resolution (find under live/). preload.scd + gig-sclang.log gitignored.
      PLN (Algolia) authored
    • feat(gig): setlist_samples.py — preload just the set's samples (kill lazy-load cracks) · a4a76e03
      Live play cracked from lazy sample loading (~dirt.doNotReadYet=true): each
      sample reads from disk on first play mid-set → crackle + xrun spikes
      (764→1085 xruns during a track). Loading the whole library = slow boot + RAM;
      lazy = cracks. This tool finds the middle path: parse a set's .tidal tracks
      (or --last N by mtime), extract every token from every quoted string, and keep
      only those that resolve to a real sample folder (Dirt-Samples, extra/,
      tidal-drum-machines) — the library itself is the filter, robust against
      ParVagues' bare-string/# dialect that defeats s"..."-based parsing.
      
      --emit-sc prints a ~dirt.loadSoundFiles preload snippet. Validated: last 15
      tracks → 55 folders (vs hundreds in the library); bombe_dj → 8. Foundation for
      #26 (wire into boot) + #24 (soundcheck). t/f excluded (Tidal booleans).
      PLN (Algolia) authored
    • fix(gig): boot the real start_and_midi.scd, not stale main_fairbanks — root of… · c63dd3d3
      fix(gig): boot the real start_and_midi.scd, not stale main_fairbanks — root of silent synths & orbit errors
      
      gig-boot.scd chained main_fairbanks.scd (scratch: 8 output channels, 8 orbits)
      BEFORE start_and_midi.scd. main_fairbanks booted the server first, so PLN's
      real boot found it already up and its options were ignored — losing the 28
      channels, 14 orbits (d1..d12 + ears test), SCLOrkSynths.load, the MI global
      effects, StageLimiter + s.volume=2. Net live: d9-d12 silent, bassWarsaw/
      FMRhodes1 never loaded, orbit 8/10/11 errors spamming the post window.
      
      Fix (#18 simplification): launcher runs PLN's canonical sclang start_and_midi.scd
      directly; gig-boot.scd retired; main_fairbanks.scd kept as reference only.
      Apply = FULL sclang/scsynth restart (running scsynth keeps 8-ch options).
      PLN (Algolia) authored
    • checkpoint(tracks+gig): save-state before ParVagues-lib rationalization · e359a343
      Snapshot of the live-set material and gig docs before the planned lib
      restructure (dedup + a proper loaded ParVagues prelude so gF/gM/djf live in
      one place, not re-defined in scratchpads). Also protects secrets: .env +
      build noise now gitignored. Includes FOH_MASTER.md (master-bus hardening
      recipe) and the ordered-202HD shopping update. Track edits: desire,
      vague_de_crime, + new network_zero_un / bombe_dj / ca_fait_rien /
      electric_hammer.
      PLN (Algolia) authored
    • chore(gig): point gig-up at the lean 'Tidal Live' session · 2bc6a8e2
      Save-As-empty produced Tidal Live (no audiofiles — the 419G stems were NOT
      copied). Retargeted the launcher from the fat archival 'Tidal Multi' to the
      lean go-forward session. Tidal Multi stays put for the freebox stem archive
      (#19), then retires.
      PLN (Algolia) authored
    • feat(gig): make gig-up idempotent — converge the rig, never spawn dupes · 0c65fda8
      Follow-up to the live launch test: re-running gig-up while the rig was
      already up would have opened a SECOND Ardour on the same session (an
      'already open / locked' dialog) and a second Pulsar. Only SuperDirt was
      guarded (skip if scsynth up).
      
      Added ardour_up() (pgrep -f 'ardour-[0-9]', since the real binary is
      ardour-9.2.0 behind the ardour9 wrapper script) and pulsar_up(), and gated
      both launches on them. Now gig-up is a converge-to-ready button: run it at
      any point and it starts only what's missing. Verified live with the full
      rig up — SuperDirt/Ardour/Pulsar all correctly reported 'already running,
      skipping', nothing duplicated. Serves #18 (dance with sound, not setup).
      PLN (Algolia) authored
    • fix(gig): gig-up.sh crashed on launch — open_term read an unused $1 under set -u · e3043558
      First real end-to-end run of the ordered launcher aborted immediately at
      "starting SuperDirt": open_term() carried a vestigial `local cmd="$1"`
      (the sclang command is hardcoded inside the function), but the function is
      called with no arguments, so `set -u` turned the unused positional into a
      fatal 'unbound variable' before sclang ever spawned.
      
      Dropped the dead parameter. Re-ran: SuperDirt boots headless, claims the
      LaunchControl XL (aconnect 16:0 -> SuperCollider 128:3) BEFORE Ardour opens,
      then Ardour 9.2.0 (Tidal Multi) + Pulsar come up — the whole rig from one
      command, no disconnect/reconnect MIDI dance. Task #9 validated live.
      PLN (Algolia) authored
    • docs(gig): SHOPPING_LIST.md — stage-signal + go-bag purchases for OPAL 2026 · f1628d8b
      Per-item 2-line rationale + 3 example search queries, constrained to few-days Amazon
      or a Pigalle scavenge (deadline ~Aug 1). Priority = a USB interface with balanced line
      outs (the real fix for "you're too quiet": today FOH is fed from the laptop headphone
      jack). Then powered USB hub (LCXL LED stability), a DI-box fallback, a cable/adapter
      kit, and an optional passive monitor controller for the fine volume control the KRK
      wheel lacks. A sub is noted as post-gig — metering solves the bass blind spot for now.
      PLN (Algolia) authored
    • feat(gig): 1-click ordered launcher — SuperDirt-first, then Ardour, killing the MIDI dance · ba8e84a7
      Start-of-gig pain: whichever of SuperDirt or Ardour opens the LaunchControl XL first
      wins it, and when Ardour wins, sclang can't get the controller → the manual
      "disconnect MIDI / reconnect MIDI" dance mid-setup. The fix is ordering + a real
      readiness gate.
      
      gig-up.sh (repo root): brings SuperDirt up FIRST and WAITS until it has actually
      claimed the controller, then launches Ardour + Pulsar. Readiness = three unambiguous
      signals, all of which appear during main_fairbanks.scd's boot: scsynth alive +
      UDP :57120 listening (SuperDirt.start) + a "SuperCollider" ALSA-seq client present
      (MIDIClient.init has run). 120s timeout (sample loads can be slow) → proceeds with a
      clear warning rather than hanging. Best-effort throughout: terminal auto-detected
      (konsole/kitty/…, background-log fallback), Ardour/Pulsar binaries probed by name,
      skips SuperDirt if scsynth is already up.
      
      gig-boot.scd: one eval that runs the real boot (main_fairbanks.scd) + the LaunchControl
      CC->OSC bridge (start_and_midi.scd) so the rig comes up fully armed in a single file;
      loaded by relative path to stay portable.
      
      Validated offline: bash -n clean; the three probes correctly read 'down' with SuperDirt
      dead; ardour9 + pulsar resolve; the Tidal Multi session path exists. End-to-end (the
      live MIDI gate) to be confirmed on the next real boot.
      PLN (Algolia) authored
    • fix(audio): shield SuperDirt from the display stack — headless sclang (QT_QPA_PLATFORM=offscreen) · d8346672
      Root-caused the 2026-07-25 overnight-sleep crash where the music (and Ardour)
      died on resume. It was NOT an audio-hardware failure — it was the *display stack*
      taking down the sound.
      
      Evidence (all at resume, ~11:17):
        11:17:31  kwin_wayland (pid 2603) SIGABRT — the Wayland compositor aborted on
                  resume-from-suspend (deep internal assertion; core shows abort→libc).
                  Plasma respawned the whole session (new kwin/plasmashell/Xwayland,
                  confirmed by their ~1h40m uptime vs the machine's 1d+).
        11:17:35  ardour9: "Fatal IO error 104 (Connection reset by peer) on X server
                  :1" — Ardour, an X11 client on Xwayland, died with the session.
        11:17:35→44  scsynth SIGABRT via std::terminate — ~4s after kwin, in the
                  cascade: sclang is a Qt6/Wayland client, so it lost its display
                  connection when kwin died and crashed, and scsynth (which it hosts)
                  aborted on the broken IPC.
      
      Why short naps survive but an overnight one didn't: a quick suspend/resume just
      pauses the Wayland connection; the long/overnight cycle triggered a GPU/DRM reset
      that made kwin actually crash — and a crashed compositor severs every client at
      once.
      
      Fix: sclang doesn't need a display. QT_QPA_PLATFORM=offscreen runs its Qt without
      connecting to any display server, so a compositor crash/restart can no longer reach
      it — the SuperDirt brain becomes immune to the entire Wayland/X lifecycle. SuperDirt
      uses no GUI; the interpreter and post window still work in the terminal. Validated:
      `QT_QPA_PLATFORM=offscreen sclang` compiles the class library and reaches the
      SuperCollider 3.14.1 banner with zero wayland/qpa errors.
      
      collide.sh is the only launcher (perf.sh only reprioritizes a *running* sclang, it
      doesn't spawn one), so this single line closes the gap.
      
      Not addressed here (harder, separate levers): kwin's resume-crash itself is a
      Plasma/GPU-driver issue — the standing mitigation is running dGPU-off
      (./gpu-mode.sh integrated), which removes the Xwayland-on-dGPU DRM reset that's the
      likely abort trigger and would also spare Ardour's :1 connection.
      PLN (Algolia) authored
  2. 24 Jul, 2026 3 commits
    • feat(perf): gpu-mode switch — the real dGPU-off lever + cockpit surfacing · 44263086
      The last unclaimed thermal lever. Our 2026-07-24 finding: on this Plasma-Wayland
      box Xwayland pins the RTX 2060 'active' regardless of runtime-PM (control=auto,
      --rtd3), so the dGPU never actually suspends live and keeps drawing ~4-5 W + idle
      heat. The ONLY real off is EnvyControl's 'integrated' mode (blacklist nvidia) — but
      that's a persistent config change that needs a re-login, categorically unlike the
      instant perf.sh modes (governor/EPP/RAPL). So it gets its own tool, not a perf-mode.
      
      gpu-mode.sh (new, at repo root beside perf.sh)
      ----------------------------------------------
      A guarded EnvyControl front-end: `query` (default — manager mode + live dGPU
      runtime state, with the right advice per mode), `integrated` (dGPU off; warns you
      lose CUDA/NVENC for demucs/audio-ML until you switch back), `hybrid` (restore with
      aggressive --rtd3 3 = D3cold-when-idle). Every switch confirms (skip with -y), runs
      `sudo envycontrol ...` (prompts for your password), then prints the exact re-login
      step (reboot safest; verify with `./gpu-mode.sh query` + `nvidia-smi`).
      
      Deliberately NOT added to the passwordless perf-audio sudoers whitelist: this is a
      rare, considered, re-login-required switch — the web Bridge and tray must never be
      able to power-cycle the GPU config unattended. Runs by hand only.
      
      Cockpit surfacing (Bridge + tray)
      ---------------------------------
      - perf.py: gpu_manager_mode() reads `envycontrol --query`, cached 300s (the mode is
        static within a login session; a re-login restarts this --user service and
        refreshes the cache), defensive → None if envycontrol absent. Added to snapshot()
        as `gpu_manager`.
      - Bridge UI: the `gpu` chip folds it in — shows `off↻` when integrated-but-pending-
        re-login (dot green: this is the quiet goal), and in hybrid its title now names the
        manager mode and points at `./gpu-mode.sh integrated` for a real off.
      - Tray tooltip mirrors it (`gpu awake·hybrid`, or `off↻` when armed).
      
      Validation: 24 perf tests pass (2 new — parse+cache, and absent→None; snapshot-shape
      now asserts dgpu/gpu_manager/thermald). Live: `./gpu-mode.sh query` reads
      hybrid + active(0000:01:00.0) and fires the Xwayland warning; Bridge /api/perf serves
      gpu_manager=hybrid; both services restarted clean.
      PLN (Algolia) authored
    • docs(perf): 2026-07-24 live-playback highlighter marker profile · f7898292
      The playing counterpart to the July-18 idle capture: mid-set highlight-stats
      shows 580 markers live vs 16 active, accumulating ~40/min (matches July's
      34/min idle) — the position-marker pool still never evicts, so the 07-19
      "marker leak fixed" is incomplete and the idle 82% renderer burn (the
      break-time heat) shares this exact root. UI-smoothness only; audio untouched.
      PLN (Algolia) authored
    • feat(perf): smart-silent mode + Bridge/tray thermal cockpit · 2960c9fb
      Problem: the laptop ran hot even idle, and the bafd3133 RAPL watt caps were
      committed but NEVER deployed — the live /usr/local/sbin/perf-audio predated
      them, so every --cool set clock-% while RAPL sat at the BIOS 135W (measured
      90C under an all-core encode). Watts are the thermal lever, not percent.
      
      - perf.sh: new --silent ("smart-silent") — powersave + EPP power + 55% clock
        ceiling + a hard 12W/25W RAPL cap + allow_dgpu_suspend (nudge the discrete
        GPU toward D3cold) + audio still RT. Quieter than --cool without disabling
        turbo (low PL2 keeps SuperDirt DSP transients from xrunning). Tunable via
        SILENT_PL1_W / SILENT_MAX_PCT.
      - perf-audio.sudoers: whitelist --silent (flag-scoped, no wildcard).
      - tools/bridge/perf.py: MODES gains silent (tray + web inherit it);
        detect_mode disambiguates silent vs cool by EPP; snapshot() gains cores_pct
        (per-cpu util deltas), dgpu (runtime_status), thermald, watts (live RAPL
        caps), power_w (actual draw — null until energy_uj is unlocked, then lights).
      - ui/index.html: the bar is a cockpit now — 16 "dancing cores" (height=util,
        colour=temp, hover=cpu/util/temp), folded chips (watt cap / dGPU asleep-awake
        / thermald), a distinct teal+snowflake tint for Silent, and a 60s temp+power
        sparkline.
      - perf-tray.py: the icon's decorative sine is now a REAL temp-history
        sparkline; tooltip + menu fold in cap/gpu/thermald.
      
      Verified: POST /api/perf {silent} -> live RAPL 12/25W; {cool} -> 28/50W; the
      135W furnace is leashed. perf tests 7-pass.
      PLN (Algolia) authored
  3. 23 Jul, 2026 2 commits
    • fix(bridge): detect resume via CLOCK_BOOTTIME, not MONOTONIC · 3a973eec
      Field-caught by the watcher itself. The laptop suspended ~10:11 and resumed
      11:21:46; resume reset the pstate cap (a brief 4.4 GHz clock spike in the monitor
      log), and the watcher DID restore cool — but at 11:22:04 via the ~30s periodic
      drift check ("[perf-watch] drift: reasserted cool"), not the fast 2s resume path.
      
      Cause: the resume detector used time.monotonic(), and CLOCK_MONOTONIC does not
      advance while the system is suspended — so on thaw the thread saw only its normal
      ~5s sleep, gap << threshold, resume missed. The periodic safety net (belt-and-
      suspenders) is what actually caught it.
      
      Fix: measure the gap with CLOCK_BOOTTIME, which counts suspended time, so a resume
      now shows as a large gap and triggers the immediate force-reassert (2s settle).
      Falls back to monotonic if CLOCK_BOOTTIME is unavailable. Verified present on this
      box (uptime read OK); watcher restarts clean with on_ac=True desired=cool.
      PLN (Algolia) authored
    • feat(bridge): power-aware perf watcher — reassert the chosen regime across AC/battery & resume · e94163f1
      The incident: mid-session on battery the pstate held turbo down (~2.5 GHz, 60°C,
      CPU pressure ~0 — the set never needed more). Plugging in on AC at 4% silently
      lifted that limit; the SAME workload jumped to 4.4–4.5 GHz and 85–93°C with fans
      maxed. Nothing was working harder — the platform just reset the frequency cap
      underneath our "cool" mode, and the tooling never noticed. set_mode() was
      fire-and-forget: it wrote the cap once and moved on.
      
      Fix: treat the perf mode as DESIRED STATE and reconcile it whenever the platform
      can drift it (AC/battery flip, resume-from-suspend), with a periodic drift check
      as the safety net.
      
      perf.py
        - write_desired()/read_desired(): persist the user-chosen mode (~/.cache/
          parvagues/perf-desired). "normal"/absent == maintenance off, so we never
          fight a deliberate switch back to platform defaults.
        - set_mode() records intent on every successful switch.
        - on_ac(): mains state from /sys/class/power_supply/A*/online.
        - reconcile(force): reassert desired if detect_mode() drifted (idempotent —
          "cool holding" when already in force, so no thrash).
        - run_watcher(): daemon loop. Reasserts (force) on a power-source edge and on
          resume (detected via a monotonic-clock gap), settling 2s for the platform;
          periodic force=False drift check otherwise. Seeds desired from the live mode
          on startup so an already-cool session is maintained immediately.
      
      bridge.py: launch run_watcher() as a daemon thread from `serve` (the always-on
      --user service is the right home; the tray is just a face).
      
      perf-tray.py: record intent (write_desired) on a successful tray switch too, so
      both faces agree and the watcher respects tray-initiated changes.
      
      Bonus: because "cool" also RT-schedules scsynth/sclang/Ardour/PipeWire, every
      reassert re-guarantees audio-chain priority after a power event — cheap insurance
      against post-transition xruns.
      
      Validated: compiles; reconcile idempotent; force reassert exercises the real
      perf-audio path (cool->cool, no disturbance); watcher thread confirmed live in
      the daemon with "on_ac=True desired=cool". Real power-transition proof lands on
      the next unplug/replug (logged to the bridge journal).
      PLN (Algolia) authored
  4. 19 Jul, 2026 1 commit
    • docs(tide-table): hammer v1 ear-findings measured + v2 rebuild log · a33099c3
      PLN re-listened to the forged hammer kit: vox cuts weren't voice-only,
      06_vox_breakitdown didn't loop, kit felt below the track's stature. All three
      confirmed by measurement and fixed in a v2 rebuild (catch report.md has the
      full story): demucs-stem vox bleed sat just −2.4 dB under the cut RMS on the
      worst file; the energy-corrected cut bounds were themselves bleed-biased
      (06 started a full second before the word); the rotated loop exports had
      never been promoted. v2 = roformer-vocals re-cuts on true onsets, a
      roformer-instrumental→demucs cascade for ghost-free instrument loops, the
      missing 2-bar breakitdown loop, and the rotation finally shipped.
      PLN (Algolia) authored
  5. 18 Jul, 2026 2 commits
    • feat(perf): RAPL package-power caps — watts are the thermal lever, not percent · bafd3133
      Discovery 2026-07-19: the Dell BIOS ships PL1=PL2=135W on this 45W-TDP
      i7-10875H. With no sustained-power brake, the only thing that ever slows
      the CPU is the 100C thermal limiter — which is how a set peaked at 91C and
      had to be aborted. Percent clock caps (--cool's max_perf_pct) don't bound
      multi-core power; watt caps do.
      
      All three modes now set package power limits via RAPL, saved/restored by
      --stop:
      - --optimize / --extreme: PL1 45W (chip spec) / PL2 90W — full nominal
        performance, bursts stay snappy, sustained runaway impossible.
      - --cool: PL1 28W / PL2 50W by default (~2.5GHz all-core), tunable with
        COOL_PL1_W / COOL_PL2_W.
      - --diagnose now prints the current PL1/PL2 against spec.
      PLN (Algolia) authored
    • docs(perf): 2026-07-18 Pulsar lag investigation + TidalCycles editor landscape research · 2f6b0b7a
      Records of the editor-lag deep-dive: local root-cause analysis (renderer
      main-thread saturation, console DOM growth, three marker leaks, .line
      transition churn, Electron 12 ceiling, thermal state) and the companion
      web-research report on the 2026 TidalCycles editor ecosystem (uzu/Codeberg
      migration, upstream issue #229, vim/VSCode/browser alternatives, no prior
      art for Pulsar forks). Fixes landed in the pulsar-tidalcycles fork, branch
      perf/event-highlighter, commit 6e9f374.
      PLN (Algolia) authored
  6. 11 Jul, 2026 8 commits
    • fix(foundry): pocket-zc rotation junction + DC-free loop exports · 1bb7c4fa
      Two refinements from measuring the first rotated re-cuts of the real kits:
      
      1. Junction in the pre-attack pocket. The rotation point was zc-snapped
         ±symmetrically around the downbeat, which could land the loop wrap
         mid-transient (rotated hammer drums read seam 0.50 / click_db −0.03:
         physically continuous, but the junction sat in busy material and a
         re-trigger clipped into the groove). _pocket_zc() searches
         [−25 ms, +2 ms] around the target and picks the zero crossing with
         the smallest local 2 ms RMS — the natural cut point in the quiet dip
         before the hit. Re-trigger catches the full attack; hammer drums
         grade recovered A 0.808 → S 0.988, bass 0.861 → S 0.916.
      
      2. Exports ship DC-free (B2), mirroring vox._finish_clip (4a74213c):
         the demucs stems' ~1e-4 DC bias was flowing into every exported loop
         (all three OLD superfreak kit files flag dc-offset on re-grade
         today). Mean-subtract after mono-sum, before write; shifts the
         zc-snapped edges by ≤1e-4 — inaudible, still effectively zero.
      
      86 tests green.
      PLN (Algolia) authored
    • feat(foundry): window-locked rotation — co-exported stems rotate by ONE shared slot · 3f9c27df
      Refinement caught before re-cutting the real kits: superfreak drums and
      bass ship from the SAME window (107.21s, 4 bars). If each stem rotated
      to its own strongest attack, their relative groove could shift by whole
      beats — bass's phrase-start is not necessarily on the drums' 1, and the
      kit is played together (loopAt on a common cycle), so relative phase
      between kit loops is musical content.
      
      export_take now derives the downbeat slot ONCE per shared time window
      from its most rhythmic stem (drums > bass > other > vocals priority)
      and applies that same slot to every stem of the window
      (_rotate_to_downbeat force_slot; per-stem roll still zc-snaps within
      ±10 ms on the stem's own mono ref). Stems at different windows keep
      independent rotation. Stem reads are cached (each stem was loaded twice
      otherwise).
      
      Test: two-stem synthetic window, drums downbeat at slot 3, other's own
      loudest attack at slot 6 — drums' marker must land at 0 and other's at
      3 (= 6−3, following drums), not 0. 86 passed.
      PLN (Algolia) authored
    • feat(foundry): rotate-to-downbeat export post-pass — loops start on the 1, not 'BCDA' · 0e69fa12
      PLN's kit audition (logged 6cfc7af0): loops felt 'timed ok but cut
      BCDA/DABC — not at a good start'. Measured and confirmed — the PLP beat
      grid has no downbeat anchor, so candidate windows start on an arbitrary
      beat: superfreak other_skank's strongest onset sat on slot 2/8 (its
      first 30 ms −23 dB below its own peak 30 ms), bass_deep's on slot 4/16,
      drums_dub's on slot 1/16. Bar-exact and tempo-locked, but musically
      rotated.
      
      The key insight that makes the fix free: for a verified clean-seam,
      bar-exact loop, ROTATION IS SAFE BY CONSTRUCTION. Looped playback of a
      rotated loop is the same audio cycle — the old wrap junction plays
      interiorly, and the new wrap (the rotation point) joins samples that
      were contiguous in the source, i.e. perfectly continuous. Rotation only
      changes where the loop STARTS, never how it wraps.
      
      _rotate_to_downbeat(): score each beat slot by its circular low-band
      (<200 Hz, kick-weighted) attack — RMS just after minus just before —
      plus a small full-band term; roll the winning slot to position 0,
      zc-snapping the roll offset (B1) so a re-trigger starts at a crossing.
      Length untouched (np.roll) ⇒ the <1 ms bar-multiple guarantee (0759def5)
      holds through rotation.
      
      Wired into export_take (on by default, rotate=False opts out; chops
      bars=0 never rotate). Verify-rerank stays export-faithful: _true_seam
      now returns min(old-wrap seam, rotated-wrap seam) — both junctions of
      the shipped cycle are measured, so rotation can never hide a bad wrap
      by moving it inside the file.
      
      Tests: +3 (downbeat at slot 5 → rotated to 0; slot-0 no-op; rotating a
      continuous loop never breaks the seam). 85 passed.
      PLN (Algolia) authored
    • docs(ear-feedback): superfreak/hammer kit audition — sub-only bass, BCDA loop… · 6cfc7af0
      docs(ear-feedback): superfreak/hammer kit audition — sub-only bass, BCDA loop rotation, demucs vocal high-pass
      
      PLN's on-disk verify of the forged kits, with measured confirmation of all
      three reactions: bass_deep is a pure-sub layer (99% <150 Hz at −12.8 dBFS —
      equal-loudness invisibility, not a level bug); loops are bar-exact but
      musically ROTATED (strongest onset lands on beat-slot 1/2/4 instead of 0;
      other_skank opens −23 dB below its own peak 30 ms — the 'cut DABC' feel);
      vox thinness is htdemucs routing vocal low end into bass/other (vocal stems
      carry ~0 energy <150 Hz). Fix directions logged: rotate-to-downbeat export
      post-pass, BS-Roformer vocals re-cut, harmonic-saturation bass variant.
      PLN (Algolia) authored
    • fix(vox): DC-free clickless cuts + length-rigid bar-quantized loop variants · 4a74213c
      Two defects surfaced while shipping the hammer (U Can't Touch This) vox
      kit extension — both fixed in the shared cut tail, now factored as
      _finish_clip():
      
      1. DC offset. Demucs vocal stems carry a small DC bias (measured
         −9.6e-5 stem-wide on the hammer catch); every cut inherits it and
         longer phrases tripped the grader's B2 dc-offset flag (|mean|>1e-4,
         'break it down' cut measured −1.1e-4). Subtlety: a naive
         mean-subtract BEFORE fading is undone by the fades — the edge ramps
         remove signal asymmetrically and re-introduce up to ~1e-3 DC
         (caught by the new regression test, not by the real-data smoke
         run). Fix: subtract the FADE-WEIGHTED mean c = Σ(w·x)/Σw (w = fade
         envelope), then fade — post-fade mean is exactly 0 per channel AND
         edges are exactly 0. On-disk verify of the shipped hammer vox files
         reads dc = −0.0e+00, edge = 0.0e+00 across all six.
      
      2. Loop-length drift. cut_phrase_loop placed the tail at exactly
         loop_beats × beat_s … then zc-snapped it (±12 ms), re-introducing
         the very grid deviation the length-preserving export rule (0759def5)
         forbids. The end snap is unnecessary — the fades already guarantee
         clickless edges — so the tail is now length-rigid: shipped
         08_vox_stophammertime_loop is 4.00 beats to 0.02 ms at BPM 132.51.
      
      Tests: tightened test_cut_phrase_loop_is_exact_beat_multiple from
      0.06-beat slack to <1 ms, added test_cut_removes_source_dc_offset
      (biased source ⇒ DC-free, unflagged cut). 82 passed.
      PLN (Algolia) authored
    • feat(foundry): length-preserving zero-crossing snap — exported loops are exact bar multiples · 0759def5
      The B1 zc-snap moved each loop edge independently (±10ms tolerance), so
      exported loop DURATIONS drifted off the bar grid: measured −4.2ms on a
      4-bar (superfreak bass) and −11.4ms on 2-bar loops (hammer kit, both
      edges snapped inward). loopAt absorbs the drift, but the kit contract we
      want is stronger: every exported loop's duration == bars × bar_len.
      
      Fix — _snap_length_preserving(): hold the exact bar-multiple sample
      count RIGID (bar_len_samples from the take's grid BPM) and slide the
      WHOLE window by a single common offset δ ∈ ±ZC_TOL_MS, picking the δ
      that minimizes combined edge amplitude |y[start]|+|y[end]| (tie-break:
      smallest |δ|). Both edges move together ⇒ duration is preserved by
      construction; guarantee |exported_dur − bars·bar_len| < 1ms (measured
      <0.02ms on real stems). Chops (bars=0) keep the per-edge snap.
      
      _snapped_window/_true_seam (verify-rerank, 791a0d78) now mirror the same
      scheme so the finder still measures the exact window export writes —
      export-faithfulness is preserved through the change.
      
      kit_multiples_check() makes the guarantee explicit per forge: after
      export, each file's duration-in-bars at the shared grid BPM is compared
      to the nearest integer multiple; >0.5% deviation warns with numbers.
      export_take_report() bundles export + check; the server /api/export now
      returns the multiples block.
      
      Spot-check on real stems (old per-edge vs new length-preserving snap,
      top-5 finder candidates re-graded on the exact export window):
        superfreak drums: dev +0.4…+3.6ms → ≤0.01ms; grades C→S, A→A, S→A, S→S
        superfreak bass:  dev −1.1…+3.7ms → ≤0.01ms; grades A→S, B→C, S→B, A→A
        hammer drums:     dev −1.8…+1.9ms → ≤0.01ms; all S stay S
        hammer bass:      dev −2.9…+6.9ms → ≤0.01ms; C→B, A→S, D→C, B→C
      Net: duration guarantee achieved; grade moves are the rigid-length
      window exposing true seams (some old windows only looked seamless
      because per-edge snap trimmed the tail). No systematic regression;
      existing superfreak/hammer kit exports untouched (validated, in use).
      
      Tests: +4 (snap length invariance, bar_len_samples, end-to-end exported-
      duration-is-exact-bar-multiple on a synthetic stem, multiples-check
      flags off-grid). 81 passed.
      PLN (Algolia) authored
    • feat(foundry): lyric-aware vocal sampler (engine/vox.py) — phrase-level,… · c5963f63
      feat(foundry): lyric-aware vocal sampler (engine/vox.py) — phrase-level, hook-ranked, loop-capable vox one-shots
      
      PROBLEM. The Foundry's vocal path (loops.analyze_chops) cuts vocals blind to what
      is sung — onset→onset slices scored on seam/zc mechanics. But a vocal one-shot's
      value to a livecoder IS the lyric. The freshly-shipped superfreak dub kit had four
      tiny 0.35-0.65s onset chops (03-06) with no idea they sat on top of some of the most
      iconic lyrics in funk ("she's a super freak", "kinky girl", "the kind you read about").
      
      APPROACH. engine/vox.py: transcribe → phrase-segment → iconicity-rank → cut → grade →
      lyric-name, DRY-reusing grade.py sub-scorers and naming.py convention.
       - transcribe: whisper word-level timestamps, shelled out like separate.py→demucs,
         cached at workspace/vox_transcript.json so re-runs are instant. Model escalates
         small→medium empirically when the known hooks don't surface (small garbled the
         patois delivery; medium recovers the real lyrics).
       - segment: group words on inter-word gaps (>=0.45s), cap 6s, split over-long phrases
         at their widest interior gap; keep per-phrase avg ASR confidence.
       - iconicity: feature-engineered rank = repetition (normalized phrase text + content
         n-grams recurring across the track — hooks repeat) + hook keywords (title-derived
         or --hooks) + clarity (ASR prob) + energy (RMS vs stem median) + duration sweet-spot.
       - cut: pre-pad + post-pad (clamped to next phrase), zero-crossing snap both edges,
         3-10ms fades so edge samples are ~0 regardless of where the snap landed; as-cut level.
       - kit-level loopability: report each phrase's duration in BEATS at the kit BPM; flag
         loop-capable phrases (within ±3% of a 1/2/4-bar multiple) and emit a bar-quantized
         _loop variant (tail pulled to the exact beat grid, still zc-snapped) for the best
         hooks, so a vocal can loopAt alongside instrument loops.
       - name: NN_vox_<lyricslug> (naming.lint-clean), dedup identical texts (best-graded
         instance, up to 2 takes of THE hook).
      
      VALIDATION. 17 new mocked-transcription tests (whisper never runs in tests): gap
      segmentation, over-long split, iconicity repetition-beats-oneoff + hook-keyword boost,
      beat/loop annotation, cut edges ~0 after zc+fade, next-start clamp, exact-beat loop
      variant, lyric-slug + lint contract. Full suite 60->77 green. Applied to the superfreak
      stem: medium transcript contains every iconic line; shipped 6 curated vox files (S/B
      tier) replacing the 4 blind chops, incl. one 2-bar (8.00-beat) loop variant. All
      re-graded on disk: no clip (peaks <0.8), no DC (<2e-4), edges exactly 0.0.
      PLN (Algolia) authored
    • fix(foundry): verify-rerank the loop finder against true post-snap seam + robust sparse seam · 791a0d78
      Problem — the finder's rank didn't reflect true seam quality. On a real
      end-to-end run over the Super freak dub kit (Soul Sugar meets Dub Shepherds),
      several top-scored 2-bar windows CLICKED at the wrap after export while cleaner
      windows ranked below them. Root cause: analyze_stem scores the seam on the RAW
      candidate window, but export_take zero-crossing-snaps both boundaries (B1) AND
      slices at the 3-decimal-rounded start_s/end_s — and both transforms move the wrap.
      A beat-exact window measuring seam 0.79 read 0.07 once rounded, and that rounded,
      snapped slice is what actually ships. So the finder's seam proxy diverged from the
      grader (grade.py), which measures the window that exists on disk — the ground truth.
      
      Approach — a verify-rerank pass (loops._verify_rerank) after candidate generation,
      before dedup/top_n. It re-measures the ~3×top_n survivors' seam on the TRUE
      post-snap window — the exact zc-snapped, rounded-bounds slice export writes —
      reusing grade.seam_score (DRY, no duplicated DSP). The proxy seam term is swapped
      for the true seam in the composite (same weights), and any candidate whose true
      seam falls below VERIFY_SEAM_FLOOR is hard-demoted so a click can never top the
      list. Gated behind a new analyze_stem(..., verify=True) kwarg (default on; False
      reproduces the pre-#19 raw-proxy ranking for autotune baselines). Public signatures
      (analyze_stem, find_takes, weights dict) unchanged — autotune.py/server.py intact.
      
      Also fixed a sparse-material seam false-positive in grade.seam_score: the wrap
      curvature was normalized by the MEAN |2nd-diff|, which collapses to ~0 on sparse
      dub percussion (mostly silence + a few hits), blowing the click ratio up (129× on
      a genuinely clean loop). Now normalized by the 90th-percentile |2nd-diff|,
      amplitude-floored — 2–3× on the same clean loop.
      
      Validation (drums+other stems, bars 1/2/4, shared 132.5 BPM grid,
      export-faithful grades):
        drums top-3  BEFORE  C 0.65 / C 0.63 / A 0.80   (clicks @93.08s, @24.06s)
                     AFTER   S 0.98 / S 0.95 / A 0.81
        other top-3  BEFORE  B 0.72 / B 0.72 / B 0.70   (bad favorite @56.74s clicks)
                     AFTER   S 1.00 / S 0.98 / S 0.99
      The known-good drums 4-bar @107.21 (S) and other regions surface; the clicking
      other @56.74 is demoted out of the top entirely.
      
      Tests: +2 verify-rerank regression tests (a proxy-clean but discontinuous wrap that
      survives the snap is demoted below a seamless one; verify=False leaves the proxy
      untouched) and +1 sparse-percussion test (a clean sparse loop is NOT flagged
      clicking). Suite 57 → 60 green.
      PLN (Algolia) authored
  7. 29 Jun, 2026 9 commits
    • docs(onboarding): refresh hexa kit — YouTube→emotion pipeline, dual tokens, docs links · 94711a72
      Refreshed the onboarding template (rendered PDF is gitignored, carries live tokens):
      -  NEW §3 "The YouTube → emotion pipeline": /sources fetch → /jobs poll →
        /artifacts download → /analyze/emotion, the showcase end-to-end flow (curl + the
        @nech/api TS shape), idempotent-on-repeat noted.
      - Credentials block now carries BOTH tokens: the api:* Bearer AND the npm read
        token for installing @nech/api from npm.nech.pl.
      - Endpoint table gains /sources, /jobs/{id} (+DELETE), /artifacts/{cid}/{name}.
      - §1 surfaces the live /docs + /openapi.json links and corrects "access": only
        /healthz is public now; docs/spec need api:docs (covered by api:*).
      - Sections renumbered (pipeline=3, client=4, good-to-know=5, support=6).
      PLN (Algolia) authored
    • docs(todo): persist open task state into project TODO.md files + archive #44 · 67d82468
      So the session task board survives a push to git.nech.pl (it is local harness state,
      not in git). Two cold-readable TODOs next to their code:
      - armada/api/TODO.md — Fourier audio-API remaining: heavy chain #30 /separate +
        #31 /loops/grade/correlate (need a GPU runner), optional #47 X-Accel + #46 Grafana,
        follow-ups (nech_api python client, hexa npm read token); EPIC #21 closes with the chain.
      - tools/foundry/TODO.md — #19 auto-tune loop (in-progress, harness validated +10.5%,
        exact resume steps) + #20 batch-explore corpus.
      Plus archived #44 (Verdaccio + @nech/api live) to completed-archive.md.
      Root TODO.md left untouched (it is the paused Pulsar livecoding-perf session).
      PLN (Algolia) authored
    • feat(clients): @nech/api published — retire interim nech.ts (#44) · 851767ff
      Verdaccio is live on npm.nech.pl and @nech/api@0.1.0 is published + install-verified
      (AudioApi/Configuration import clean as a consumer). So the hand-written zero-dep
      nech.ts drop-in is retired (git rm); @nech/api is the one true client. README +
      onboarding.html now show the @nech scope install with the read token, and the docs
      row reflects that /docs + /openapi.json are bearer-gated (api:docs) not public.
      VERDACCIO.md marked DEPLOYED with the three gotchas folded in (listen 0.0.0.0,
      chown 10001, TLSv1.2-only) + the auth-gated-reads note.
      PLN (Algolia) authored
    • docs(tasks): archive #48 — docs/openapi gated behind api:docs (deployed live) · ae65658e
      Rich entry for the documentary: the anonymous-exposure finding, the cross-cutting
      api:docs design (read-specs decoupled from call-API), the lockstep drift-guard
      dance, and the shared-repo rebase + diff-before-tee discipline that kept SRE work
      intact.
      PLN (Algolia) authored
    • re-vendor(scopes): api:docs cross-cutting docs scope (lockstep w/ nechapi 0ab148b) · d4d27a49
      Mirrors the merged platform canonical: required_for() maps docs|openapi.json|redoc
      → {realm}:docs. Byte-identical to nechapi/_platform/scopes.py (drift guard green).
      Part of #48; deploy = nechapi-platform redeploy + nginx reload.
      PLN (Algolia) authored
    • docs(tasks): archive #27 (artifact store) + #29 (/sources) · f140b0e8
      Rich entries for the documentary trail: the freebox-deferred simplification of
      #27, the open-url-scope-with-apikey-trust decision on #29, the SSRF block set,
      and how #25/#26 pre-built the seams (result_ref/gc, alias table, born-done path)
      that made both mostly wiring.
      PLN (Algolia) authored
    • feat(audio-api): /sources (#29) + artifact store (#27) · c7e5392b
      Two links of the heavy chain, built on the #25 job backbone. Both ship the CODE
      now; they go live the moment erable ssh is back (yt-dlp install) — no GPU needed.
      
      #27 — artifact store (artifacts.py). Big binaries (fetched sources, later stems/
      loops) live content-addressed on local disk under FOURIER_ARTIFACTS, tracked as
      ordinary cache rows (kind→result_ref), so the existing LRU cache.gc() already
      evicts the coldest under a cap. Freebox was the original SSOT plan — deferred per
      PLN; erable-local for now. Serving is zero-copy via nginx X-Accel-Redirect
      (FOURIER_X_ACCEL), FileResponse fallback in dev. resolve() refuses path traversal
      / anything outside the root.
      
      #29 — /sources (engines/sources.py + POST /sources). The worker shells out to
      yt-dlp → bestaudio → 44.1k WAV (analysis-ready for /separate, /loops, features),
      content-addresses it, stores it, and aliases url→content_id so a repeat URL is an
      idempotent born-done job (no re-download). Submit returns 202 {job_id}; poll
      /jobs/{id}. URL scope is OPEN (any http(s)) per PLN — gated by the platform apikey
      + nechapi monitoring — but we still hard-block the SSRF footguns (non-http(s),
      localhost, RFC1918/link-local/reserved IPs; incl. the 169.254.169.254 metadata
      classic). The fetch is a thin seam so tests mock the network with a synthetic WAV.
      
      Also: GET /artifacts/{cid}/{name} serving endpoint; healthz reports yt-dlp
      presence; _meter now counts by ROUTE TEMPLATE not concrete path (else /jobs/{id}
      & /artifacts/{id} would mint unbounded Prometheus series). OpenAPI refreshed
      13→17 ops, @nech/api regenerated (submitSource/getArtifact/getJob/cancelJob).
      yt-dlp added to requirements-deploy; DEPLOY.md §5b documents the yt-dlp+ffmpeg
      install and the nginx internal location. 78→95 tests green.
      PLN (Algolia) authored
    • rename(audio-api): codename Douanier → Fourier · 5ad4ed0e
      The audio API was internally codenamed "Douanier" (Le Douanier Rousseau — a
      customs-officer pun on edge auth). Renamed to "Fourier": the FFT is literally
      the transform behind /spectrum and most of the feature stack, and central auth
      is the platform’s job now, not ours — so the name should point at the signal
      work, not the gate.
      
      Internal-codename-only: the public contract (/audio/v1, gateway headers) is
      untouched. Mechanical case-aware sweep across armada/api + the completed-archive,
      plus hand-rewritten prose (the Rousseau attribution → Joseph Fourier; dropped the
      customs-gate / 🛂 metaphors). Renames: douanier.py → fourier.py,
      deploy/douanier-worker.service → deploy/fourier-worker.service. Env prefix
      DOUANIER_* → FOURIER_* (config contract; safe — nech.pl is pre-users), dev venv
      ~/.virtualenvs/douanier → fourier (shebangs fixed). 78 tests green after.
      PLN (Algolia) authored