111 of 122 top-level src/utils/ files move into 16 topic folders (audio,
cache, cover, share, server, playback, playlist, deviceSync, waveform,
mix, format, export, changelog, ui, perf, componentHelpers). True
singletons with no cluster stay at the utils/ root.
Pure file-move: a path-aware codemod rewrote 539 relative-import
specifiers across 275 files; no logic touched. The hot-path coverage
gate list (.github/frontend-hot-path-files.txt) is updated to the new
paths for the 11 gated utils files — a mechanical consequence of the
move, not a CI change. tsc is green.
The streaming-fallback seek recovery loop + its visual coverup move
into `src/store/seekFallbackState.ts`:
- 6 module mutables (retry timer + start + target; fallback trackId +
restartAt; visual target)
- 3 const gates (180 ms retry interval, 6 s retry budget, 1.6 s
visual guard)
- `scheduleSeekFallbackRetry(trackId, seconds)` — bounded retry loop
that hits `audio_seek` every 180 ms up to 6 s, re-scheduling on
recoverable errors and clearing the visual target on success or
hard failure
- `clearSeekFallbackRetry()` — cancel timer + reset state
- get/set accessors for the three caller-touched fields
(`SeekFallbackVisualTarget`, trackId, restartAt)
playerStore's ~30 direct-access sites become accessor calls. Inside
the progress handler the visual target is captured into a local once
per call so type narrowing + repeated reads stay clean.
17 focused tests pin the constants, get/set round-trips, the retry
loop's happy path (setSeekTarget + clear visual), recoverable retry
re-schedule, non-recoverable abort, track-change abort, retry-budget
exhaustion, and the three coalesce cases (different track id /
seconds delta > 0.25 s / seconds delta ≤ 0.25 s).
playerStore 2909 → 2869 LOC.