# OPAL 2026 — completed-task archive

Long-term learning + blog/video source material for the gig-prep thread. Each entry is a
finished board task captured in full (description · done · learnings · deps), appended by the
`/pre-compact` ritual before the task is cleared off the active board.

Deliberately **separate from `armada/tasks/completed-archive.md`**, which is scoped to
L'Armada (the media/marketing toolbox). Same discipline, different thread. Newest first.

---

## #39 — Move the rig off the onboard SOF codec onto the UMC202HD
- **Description:** the XPS's `sof-soundwire` codec was a tracked glitch source, and on
  2026-07-26 it escalated from "suspected" to "took the whole stack down": the SOF DSP stopped
  answering IPC (`ipc tx timed out for 0x30100000`, ASoC -110) while probing capture `pcm4`
  (Microphone), and WirePlumber went to **uninterruptible D-state in `ipc3_tx_msg_unlocked`** —
  unkillable even by SIGKILL. It took scsynth with it, mid-session. Only a reboot cleared it.
- **Done:** UMC202HD made the default sink; SOF internal DMIC disabled via
  `~/.config/wireplumber/wireplumber.conf.d/51-disable-sof-dmic.conf` (`node.disabled` on
  `HiFi__Mic__source` = card3 `pcm4c`, the exact PCM that hung). Capture-only — 5 SOF playback
  sinks left intact. Verified 0 IPC errors after restart. `tools/gig_record.sh` needed no change
  at all: it captures `$(default sink).monitor`, so it auto-followed onto the UMC bus.
- **Learnings:**
  - **Card profile: keep `HiFi: Default`, not `pro-audio`.** Only two exist. HiFi gives one
    stereo sink + 2 mono sources, which is what ordinary apps expect; `pro-audio` strips the UCM
    mapping for raw channel access — a real win on a big desk, worthless on a 2-in/2-out box.
  - **Ardour does NOT follow the PipeWire default sink** — it restores its own saved ports. The
    rig kept playing out the laptop jack after the "switch", with no warning. Spawned #41.
  - **The ALSA card number is not stable** across resume/replug (UMC was card 0, later card 1).
    Match by name, never index.
  - **Suspend/resume breaks it at the session layer, not the hardware layer:** 118 re-enumerations
    on resume, card healthy afterwards, but WirePlumber never rebuilt the node → no sink, silent
    fallback to SOF. Diagnose by whether events are *ongoing*, not by their count.
  - A wireplumber restart **resets sink volume to the device default (87%)** — which is how a
    routine verification tone became painfully loud. Set a safe level *before* making sound.
- **Deps:** unblocked by #40; spawned #41; feeds #16 (balanced outs for FOH), #17 (gain must be
  re-measured on balanced TRS — the 3.5mm numbers do not carry over).

## #38 — Verify BootTidal.hs boots clean on a fresh SuperCollider start
- **Description:** commit `98d073f` baked the live-validated prelude (the gF mute-bomb fix) into
  `BootTidal.hs`. It had only ever been proven as a live shadow-eval, never from cold.
- **Done:** verified by a real cold boot after the reboot (`bash gig-up.sh`, exit 0): perf mode
  applied, **65 sample folders preloaded** from the last 20 tracks, SuperDirt ready with scsynth
  up + `:57120` listening + MIDI claimed, Ardour and Pulsar launched. No fallback to the stale
  `main_fairbanks`. Routing confirmed: `SuperCollider:out_1..N` land on Ardour's `Tidal NN` stems.
