uvicorn's reloader is unusable on Windows for this app: it restarts the
server child via CTRL_C_EVENT, which is never delivered to a plain spawn
child (no console process group of its own), so the child kept running
old code and the reloader blocked in process.join() after the first
reload — the scanner included. And when a child does restart, uvicorn
hands it parent-bound sockets that ProactorEventLoop cannot register
with IOCP (WinError 87 on accept), while the selector loop would break
asyncio subprocesses (ffmpeg/ffprobe showreel generation).
In dev mode on Windows, mediahive now runs a small supervisor instead:
it watches the package with watchfiles and respawns a fresh child
process (which binds its own sockets) on every change, and terminates
the child on Ctrl-C. POSIX keeps uvicorn's native reload.
Shutdown: RootScanner.stop() no longer does a final best-effort state
save — those to_thread writes ran on the asyncio default executor, whose
non-daemon threads blocked interpreter exit, so log lines appeared after
the shell prompt returned. All scanner state is already persisted after
each completed scan and when the showreel queue drains. Shutdown-time
log lines (scan cancelled, showreel worker stopping) move to debug.