# ☕ Morning, 2026-07-30

Supersedes yesterday's file. Branch `claude/preload-eager-banks`, nothing pushed.
Ordered by urgency, not effort.

> **Small thing that affects your planning:** yesterday's file said "OPAL Day-5",
> today you framed it "J-7". There is no OPAL 2026 entry in
> `Web/www/content/lives/2026/` yet, so I can't resolve it and I'm not going to
> guess a countdown. Worth pinning down — and worth adding to the site content,
> since that's the canonical place for gig metadata.

---

## ⚠️ 0. THIRTY SECONDS, DO THIS FIRST

**Ardour's SAVED session has 3 orbits at -inf dB:** `Tidal 06`, `Tidal 10`, `Tidal 12`.

Your LIVE faders are probably fine — you've been playing. That *is* the bug: the fix
isn't persisted, so it dies with the process and a relaunch at the venue restores the
broken mix.

I cross-referenced those three against the orbits each setlist track declares:

```
do_it_right       d6 d10 d12      perfect            d6 d10 d12
take_5_drops      d10 d12         gimme_acid         d10 d12
the_revolution    d10 d12         vague_de_crime     d6 d10
piment_bresilien  d10             you_my_sunshine    d6
desire            d6
```

**9 of your 13 tracks would lose at least one orbit on a relaunch.**

1. Ardour → check Tidal 06 / 10 / 12 faders, raise any that are down
2. **Ctrl+S** ← this is the entire fix
3. `python3 tools/check-mix.py` → expect no `-inf`

