Measured on macOS build 0.5.2-153: a backend given SIGTERM directly
completes graceful shutdown in 5.25s and clear_sentinel() runs
correctly. But the desktop shell's quit path
(frontend/src-tauri/src/bootstrap.rs:388) grants only a 2s grace
before force_terminate()+kill(), and Windows
(frontend/src-tauri/src/tools.rs:522-528) grants no graceful phase at
all. clear_sentinel() used to be the LAST statement of lifespan
shutdown (backend/main.py:1213), behind ~50s of bounded waits and
model unload/free_vram()/gc.collect()/httpx close — so a deliberate,
clean quit routinely got SIGKILLed before reaching it, leaving
run_sentinel.json behind for the next launch to misreport as a crash.
Move the sentinel clear to the TOP of the shutdown block, immediately
after `yield`: once uvicorn has begun graceful shutdown the exit is
deliberate by definition, so the sentinel has already done its job.
One os.remove is comfortably inside any shutdown deadline, including
Windows' effectively-zero one. The later clear_sentinel() call is
removed (not duplicated) so a later failure in this function can't
mask the early result; the truthful "Shutdown: done."/degraded log at
the end now reads that earlier return value instead of re-clearing.
Adds a regression test that forces a later shutdown step
(model_loads_begin_shutdown) to raise, simulating the kill hitting
mid-teardown, and asserts the sentinel is already gone and
detect_unclean_shutdown() reports no crash. Confirmed fail-before /
pass-after against this change.
Fixes#1895
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>