826bf9b6d8
The GUI-updater handoff race: the Desktop fires SIGTERM + app.quit() and spawns hermes-setup, but its Python backend (`python.exe -m hermes_cli.main serve`) can survive the teardown. The Desktop is gone -- nothing will respawn that backend -- yet the venv-holder guard refused on it and the update dead-ended with "Hermes is still running" while the user had zero windows open (observed twice on 2026-08-09, 01:59 and 02:17, bootstrap-installer.log). New `_orphaned_desktop_backend_pids()` classifies remaining holders: a serve/dashboard backend whose supervising parent is provably dead (PID gone, or recycled -- parent created after the child) is a straggler safe to reap. Any live-parent backend, non-backend holder, or unprovable case keeps the refusal exactly as before. Reaping uses the new `_stop_process_trees()` (taskkill /T /F), mirroring the Desktop's forceKillProcessTree and install.ps1's venv sweep so the managed .hermes-runtime interpreter child dies with its launcher (#70026). Builds on #81327 (salvaged intact underneath): that fixed the same parent-only-kill gap in install.ps1's venv sweep; this closes the remaining dead-end in the `hermes update` guard itself. E2E on a real Windows box: spawned a detached orphan with a backend-shaped argv -> classifier returned its PID and the tree reap killed it; a non-backend orphan and the live Desktop backend (parent alive) both returned None (refusal preserved).