Not touched by me: the D-row faders are MIDI-learned to CC 77-84, so writing from
outside desyncs them from the physical desk; CC77 down is total silence; and mix levels
are your musical intent. Writing to a live session while you're out isn't my call. (#62)

---

## 🔴 1. BEFORE YOU OPEN ANY TRACK IN PULSAR — reload from disk

**I changed 213 `.tidal` files on disk.** Pulsar saves the **BUFFER**, not the file.
Anything you had open now has a stale buffer, and your next Ctrl-S in that tab
**silently reverts the migration in that file**.

`Window: Reload` before evaluating anything. (This is exactly why #95 exists.)

---

## 2. The gM refactor is DONE — corpus-wide, not just the set

Your map, now authored in `tools/lcxl_grid.py` with your words above it:

| mutes | F1 / gM1 | F2 / gM2 | F3 / gM3 |
|---|---|---|---|
| | **d1 alone** (the kick) | other percs (d2 d3 d8) | bass + melodics (d4–d12) |

| DJF | C1 / gF1 | C2 / gF2 | C3 / gF3 |
|---|---|---|---|
| | all percs | bass only | melodies |

**917 lines across 213 files.** The bulk was *not* the kick — it was **505 perc lines
vacating gM1** so the kick could have it. Plus **7 orbits in the set that had no mute at
all** got one (your "no mute is oversight").

### One thing I had wrong, and you should know

#105 was written as "align gM to gF" — mirror the mute index onto the filter index. I
measured 35 mismatches under that rule and was ready to ship it. **The rule was wrong.**
Your answer showed filters and mutes group *differently on purpose*. Under your real map
it's 55 edits in the set, and `d4: gF2+gM3` — which the mirror rule called broken in 11
tracks — is **correct**. Shipping the "alignment" would have moved 11 good lines and left
the kick sharing a button with the hats.

### How it was verified (you asked me to be careful)

- **Convergence** — migrator run twice; second pass proposes 0 changes
- **Structural proof** — all 917 changed lines diffed against HEAD: 908 provably
  mute-token-only, the other 9 individually listed and accounted for. Nothing else moved.
- **`silent-eval --seeded`: 13/13 ok** — whole setlist builds cold, before and after
- **222 tests pass** (was 213 passed / 1 failed when I started)
- **Full before/after `silent-eval` over all 213 files** — see the commit for the result

**THE HONEST LIMIT, and it's the important part:** every button CC seeds to 0 at boot and
`gMute<N>` is `midiOn "^7N" (mask "f*16")`, so all three mutes are equally inert until
pressed. **None of the above proves anything about behaviour under a press** — the only
thing this change actually alters. That needs your fingers, two minutes on any track:

> **F1 → only the kick drops. F2 → percs drop, kick keeps playing. F3 → bass + melody drop.**

If you hate it: `git revert 27c1581`, one commit, all 917 lines.

---

## 3. 🎧 you_my_sunshine — your "FIXME VOICE OFF" is fixed, confirm by ear

d5 (the voice) and d11 (`no_sunshine:4/4` chopped) were **both on `# cut 5`**. Cut groups
are monophonic, so every d11 event killed the vocal mid-word. That was also the root cause
of your `-- FIXME VOICE OFF` on d5 — **one bug wearing two labels.**

d11 → `# cut 11` (group N = orbit N, as d3/d6/d8/d9 already do). Found by pvlint PV004. If
the vocal was *meant* to be monophonic across both layers, put it back to 5 — the comment
says so, and it's the only line that changed. (#70)

---

## 4. 🤔 gimme_acid `/!\ FIXME EXPLOSIF /!\` — diagnosed, NOT fixed, your call

```haskell
# width (range 0.2 0.8 "^33")
```

`^33` is an A/B knob → BootTidal's #55 seed gives it **0** → this sits at **width 0.2 at
boot**. Your own preset comment two lines above says nominal is **width = 0.51**. So the
synth starts at an extreme and the knob swings it *through* nominal rather than away.

Same shape BootTidal already documents for piment_bresilien's `legato (range 0.05 2 "^52")`,
whose stated remedy is *fix the range in the track*.

Candidate: `range 0.51 0.8 "^33"` → boots at nominal, knob only adds. Costs the 0.2–0.51
downward half. **That's a sound decision, so I wrote the analysis into the file and left
the sound alone.**

Also removed a duplicated `# filterRange (range 0 8 "^53")` right below it — same value
twice, harmless, clearly a paste.

---

## 5. 📋 The non-set missing-mute report

`docs/2026-07-30-orbits-without-a-mute.md` — you said flag, don't fix, outside the set.
2397 blocks, split into the cut that makes it actionable:

| | files | blocks | |
|---|---|---|---|
| **A · partial** | 110 | 321 | uses family mutes but missed some — **the oversight you meant** |
| **B · never** | 354 | 2076 | no orbit has one; predates the convention — a style era, not a mistake |

Group A is the real list. Fix any one file with
`python3 tools/fix-mute-roles.py --fill --apply <path>`.

---

## 6. New guardrails, so none of this re-drifts

- **PV012** — flags an orbit whose mute doesn't match its **role**. Deliberately *not*
  "gM index ≠ gF index", with a regression test asserting `d2 $ gF1 $ gM2` is clean.
- **PV013** — flags an orbit with **no** family mute.
- `fix-mute-roles.py --check` exits 1 on drift — worth wiring into the pre-gig gate (#44).

**PV013 caught a bug in the migrator that wrote it**, minutes after existing: the migrator
had its own orbit regex requiring `^dN $` and walked straight past
`d5  -- The Voice of Love` (no `$`, gates on a commented line). It now uses pvlint's parser
and owns none of its own. That miss had hidden 14 rewrites and 7 missing mutes.

---

## 7. Waiting on you — carried over, not urgent today

- **#12 the BPM arc** — `124, 133, 89, 124, 124, 128, 160, 120, 160, 166, 129` has two big
  drops (160→120→160). Your taste call, and it blocks #13 and #14.
- **#31 desire's bass** — four alternatives in `study/desire_bass_rhythm.tidal`, measured
  to genuinely differ (3.0 / 4.0 / 4.5 / 5.75 onsets per bar). A/B them.
- **#86** — the 2-minute remap rollback drill, next time Ardour is closed.
- **take-94 mix v2** — `mix/take94/take94_trimmed_v2.wav`, with the clamp+fade you asked
  for. 18.75 min, peak -3.36 dBFS, all 5 silence gaps handled including your +18:00 one.
- **#110 USB safety take** — you said the fallback is the point but the reference take is
  valuable too, so: both from one run. The mastered one is blocked on **#91** (record bus
  ~16 dB hot, 8 of 12 orbits over 0 dBFS — a fallback that clips isn't a fallback).

## 8. Found but couldn't act on

- **#112 — 14 corpus tracks don't compile at all.** Pre-existing, **none in the setlist**,
  found while baselining the migration. Same class as desire's `plz` typo.
- **#56 xruns** — needs a gig-log session to analyse and I found none on disk. Your next
  full run-through produces one.
