From 8e341147f4d28979a35f79d04dd53fd68c49f50b Mon Sep 17 00:00:00 2001 From: Adrian Florescu Date: Sat, 4 Jul 2026 09:03:06 +0300 Subject: [PATCH 1/3] R4: stop the zombie stream on stall/error; ensure() no longer cancels a pending sound start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From the post-review refactor plan (plan.md, R4). Two small behavior fixes, each with dedicated unit tests; old e2e untouched and green (37/37). - radioMachine: STALLED and PLAYER_ERROR from 'playing' now run stopPlayer on the way to 'retrying', symmetric with the LOADING_TIMEOUT path. The stalled stream stayed attached before, and a refilled buffer could resume audibly UNDER the loading tone during the 3s RETRY_DELAY — overlapping sounds, which the machine must never allow. (paused's PLAYER_ERROR keeps the old behavior: the element is already silent there, no overlap risk.) - soundEffects: minimal fix for the ensure() vs pending-preload race. On a slow first load, a supervisor tick landing while play() still waited for the blob would call element.play() on the empty src, the NotSupportedError flipped isPlaying to false, and the pending start bailed — one extra ~2.5s of silence exactly when the feedback sound mattered. ensure() now leaves the element alone while nothing was started yet (no src attribute = startPlayback hasn't run). The full reconcile() redesign stays in 4b. - soundEffects.test.ts: first unit tests for this module (fake