Commit 9bdf3953 by PLN (Algolia)

docs(gig): mid-set recovery runbook + measured OPAL BPM arc

Two gig-prep deliverables that needed no ears, written at J-10.

RUNBOOK.md (#15) — the "when it breaks" playbook, organised symptom-first because
that is how it actually gets read: you don't know the cause, you know the sound
stopped. Ordered cheapest-first, with an explicit rule zero that rebooting
SuperCollider is the LAST resort (30-120s of dead air and every pattern lost) and
almost nothing needs it.

The section worth having written down is 2b, the zombie control port. Observed live
today: a leftover ghc process holds Tidal's port 6010, so a fresh Tidal boots, fails
to bind, prints "Can't handshake with SuperCollider without control port" — and
Pulsar looks completely normal. You eval and nothing happens, with NO error surfaced
in the editor. That is the nastiest class of failure: silent, and indistinguishable
from "my code is wrong". Detection is one line (ss -lunp | grep 6010, expect exactly
one ghc and it should be the current one), so it's now in the pre-set checklist
rather than only in the recovery section.

Also captured: the mute-bomb rescue path (prelude.tidal as in-session hot-patch on a
stale boot, with the tell that pulling fader 49 to 0 makes it WORSE not better);
fader 77 being down as a silent global-gain failure that looks like nothing is
broken; Pulsar being frozen NOT stopping audio, since patterns live in ghci and not
the renderer, so Window: Reload keeps the music playing; and the LCXL USB-OUT
endpoint stall where LEDs die but input survives (replug).

Every procedure is labelled by how well it's actually established — §1a/2a/2b/2c/7
have commands verified by running them today, §4/§6 come from documented prior
incidents with known-good fixes but weren't re-tested tonight, and §5's gig-up.sh
path was read from source rather than exercised end to end. A runbook you have never
executed is a hypothesis, and it should say so.

docs/2026-07-25-opal-bpm-arc.md (#12 input) — read setcps out of all 12 setlist
tracks instead of trusting the backlog's [bpm] markers, and three disagree:
- Gimme Acid: 80 measured vs 160 written — almost certainly the standard dnb
  half-time convention, but it decides where the track sits in the arc.
- Sunshine: 144 measured vs 166 written. 22 BPM is far too big to be a rounding
  note, and it inverts the end of the set: the backlog plans Mafia(160) ->
  Sunshine(166) as a rising finish, but measured it is a 16 BPM DROP. Either the
  file drifted or the number was aspirational — needs a decision, since the shape
  of the finale depends on it.
- Desire: 127 vs 129. Trivial, but it's the FINALE, so make them agree.

Charted the measured arc and flagged three >40 BPM cliffs (->89, ->80, ->160). The
89 and 80 ones look deliberate — a slow track between fast ones is a real dramatic
move — and they are only a problem if the transition is unprepared, which is
precisely what the riser audit (#33) and default-xfade (#13) exist to fix. Also
noted the 124/124/128 plateau as the most transition-safe stretch in the set.

Ordering itself is left as PLN's call; this is only the tempo dimension.
parent 5a010b11
# RUNBOOK — when it breaks mid-set
**Task #15.** Symptom-first, because that's how you read it in a panic: you don't know the
cause, you know *the sound stopped*. Written 2026-07-25 (J-10 to OPAL).
> **Rule zero: the room forgives silence, not flailing.** Everything below is ordered
> cheapest-first. Do not reboot SuperCollider as a first move — it costs 30–120s of dead air
> and loses every pattern. Almost nothing here needs it.
---
## 0. The 10-second triage
Something's wrong. Ask in this order:
1. **Is it only ONE stream?** → §1 (a track problem)
2. **Is it ALL sound, but Pulsar still responds?** → §2 (a routing/eval problem)
3. **Is Pulsar frozen/unresponsive?** → §3 (an editor problem — audio may be fine!)
4. **Did the laptop just wake / did the screen just do something weird?** → §4 (the display
stack takes the audio stack down with it)
**Silence-with-a-working-editor is the most recoverable state there is.** Keep a hand on the
LCXL and keep the last working track's code in front of you.
---
## 1. One stream died
### 1a. You pushed a fader and the sound vanished — THE MUTE-BOMB
**This is the #1 historical footgun.** Old `gF1/2/3` sent values into `djfbus`, a phantom
SuperDirt effect; faders rest at 0, so gF-wrapped streams muted with no recovery.
- **Fixed permanently 2026-07-25** (commit `98d073f`) — `BootTidal.hs` now uses safe-at-rest
`hpf`/`lpf` where fader-rest = bypass. A fresh boot is immune.
- **But if you're on a STALE boot** (an old ghci that survived, a machine that didn't pick up
the new file): symptom is *push fader 49 → sound MUTES instead of sweeping*.
- **Rescue, no reboot:** open `live/lib/prelude.tidal`, Ctrl-Enter inside each `:{ … :}`
block. It shadows the boot defs in the live session. Patterns keep playing.
- **Instant mitigation while you find it:** pull the fader back to 0. Rest = bypass on the
new defs; on the old broken defs, 0 is the *mute* position, so if pulling to 0 makes it
worse you have confirmed you're on a stale boot.
### 1b. A stream is just gone / wrong
- `d5 $ silence` then re-eval the block. With default-xfade `dN` (#13) this blends over 4
cycles rather than cutting.
- Need it back NOW with no blend: `jumpTo 5 $ …` (orbit-correct hard cut) or `p 5 $ …`.
- Muted by a global mask? Check `gM1/2/3` — buttons **73 / 74 / 75**. Press to un-toggle.
- Panic chord stuck on? Buttons **73+74+91+92** together toggles `^93`. If `gPanic` is wired
into that stream, `^93=1` means gain 0. Hold the chord again to release (LEDs show state).
---
## 2. All sound gone, Pulsar still works
### 2a. Check SuperDirt is actually alive — VERIFIED diagnostic
```sh
pgrep -x scsynth && ss -uln | grep 57120
```
- **Both OK** → SuperDirt is up, so it's a Tidal-side problem → §2b.
- **Nothing** → scsynth died → §5 (the only real reboot case).
### 2b. Tidal booted but nothing reaches SuperCollider — the ZOMBIE PORT
**Observed live 2026-07-25.** A leftover `ghc` process holds Tidal's control port 6010; the
new Tidal boots, fails to bind, and prints *"Can't handshake with SuperCollider without
control port"*. Pulsar looks completely normal. You eval, and nothing happens — **no error**.
```sh
ss -lunp | grep 6010 # who holds it
```
Expected healthy output — exactly one `ghc` process, and it's *your current* Tidal:
```
UNCONN 0 0 127.0.0.1:6010 0.0.0.0:* users:(("ghc-9.4.7",pid=NNNN,fd=22))
```
If that pid is a stale one, kill it (`kill NNNN`) and restart Tidal **in Pulsar only**
(Ctrl-Alt-B / Tidal: Boot) — SuperCollider is untouched, samples stay warm.
> Worth doing as part of soundcheck, not just as a fix: this failure is *silent*.
### 2c. Ardour stole the LaunchControl XL
Whoever opens the LCXL first wins it. `gig-up.sh` exists precisely to force
SuperDirt-first ordering, but if something restarted out of order:
```sh
aconnect -l | grep -iE "client [0-9]+:"
```
You want a `client 128: 'SuperCollider' [type=user,...]` present. If SuperCollider isn't
listed as a MIDI client, it never claimed the controller — knobs will do nothing while
audio still plays.
### 2d. Master path
- Ardour master fader / limiter — is the record-bus limiter clamping everything?
- `# gain` global: `^77` is the global gain fader (`midiGGlobal = "^77" * 1.3`). **If fader
77 is down, everything is silent and nothing looks broken.** Check it early — it's a
one-move fix that's easy to overlook.
---
## 3. Pulsar frozen or crawling
**Audio usually keeps playing — do not panic-kill it.** Tidal patterns live in the ghci
process, not the editor; a wedged renderer doesn't stop the music.
- **`Window: Reload`** (Ctrl-Alt-Ctrl-L / command palette) — reloads the renderer, keeps the
ghci session and therefore the playing patterns. This is the cheap fix and it resolves the
known highlighter marker-leak renderer burn.
- If the reload loses the Tidal session, re-boot Tidal in Pulsar and re-eval the current
track from the file — SuperDirt and your samples are still warm.
- Known standing issue: the highlighter marker pool leaks and pegs a core (#7). Reload is the
mitigation until that's fixed.
---
## 4. Post-suspend / display-stack death
**Established root cause** (memory: `project_resume_audio_crash`): on resume, kwin can abort,
and because sclang was Qt-linked it got cascade-killed along with scsynth and Ardour —
"music death" after an overnight suspend.
- **Mitigated** by headless sclang (`QT_QPA_PLATFORM=offscreen`, commit `d834667`) — SuperDirt
no longer shares fate with the display stack.
- **Don't suspend the gig laptop between soundcheck and set.** The mitigation reduces the
trigger; it doesn't make resume free.
- dGPU off (`./gpu-mode.sh`) further reduces the trigger.
---
## 5. Full SuperCollider restart — the LAST resort
Only when scsynth is genuinely dead (§2a found nothing).
```sh
./gig-up.sh
```
Idempotent: it converges the rig rather than spawning duplicates, brings SuperDirt up
**first**, waits for a real readiness gate (scsynth alive + `:57120` listening + a
SuperCollider MIDI client), and only then touches Ardour/Pulsar. Preloads the last 20 edited
tracks' samples so the first plays don't crack.
**Expect up to ~120s** (`READY_TIMEOUT`). That is a long time on stage. Have something to say,
or a track already playing off another source.
---
## 6. Hardware
### LCXL lights are dark but knobs still work
**Diagnosed pattern** (memory: `reference_lcxl_led_stall`): the USB **OUT** endpoint stalls
(urb -32) while IN keeps working — so input is alive and LED feedback is dead.
**Fix: physically replug the LCXL.** It re-enumerates and LEDs come back. Input bindings
survive because the CC→OSC bridge re-claims it.
### Knobs do nothing at all
Not the same failure — that's §2c (SuperCollider never claimed the controller).
---
## 7. Always-on recording — check it's still going
The set is being captured continuously to rotating opus segments, independent of Ardour.
```sh
bash tools/gig_record.sh status
```
Healthy output is `gig_record: RUNNING (pid NNNN)` plus recent segment files in
`recordings/`. A brand-new segment reading `0` bytes is **normal** — it just rotated (they
rotate hourly).
If it died mid-set: `bash tools/gig_record.sh start`. It's a separate process from Ardour
on purpose — if Ardour dies you still have the set.
---
## Pre-set checklist (the 2 minutes that prevent §2b and §2d)
1. `./gig-up.sh` — wait for the readiness gate, don't race it.
2. `ss -lunp | grep 6010` → exactly one ghc, and it's the current one.
3. `aconnect -l | grep SuperCollider` → present.
4. **Fader 77 up.** Faders 49/50 down (= bypass).
5. Eval one track, hear it, then `hush`.
6. `bash tools/gig_record.sh status` → RUNNING.
7. Do **not** suspend the laptop after this point.
---
## Status of these procedures
- **§1a, §2a, §2b, §2c, §7** — commands verified by running them on 2026-07-25.
- **§4, §6** — from documented prior incidents with established root causes; the *fixes*
(headless sclang, replug) are known-good, but not re-tested tonight.
- **§5**`gig-up.sh` behaviour read from source, not exercised end-to-end tonight. Worth a
real rehearsal run (#14), because a runbook you've never executed is a hypothesis.
# OPAL 2026 — measured BPM arc (task #12 input)
**Date:** 2026-07-25 · **Method:** read `setcps` out of each setlist track's `.tidal` and
compare against the BPM annotations in `backlog.md` § OPAL 2026. Measured, not assumed —
the backlog markers turned out to be wrong on three tracks.
## Backlog vs. reality
| # | Track | backlog | **measured `setcps`** | |
|---|---|---|---|---|
| 1 | Ceci n'est pas Une Bombe | 124 | **124** | ✓ |
| 2 | WAP | 133 | **133** | ✓ |
| 3 | Am i Doing it Right? | 89 | **89** | ✓ |
| 4 | Take Five Drops | 124 | **124** | ✓ |
| 5 | Piment Brésilien | 124 | **124** | ✓ |
| 6 | Perfect <3 | 128 | **128** | ✓ |
| 7 | Gimme Acid | 160 | **80** | ⚠︎ half-time notation |
| 8 | Vague de CRIME | 120 | **120** | ✓ |
| 9 | Mafia | 160 | **160** | ✓ |
| 10 | Sunshine | 166 | **144** | ❌ **22 BPM out** |
| 11 | Desire (FINALE) | 129 | **127** | ❌ 2 BPM out |
| 12 | Revolution (encore) | — | **114** | — |
| — | Electric Hammer (floated closer) | — | **130** | — |
### The three that don't match
**Gimme Acid — 80 measured vs 160 written.** Almost certainly not a bug: this is the standard
dnb half-time convention (80 written = 160 felt, the kick pattern does the doubling). Worth
confirming, because it matters for the arc: if it *feels* like 160 it belongs next to Mafia,
if it *is* 80 it's the slowest thing in the set after Do It Right.
**Sunshine — 144 measured vs 166 written.** This is the one to actually check. 22 BPM is far
too big to be a rounding note, and it changes the shape of the set's peak: the backlog plans
Mafia (160) → Sunshine (166) as a rising finish, but measured it's Mafia (160) → Sunshine
(144), which is a 16 BPM *drop*. Either the file drifted from the intent, or the backlog
number is aspirational.
**Desire — 127 vs 129.** Trivial, but it's the FINALE, so worth making the file and the plan
agree.
## The measured arc
Setlist order as written, measured tempi:
```
124 Bombe ▓▓▓▓▓▓▓▓▓▓▓▓
133 WAP ▓▓▓▓▓▓▓▓▓▓▓▓▓▓
89 Doing it Right ▓▓▓▓▓ ← -44 cliff
124 Take Five ▓▓▓▓▓▓▓▓▓▓▓▓ ← +35 climb
124 Piment ▓▓▓▓▓▓▓▓▓▓▓▓
128 Perfect ▓▓▓▓▓▓▓▓▓▓▓▓▓
80 Gimme Acid ▓▓▓▓ ← -48 cliff (or 160 felt: +32)
120 Vague de CRIME ▓▓▓▓▓▓▓▓▓▓▓
160 Mafia ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ← +40 climb
144 Sunshine ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ← -16 (backlog expected +6)
127 Desire FINALE ▓▓▓▓▓▓▓▓▓▓▓▓▓
114 Revolution ▓▓▓▓▓▓▓▓▓▓▓
```
**Observations, offered as input not verdict** — set order is a musical call and this is only
the tempo dimension:
- **Three cliffs over 40 BPM**: →89 (Doing it Right), →80 (Gimme Acid), →160 (Mafia). The
89 and 80 ones are *deliberate-looking* — a slow track between fast ones is a valid
dramatic move, and both are marked light/odd in the backlog. They're only a problem if the
transition is unprepared, which is exactly what risers (#33) and default-xfade (#13) address.
- **The Mafia → Sunshine → Desire tail** is the part where measurement disagrees with the
plan. As written the set peaks at Mafia and then descends 160 → 144 → 127 → 114. That's a
coherent *comedown* finish, but the backlog's `[166]` for Sunshine suggests you intended to
keep climbing to the peak instead. Worth deciding deliberately rather than inheriting.
- **The 124 plateau** (Take Five → Piment → Perfect, 124/124/128) is the most transition-safe
stretch in the set — near-identical tempi mean crossfades will simply work. Good place to
put anything you're less sure of.
## What this needs from PLN
1. Confirm Gimme Acid's 80 is half-time notation (changes where it sits in the arc).
2. **Decide Sunshine: 144 or 166?** Fix the file or fix the backlog — right now they disagree
by 22 BPM and the shape of your finish depends on which is right.
3. Desire: align the file (127) and backlog (129).
4. Then the ordering call itself, which is ears, not data.
Related: `docs/2026-07-25-riser-audit.md` (#33) — the transitions across those three cliffs
are exactly where a riser earns its keep.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment