---
log: 024
title: "An address is a lease"
date: 2026-08-14
task: "#41 #134 #155 #156 #159 — the evening the client numbers swapped"
tags: [midi, alsa, routing, pulsar, cockpit, debugging]
shareable: true
---

## Cap (what & why)

A morning spent fixing four MIDI taps that were all watching the wrong device.
An evening watching that fix become wrong again, in the time it takes to unplug
a USB cable and plug it back in.

The rig is a laptop, a Launch Control XL, an Arturia KeyStep and a Behringer
interface. In the morning ALSA had the LCXL as client 20 and the KeyStep as 24,
and three of the four tools tapping MIDI were bound to 24 — reading a keyboard
nobody was touching, while the surface went unwatched. I re-pointed them all to
`20:0` and moved on, pleased.

## Manœuvre (how)

That evening PLN replugged, and the kernel handed out the numbers in the other
order:

    client 20: 'Arturia KeyStep 32'    <- was the LCXL
    client 24: 'Launch Control XL'     <- was the KeyStep

Both of my fixes were now wrong. The one tap I had *not* fixed — the one I had
described in a task as broken — was now the only correct one.

So the tap got rewritten to resolve `"Launch Control XL"` **by name, on every
respawn**, and to stay up whether or not anyone is subscribed, and to keep the
last value of every control so a view that connects mid-set paints the real
board instead of a blank one. First run against the live rig, no configuration
at all:

    starting   port=None
    ok         port=24:0

## Prise (findings / artifacts)

- **`tools/bridge/surface.py`** — one supervised tap, name-resolved, always on.
  It also does not litter: three smoke runs of the file each orphaned an
  `aseqdump` to pid 1, which is precisely the mess PLN had complained about
  that morning. `try/finally` does not survive SIGKILL, OOM or `timeout`;
  `PR_SET_PDEATHSIG` moves the guarantee into the kernel.
- **`tools/master-out.py`** — Ardour's Master follows a preference list
  (UMC → headphones → speakers) instead of being re-patched by hand in
  qjackctl on every move. Idempotent by construction: it diffs wanted links
  against existing ones and applies only the delta, so "already correct" costs
  zero `pw-link` calls and cannot glitch a live set.
- **The scene keys never fired, and one of them muted an orbit.**
  `ctrl-alt-[`/`]` are Pulsar core's fold/unfold, bound on `atom-text-editor` —
  a more specific selector than the package's `atom-workspace`, so core wins
  whenever focus is in the editor, which while livecoding is always.
  `ctrl-alt-0` was `tidalcycles:toggle-mute-10`: "backdrop off" silently muted
  d10.
- **A hush on a dormant REPL threw a modal dialog over the editor.** `eval()`
  boots GHCi first; `hush()` does not, and `remote.js` asserts in a comment that
  hush is "safe to fire blind". After every Window Reload the first panic-hush
  was the one guaranteed to fail.

## Sel (the shareable learning)

**An address is a lease, not an identity.**

Every fix I shipped in the morning wrote down a port number. `20:0`. It looked
like a fact — it had been verified, it was in the task description, three tools
agreed on it. It was a lease with no expiry printed on it, and the kernel
renewed it to a different tenant the next time a cable moved.

The tell was available and I ignored it: the thing that had gone wrong in the
morning *was a stale port number*. I fixed four instances of the symptom and
none of the cause, then wrote it up as done. It took the numbers swapping —
the same bug, in reverse, within eight hours — to make the shape visible.

There is a smaller trap folded inside it, which cost twenty minutes on its own.
While the surface was unplugged, `aseqdump -l` and `aconnect -i` listed no
hardware inputs at all. I concluded the discovery command was broken and nearly
"fixed" a tool that was working perfectly. Meanwhile the old taps were still
running happily on connections made back when the address was valid — so "other
tools are still reading it" proves nothing about whether the port is right.
Absence of the device and absence of the listing are indistinguishable unless
you check whether the device is there.

The general form, for anything that binds to hardware: resolve by the name a
human would use, re-resolve every time you reconnect, and never persist the
number. The number is what the system told you once. The name is what you meant.

## Hameçon (hook)

*"I fixed it by writing down the address. The address was a lease, and the
kernel renewed it to somebody else."*
