fix(install): stop managed runtime child trees on Windows - #81327
Closed
helix4u wants to merge 1 commit into
Closed
Conversation
helix4u
marked this pull request as ready for review
August 7, 2026 21:20
6 tasks
Contributor
|
Salvaged into #82179 — your commit is rebased onto current main intact (authorship preserved), with the sibling fix stacked on top: the same orphaned-straggler problem your install.ps1 sweep fixes also dead-ends |
teknium1
added a commit
that referenced
this pull request
Aug 9, 2026
…e venv-holder guard 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).
Contributor
|
Merged via #82179 (your commit landed intact on main — thanks!) |
4 tasks
teknium1
added a commit
that referenced
this pull request
Aug 9, 2026
… update Pre-#67193 hermes-setup binaries (June 2026 and earlier, including the newest published build) resolve bootstrap-cache/install-<branch>.ps1 by "exists -> reuse forever": a branch-ref cache entry written at install time is never re-downloaded, so every GUI update/repair executes a months-stale install script. The binary has no self-update path, so no amount of `hermes update` fixes it. Live incident (2026-08-09, ryanc): install-main.ps1 cached June 4 lacked the #81327 venv process-tree sweep; the bootstrap venv stage died with "Cannot remove item venv\Scripts\python.exe: Access denied" on a straggler backend pair, twice, despite every relevant fix already being merged on main - the installer simply never ran that code. Fix: `_refresh_bootstrap_cache_scripts()` runs at the end of every update path (git, zip, already-up-to-date repair), overwriting mutable branch-ref cache entries with the freshly pulled scripts/install.ps1 / install.sh. The stale binary's unconditional reuse becomes a feature: it "reuses" a file the update keeps permanently current. Post-#67193 installers re-download on every run anyway, so this is a harmless pre-seed of identical bytes for them. Scope guards: 40-hex commit-SHA entries are immutable pins and are never touched; .ps1 gets the UTF-8 BOM to match the installer's cache format (#67193); best-effort - a failed refresh never fails the update. E2E on the incident machine: poisoned the real bootstrap-cache/install-main.ps1 with a stub, ran the real function - healed byte-exact to the checkout's script (BOM intact, #81327 tree-kill sweep present).
kotasiddharth
pushed a commit
to kotasiddharth/hermes-agent
that referenced
this pull request
Aug 9, 2026
…e venv-holder guard 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 (NousResearch#70026). Builds on NousResearch#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).
kotasiddharth
pushed a commit
to kotasiddharth/hermes-agent
that referenced
this pull request
Aug 9, 2026
… update Pre-NousResearch#67193 hermes-setup binaries (June 2026 and earlier, including the newest published build) resolve bootstrap-cache/install-<branch>.ps1 by "exists -> reuse forever": a branch-ref cache entry written at install time is never re-downloaded, so every GUI update/repair executes a months-stale install script. The binary has no self-update path, so no amount of `hermes update` fixes it. Live incident (2026-08-09, ryanc): install-main.ps1 cached June 4 lacked the NousResearch#81327 venv process-tree sweep; the bootstrap venv stage died with "Cannot remove item venv\Scripts\python.exe: Access denied" on a straggler backend pair, twice, despite every relevant fix already being merged on main - the installer simply never ran that code. Fix: `_refresh_bootstrap_cache_scripts()` runs at the end of every update path (git, zip, already-up-to-date repair), overwriting mutable branch-ref cache entries with the freshly pulled scripts/install.ps1 / install.sh. The stale binary's unconditional reuse becomes a feature: it "reuses" a file the update keeps permanently current. Post-NousResearch#67193 installers re-download on every run anyway, so this is a harmless pre-seed of identical bytes for them. Scope guards: 40-hex commit-SHA entries are immutable pins and are never touched; .ps1 gets the UTF-8 BOM to match the installer's cache format (NousResearch#67193); best-effort - a failed refresh never fails the update. E2E on the incident machine: poisoned the real bootstrap-cache/install-main.ps1 with a stub, ran the real function - healed byte-exact to the checkout's script (BOM intact, NousResearch#81327 tree-kill sweep present).
ma1138569845
pushed a commit
to ma1138569845/dechnicAuditor-agent
that referenced
this pull request
Aug 10, 2026
…e venv-holder guard 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 (NousResearch#70026). Builds on NousResearch#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).
ma1138569845
pushed a commit
to ma1138569845/dechnicAuditor-agent
that referenced
this pull request
Aug 10, 2026
… update Pre-NousResearch#67193 hermes-setup binaries (June 2026 and earlier, including the newest published build) resolve bootstrap-cache/install-<branch>.ps1 by "exists -> reuse forever": a branch-ref cache entry written at install time is never re-downloaded, so every GUI update/repair executes a months-stale install script. The binary has no self-update path, so no amount of `hermes update` fixes it. Live incident (2026-08-09, ryanc): install-main.ps1 cached June 4 lacked the NousResearch#81327 venv process-tree sweep; the bootstrap venv stage died with "Cannot remove item venv\Scripts\python.exe: Access denied" on a straggler backend pair, twice, despite every relevant fix already being merged on main - the installer simply never ran that code. Fix: `_refresh_bootstrap_cache_scripts()` runs at the end of every update path (git, zip, already-up-to-date repair), overwriting mutable branch-ref cache entries with the freshly pulled scripts/install.ps1 / install.sh. The stale binary's unconditional reuse becomes a feature: it "reuses" a file the update keeps permanently current. Post-NousResearch#67193 installers re-download on every run anyway, so this is a harmless pre-seed of identical bytes for them. Scope guards: 40-hex commit-SHA entries are immutable pins and are never touched; .ps1 gets the UTF-8 BOM to match the installer's cache format (NousResearch#67193); best-effort - a failed refresh never fails the update. E2E on the incident machine: poisoned the real bootstrap-cache/install-main.ps1 with a stub, ran the real function - healed byte-exact to the checkout's script (BOM intact, NousResearch#81327 tree-kill sweep present).
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
…e venv-holder guard 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 (NousResearch#70026). Builds on NousResearch#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).
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
… update Pre-NousResearch#67193 hermes-setup binaries (June 2026 and earlier, including the newest published build) resolve bootstrap-cache/install-<branch>.ps1 by "exists -> reuse forever": a branch-ref cache entry written at install time is never re-downloaded, so every GUI update/repair executes a months-stale install script. The binary has no self-update path, so no amount of `hermes update` fixes it. Live incident (2026-08-09, ryanc): install-main.ps1 cached June 4 lacked the NousResearch#81327 venv process-tree sweep; the bootstrap venv stage died with "Cannot remove item venv\Scripts\python.exe: Access denied" on a straggler backend pair, twice, despite every relevant fix already being merged on main - the installer simply never ran that code. Fix: `_refresh_bootstrap_cache_scripts()` runs at the end of every update path (git, zip, already-up-to-date repair), overwriting mutable branch-ref cache entries with the freshly pulled scripts/install.ps1 / install.sh. The stale binary's unconditional reuse becomes a feature: it "reuses" a file the update keeps permanently current. Post-NousResearch#67193 installers re-download on every run anyway, so this is a harmless pre-seed of identical bytes for them. Scope guards: 40-hex commit-SHA entries are immutable pins and are never touched; .ps1 gets the UTF-8 BOM to match the installer's cache format (NousResearch#67193); best-effort - a failed refresh never fails the update. E2E on the incident machine: poisoned the real bootstrap-cache/install-main.ps1 with a stub, ran the real function - healed byte-exact to the checkout's script (BOM intact, NousResearch#81327 tree-kill sweep present).
blut-agent
pushed a commit
to blut-agent/hermes-agent-fork
that referenced
this pull request
Aug 11, 2026
…e venv-holder guard 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 (NousResearch#70026). Builds on NousResearch#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).
blut-agent
pushed a commit
to blut-agent/hermes-agent-fork
that referenced
this pull request
Aug 11, 2026
… update Pre-NousResearch#67193 hermes-setup binaries (June 2026 and earlier, including the newest published build) resolve bootstrap-cache/install-<branch>.ps1 by "exists -> reuse forever": a branch-ref cache entry written at install time is never re-downloaded, so every GUI update/repair executes a months-stale install script. The binary has no self-update path, so no amount of `hermes update` fixes it. Live incident (2026-08-09, ryanc): install-main.ps1 cached June 4 lacked the NousResearch#81327 venv process-tree sweep; the bootstrap venv stage died with "Cannot remove item venv\Scripts\python.exe: Access denied" on a straggler backend pair, twice, despite every relevant fix already being merged on main - the installer simply never ran that code. Fix: `_refresh_bootstrap_cache_scripts()` runs at the end of every update path (git, zip, already-up-to-date repair), overwriting mutable branch-ref cache entries with the freshly pulled scripts/install.ps1 / install.sh. The stale binary's unconditional reuse becomes a feature: it "reuses" a file the update keeps permanently current. Post-NousResearch#67193 installers re-download on every run anyway, so this is a harmless pre-seed of identical bytes for them. Scope guards: 40-hex commit-SHA entries are immutable pins and are never touched; .ps1 gets the UTF-8 BOM to match the installer's cache format (NousResearch#67193); best-effort - a failed refresh never fails the update. E2E on the incident machine: poisoned the real bootstrap-cache/install-main.ps1 with a stub, ran the real function - healed byte-exact to the checkout's script (BOM intact, NousResearch#81327 tree-kill sweep present).
33hodl
pushed a commit
to 33hodl/hermes-agent
that referenced
this pull request
Aug 12, 2026
…e venv-holder guard 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 (NousResearch#70026). Builds on NousResearch#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).
33hodl
pushed a commit
to 33hodl/hermes-agent
that referenced
this pull request
Aug 12, 2026
… update Pre-NousResearch#67193 hermes-setup binaries (June 2026 and earlier, including the newest published build) resolve bootstrap-cache/install-<branch>.ps1 by "exists -> reuse forever": a branch-ref cache entry written at install time is never re-downloaded, so every GUI update/repair executes a months-stale install script. The binary has no self-update path, so no amount of `hermes update` fixes it. Live incident (2026-08-09, ryanc): install-main.ps1 cached June 4 lacked the NousResearch#81327 venv process-tree sweep; the bootstrap venv stage died with "Cannot remove item venv\Scripts\python.exe: Access denied" on a straggler backend pair, twice, despite every relevant fix already being merged on main - the installer simply never ran that code. Fix: `_refresh_bootstrap_cache_scripts()` runs at the end of every update path (git, zip, already-up-to-date repair), overwriting mutable branch-ref cache entries with the freshly pulled scripts/install.ps1 / install.sh. The stale binary's unconditional reuse becomes a feature: it "reuses" a file the update keeps permanently current. Post-NousResearch#67193 installers re-download on every run anyway, so this is a harmless pre-seed of identical bytes for them. Scope guards: 40-hex commit-SHA entries are immutable pins and are never touched; .ps1 gets the UTF-8 BOM to match the installer's cache format (NousResearch#67193); best-effort - a failed refresh never fails the update. E2E on the incident machine: poisoned the real bootstrap-cache/install-main.ps1 with a stub, ran the real function - healed byte-exact to the checkout's script (BOM intact, NousResearch#81327 tree-kill sweep present).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
The Windows venv sweep already limits its process selection to executables inside this Hermes install's venv. It then used
Stop-Processon only the selected parent, which can leave a managed Python child under.hermes-runtimealive and holding the install open.This keeps the existing path check and changes only the stop action: each verified venv process is now stopped with its full child tree. Unrelated Python processes remain outside the selection.
Related Issue
Related to #70026, specifically the current-main managed-runtime child repro in #70026 (comment).
Type of Change
Changes Made
scripts/install.ps1to runtaskkill /T /Ffor each process selected by the existing venv-prefix check.How to Test
scripts/run_tests.sh tests/test_install_ps1_venv_process_tree.py -q -j 4.install.ps1 -Stage venvpath against a temporary Hermes home..hermes-runtimechild exit while the unrelated process stays alive.Local checks completed:
scripts/install.ps1ASCII check: passed.python -m py_compile tests/test_install_ps1_venv_process_tree.py: passed.python scripts/check-windows-footguns.py scripts/install.ps1 tests/test_install_ps1_venv_process_tree.py: passed.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AScreenshots / Logs
N/A. The linked issue comment contains the current-main process tree and updater failure evidence.