Skip to content

v2.8.0 — Live macros stop stranding state; new control-surface e2e suite

Choose a tag to compare

@stackdump stackdump released this 27 Jul 04:40
· 32 commits to main since this release
0bc162f

Fixes the "live macros and beat pads stopped working" class of bugs, and adds a headless-Chrome suite that exercises every control in the studio so this can't regress silently again.

Root cause — not the Tone 15 upgrade

The suspicion was v2.7.0's Tone.js major bump. It wasn't: the engine, render, MIDI, and voice-recycle paths were audited against the pinned tone@15.1.22 bundle and are clean.

The real cause was cancel paths that cancelled in-flight macro animations without restoring their pre-fire state. Stop, Panic, and project-sync each carried their own divergent cleanup — Panic restored, the other two didn't. Interrupting a macro therefore left the studio in whatever half-swept state the animation was in:

  • Stop mid Sweep HP → master highpass stranded up, mix goes thin and near-silent.
  • Stop mid Tape Stop → tempo stuck at the crawl.
  • Stop mid Build Up / Wind Down → Feel puck stranded mid-sweep — and the puck persists to localStorage, so this one survived reload and left the studio permanently mis-tuned. This is why it read as "the app is broken" rather than "that macro glitched."

A second, independent bug compounded it: ticking any one-shot Repeat box routed a fire through the serial macro queue every beat (~500 ms at 120 BPM) against a ~700 ms queue budget, so the queue grew without bound and Auto-DJ's busy check reported "skipped — busy" for the rest of the session.

Fixes

  • Every animation token (FX sweep/hold, tempo, feel, channel pan/decay) carries its own restore closure. clearLocalMacroState is now the single canceller, and Panic / Stop / project-sync all delegate to it — one behavior, three call sites.
  • Stop halts Tone.Transport before cancelling macros, so restoreFeelState's reverb flush takes its synchronous branch instead of scheduling a restore that the stop then deletes (reverb-wet stranded at 0).
  • Tempo macros inherit a live prior animation's startBpm, the same guard fxSweep already had — a double-fired Half Time no longer makes 60 BPM permanent.
  • Repeating one-shots, paired one-shot FX, and stacked macros bypass the serial queue instead of claiming its slot; markMacroRunning's release timer gets a generation guard against double-dequeue.
  • Seamless Auto-DJ regen re-asserts the incoming project's tempo to the worker (a tempo macro straddling the boundary used to leave the new track playing at the old BPM).
  • Stale Fire-pad re-mute timers are cleared on cancel, so they can't re-mute a hit track the user deliberately unmuted on a newly loaded project.
  • Reverb flush publishes its in-flight target so a snapshot taken inside the 180 ms flush window (Auto-DJ stack of two Feel macros) captures the real wet value, not the transient 0.

New — make test-e2e

scripts/test-e2e-controls.mjs drives headless Chrome over raw CDP. No dependencies beyond node ≥ 20 and google-chrome — no Playwright, no Python. 150 checks, each asserting an observable effect rather than "didn't throw":

  • Transport, all seven panels, mixer, Feel, Auto-DJ firing, generate / shuffle / genre, share-payload CID sealing.
  • Every macro in the catalog, with per-kind effect assertions and restore assertions (tempo returns, mutes return, queue releases).
  • The four Fire pads and the engine one-shots (airhorn / laser / subdrop / booj) verified audible via an analyser tap on Tone.Destination, not just "fired."
  • FX sliders traced down to the actual engine AudioParam values.
  • Keyboard shortcuts driven as real key events.
  • MIDI input via a Web MIDI API mock — note-on through a fake device produces audio, so the MIDI path is covered without hardware.
  • Stop-mid-macro and Panic-mid-macro restore regressions — the specific bugs above now have permanent guards.

Any uncaught exception or console.error during the run fails the suite.

Verification

  • ✅ 150/150 e2e locally and against production after deploy.
  • ✅ scripts/test-macro-audio.py 5/5 (spectral-centroid and RMS analysis of real captured audio).
  • ✅ go test ./... green.

🤖 Generated with Claude Code