- **Learnings:** the gap between "live-confirmed" and "durable" is exactly where the gF bug hid
  (#21) — this closed it for real. Also: a cold boot is the cheapest moment to change audio
  routing, because nothing is loaded yet and there are no warm samples to lose.
- **Deps:** closed out the #21/#13 thread; prerequisite for #14 rehearsals.

## #40 — Conclude the SOF hang / UMC audio-path decision with PLN
- **Description:** a deliberately-written "START HERE" task, created before a forced reboot, so
  the next session would open by asking PLN to decide rather than re-deriving the context.
- **Done:** post-reboot state verified (UMC card 0, live sink, 6 enumerations / 2 disconnects =
  normal boot churn not the 1 Hz loop, **0 SOF IPC errors**, WirePlumber in `S` not `D`), then
  all four decisions put to PLN and executed the same turn.
- **Learnings:**
  - **A resume task written for a cold reader works.** It carried the kernel signatures, the
    verification commands and the open decisions, and the session resumed with zero re-derivation.
    Worth repeating for any hand-off across a reboot or a compact.
  - **One decision in it was based on a wrong premise of mine:** I flagged the branch-vs-master
    question as a convention violation. PLN corrected the doctrine — *"my convention is
    master-by-default-no-branching-hell im a lone dev… i am the one who reviews"*. Branch only for
    cloud agents, parallel sessions, or genuine prototypes. The memory that said otherwise was
    wrong and cost real deliberation; corrected in `feedback_commit_often`.
- **Deps:** blocked #39; both closed together.

## #15 — Mid-set recovery runbook ("when it breaks")
- **Description:** PLN needed a playbook for the gig going wrong on stage, where the failure
  mode is panic as much as it is technical.
- **Done:** `RUNBOOK.md` at repo root (commit `9bdf395`). Symptom-first structure, ordered
  cheapest-first, with an explicit rule zero that a SuperCollider reboot is the *last* resort.
- **Learnings:**
  - Structure it **symptom-first, not cause-first** — mid-set you don't know the cause, you
    know the sound stopped. §0 is a 10-second triage that routes to a section.
  - **A frozen Pulsar does not stop the audio.** Patterns live in the ghci process, not the
    renderer, so `Window: Reload` is a cheap fix that keeps the music playing. This inverts
    the instinct to panic-kill the editor.
  - Documented §2b, the **zombie port 6010** failure (see #21 learnings) — the nastiest kind,
    because it is completely silent.
  - **Fader 77 is global gain.** Down = everything silent, nothing looks broken. Now in the
    pre-set checklist because it's a one-move fix that's easy to overlook.
  - Every procedure is **labelled by evidence strength** — verified-by-running vs.
    read-from-source vs. inherited-from-prior-incident. A runbook you've never executed is a
    hypothesis and should admit it.
- **Deps:** feeds #14 (rehearsal must actually *execute* the runbook, especially §5).

## #21 — The gF/djfbus mute-bomb
- **Description:** `gF1/2/3` sent values into `djfbus`, a **non-registered** SuperDirt effect.
  LCXL faders rest at 0, so any gF-wrapped stream muted with no recovery — and essentially
  every mature ParVagues track wraps its streams in gF. Armed across the whole set.
- **Done:** Safe-at-rest rewrite using core `hpf`/`lpf` mapped so fader-rest == bypass
  (`gHPF = range 20 8000`, `gLPF = range 20000 180`, `gF3 = id`). Shipped first as a
  shadow-eval in `live/lib/prelude.tidal` (`29ce103`), live-confirmed by PLN ("f123 djfs seem
  to work great overall"), then **baked permanently into `BootTidal.hs`** (`98d073f`).
- **Learnings:**
  - **A fix that lives only in a running process is not a fix.** It was correct and validated
    for a full day while still being one SuperCollider reboot away from silently rearming.
    "Live-confirmed" and "durable" are different states; the gap between them is where this
    kind of bug hides.
  - Design controls so **rest position == no effect**. The bug wasn't really the missing bus,
    it was that the *default* state of the hardware was an *extreme* value. `gF3 = id` is the
    strongest form of this — a spare fader that cannot touch sound at all.
  - Discovered while type-checking: a stale `ghc` holding port **6010** makes a fresh Tidal
    print "Can't handshake with SuperCollider" and then behave **completely normally in
    Pulsar** while nothing plays and no error surfaces. Detect with `ss -lunp | grep 6010`.
  - `prelude.tidal` was demoted from accidental-source-of-truth to explicit **rescue sheet**,
    with the stale-boot tell written down (push fader 49 → mutes; pulling to 0 makes it
    *worse*, because 0 is the mute position on the old defs and bypass on the new).
- **Deps:** unblocked the same bake-in for #13; feeds #15's §1a and #18.

## #32 — Always-on lean gig recorder
- **Description:** Capture every performance without depending on Ardour, and slice test
  samples for the Slop visuals work.
- **Done:** `tools/gig_record.sh` (`29ce103`) — PipeWire → rotating hourly opus segments in
  `recordings/`, with `start`/`stop`/`status`/`slice` subcommands. Verified running
  continuously across this session (pid 3686091).
- **Learnings:** deliberately a **separate process from Ardour** — if Ardour dies you still
  have the set. A freshly-rotated segment reading 0 bytes is normal and should not read as
  failure; `status` had to be taught to say so.
- **Deps:** spawned #37 (GUI toggle, HUD REC indicator, silence-trim, per-track metadata).

## #19 — Free 100GB+ before the gig
- **Description:** Disk was at ~28GB free with a deadline of ~Aug 1; Ardour stem recordings
  were the bulk.
- **Done:** Freebox mount repaired, 436GB of **verified-identical** stem media archived and
  deleted → **188GB free**.
- **Learnings:** `du`-summed bytes deleted (436GB) vs `df`-reported bytes freed (160GB)
  disagreed sharply. Cause is btrfs `compress=zstd` plus reflink/CoW sharing — benign
  filesystem semantics, not partial deletion. **`df` is the authoritative number**; don't
  chase the gap. Verify identity *before* deleting, always.
- **Deps:** unblocked all recording-heavy work (#32, rehearsals).

## #9 — 1-click ordered gig launcher
- **Description:** At every gig start, whichever of SuperDirt or Ardour opened the
  LaunchControl XL first won it. When Ardour won, sclang couldn't get the controller and PLN
  was stuck doing a disconnect/reconnect MIDI dance during setup.
- **Done:** `gig-up.sh` (`ba8e84a`, hardened by `e304355`, `0c65fda`, `2bc6a8e`, `c35c902`) —
  brings SuperDirt up **first**, waits on a real readiness gate (scsynth alive + `:57120`
  listening + a SuperCollider MIDI client present), and only then launches Ardour and Pulsar.
- **Learnings:**
  - **Order beats retry.** The MIDI dance was a symptom of a race; gating on readiness
    removed the race instead of automating the workaround.
  - Gate on **observable readiness**, not `sleep`. Three independent checks, because any one
    of them alone can be true while the rig is still unusable.
  - Made **idempotent** — it converges the rig rather than spawning duplicates, so it's safe
    to run when you're unsure of the current state. That's exactly the property you want from
    something you'd run while panicking (see #15 §5).
  - `set -u` bit hard: `open_term` read an unused `$1` and crashed the launcher on start
    (`e304355`).
- **Deps:** feeds #15 §5, #18; prerequisite for #14.

## #27 — Editor highlight horizontal jitter
- **Description:** Eval highlights visibly shifted text horizontally when toggling.
- **Done:** `border` → `outline` across `.event-highlight` and all 17 per-stream `-dN` rules.
- **Learnings:** **`border` participates in layout and reflows text; `outline` does not.** For
  any decoration that toggles at performance rates, outline is the correct primitive. (The
  *residual* wiggle after this fix turned out to have a separate cause — see #35, a competing
  runtime style injection toggling 30×/sec.)
- **Deps:** partially overlaps #35; both feed #6.

## #5 — Version-control the pulsar-parvagues-hud package
- **Description:** The HUD package was live-edited with no git history.
- **Done:** Package brought under version control with its own repo/branch.
- **Learnings:** the HUD, `pulsar-tidalcycles`, and `~/.pulsar/styles.less` are **three
  separate version-control domains** (the last untracked entirely, as user config). A single
  visual bug can require coordinated edits across all three — which is exactly what #35 hit.
  Knowing the domain boundaries is a prerequisite for debugging anything editor-side.
- **Deps:** prerequisite for #6, #7, #22, #35, #36.
