You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fixes the Windows hermes update gateway resume path so a cleanly-stopped gateway does not make update wait for the full drain timeout, and so captured gateway launcher commands are not replayed as terminal/console wrappers.
Root cause found in live testing on Windows: the update pause helper wrote the planned-stop marker and the gateway exited cleanly, clearing gateway.pid and gateway.lock, but the old pythonw.exe process object could linger. The update helper waited only on raw PID existence, so with agent.restart_drain_timeout: 180 it looked hung even though the gateway had already stopped. Separately, unmapped Windows gateway resume replayed captured argv; if that argv was a launcher such as hermes.exe gateway run, the watcher could preserve a wrapper-shaped launch instead of normalizing to direct windowless Python.
This PR makes mapped profile waits treat a cleared/replaced profile pid file as stopped, and normalizes captured non-Python gateway launcher argv to the same direct pythonw.exe -m hermes_cli.main ... gateway run shape used by clean gateway starts.
Related Issue
N/A. Related prior PR discovered during duplicate search: #49025 covers an older pythonw-only approach in hermes_cli/gateway.py; this PR targets the current update pause/resume path on main and the mapped-profile wait hang reproduced locally.
Type of Change
🐛 Bug fix (non-breaking change that fixes an issue)
✨ New feature (non-breaking change that adds functionality)
🔒 Security fix
📝 Documentation update
✅ Tests (adding or improving test coverage)
♻️ Refactor (no behavior change)
🎯 New skill (bundled or hub)
Changes Made
hermes_cli/main.py: pass mapped profile pid-file paths into the Windows update gateway wait helper, and treat the old gateway as stopped once its profile pid file no longer resolves to the old PID.
hermes_cli/gateway_windows.py: normalize captured non-Python gateway launcher argv, such as hermes.exe --profile work gateway run, to direct windowless pythonw.exe -m hermes_cli.main --profile work gateway run before the restart watcher respawns it.
tests/hermes_cli/test_update_concurrent_quarantine.py: add coverage for the mapped Windows gateway case where the pid file is cleared while the old process still exists.
tests/tools/test_windows_native_support.py: add coverage that captured hermes.exe ... gateway run is not replayed and is not treated as a Python interpreter.
How to Test
Run the focused update pause/resume tests: python scripts/run_tests_parallel.py -j 4 tests/hermes_cli/test_update_concurrent_quarantine.py -- -k "windows_update_gateway_exit or pause_windows_gateways_for_update_stops_profile"
Run the focused Windows restart-spec tests: python scripts/run_tests_parallel.py -j 4 tests/tools/test_windows_native_support.py -- -k TestWindowlessGatewayRestartSpec
On Windows, with a running gateway and agent.restart_drain_timeout: 180, run the update-style pause/resume helpers. Verified locally that the helper returned in 9.23s instead of timing out, restarted the gateway, and the final gateway process was pythonw.exe -m hermes_cli.main gateway run --replace with pid/lock/runtime status passing.
The local test runner printed a post-summary cp1252 progress callback traceback when rendering a checkmark, but both focused test invocations exited 0 and reported 100% pass.
Thanks for the detailed write-up and the live-testing work, @helix4u. We're going to pass on this one.
The drain-timeout half (treating a cleared/replaced profile pid file as stopped in _pause_windows_gateways_for_update) is sound and reachable. But the wrapper-normalization half is wired to dead code: the captured-argv normalization lives inside windowless_gateway_restart_spec, and nothing in the production resume path actually calls it. _resume_windows_gateways_after_update -> launch_detached_gateway_restart_by_cmdline -> _spawn_gateway_restart_watcher replays run_argv verbatim and never invokes the spec. There's already a standing-red test on main (test_watcher_rewrites_console_python_to_windowless) asserting gateway.pyshould call that helper — it doesn't. So the wrapper fix improves a helper that never runs, and the actual replay bug it's meant to address stays open.
Closing rather than merging a partial fix. If you want to take another pass, the real fix is wiring windowless_gateway_restart_spec into _spawn_gateway_restart_watcher so the normalization is on the live respawn path (which would also turn that standing-red test green). Appreciate the contribution.
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
comp/cliCLI entry point, hermes_cli/, setup wizardP2Medium — degraded but workaround existsplatform/windowsNative Windows-specific behavior or breakagesweeper:risk-platform-windowsSweeper risk: may break or behave differently on native Windowstype/bugSomething isn't working
3 participants
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?
Fixes the Windows
hermes updategateway resume path so a cleanly-stopped gateway does not make update wait for the full drain timeout, and so captured gateway launcher commands are not replayed as terminal/console wrappers.Root cause found in live testing on Windows: the update pause helper wrote the planned-stop marker and the gateway exited cleanly, clearing
gateway.pidandgateway.lock, but the oldpythonw.exeprocess object could linger. The update helper waited only on raw PID existence, so withagent.restart_drain_timeout: 180it looked hung even though the gateway had already stopped. Separately, unmapped Windows gateway resume replayed captured argv; if that argv was a launcher such ashermes.exe gateway run, the watcher could preserve a wrapper-shaped launch instead of normalizing to direct windowless Python.This PR makes mapped profile waits treat a cleared/replaced profile pid file as stopped, and normalizes captured non-Python gateway launcher argv to the same direct
pythonw.exe -m hermes_cli.main ... gateway runshape used by clean gateway starts.Related Issue
N/A. Related prior PR discovered during duplicate search: #49025 covers an older pythonw-only approach in
hermes_cli/gateway.py; this PR targets the current update pause/resume path on main and the mapped-profile wait hang reproduced locally.Type of Change
Changes Made
hermes_cli/main.py: pass mapped profile pid-file paths into the Windows update gateway wait helper, and treat the old gateway as stopped once its profile pid file no longer resolves to the old PID.hermes_cli/gateway_windows.py: normalize captured non-Python gateway launcher argv, such ashermes.exe --profile work gateway run, to direct windowlesspythonw.exe -m hermes_cli.main --profile work gateway runbefore the restart watcher respawns it.tests/hermes_cli/test_update_concurrent_quarantine.py: add coverage for the mapped Windows gateway case where the pid file is cleared while the old process still exists.tests/tools/test_windows_native_support.py: add coverage that capturedhermes.exe ... gateway runis not replayed and is not treated as a Python interpreter.How to Test
python scripts/run_tests_parallel.py -j 4 tests/hermes_cli/test_update_concurrent_quarantine.py -- -k "windows_update_gateway_exit or pause_windows_gateways_for_update_stops_profile"python scripts/run_tests_parallel.py -j 4 tests/tools/test_windows_native_support.py -- -k TestWindowlessGatewayRestartSpecagent.restart_drain_timeout: 180, run the update-style pause/resume helpers. Verified locally that the helper returned in 9.23s instead of timing out, restarted the gateway, and the final gateway process waspythonw.exe -m hermes_cli.main gateway run --replacewith pid/lock/runtime status passing.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
Focused checks passed:
python -m py_compile hermes_cli\main.py hermes_cli\gateway_windows.pypython scripts\run_tests_parallel.py -j 4 tests/hermes_cli/test_update_concurrent_quarantine.py -- -k "windows_update_gateway_exit or pause_windows_gateways_for_update_stops_profile"— 2 passedpython scripts\run_tests_parallel.py -j 4 tests/tools/test_windows_native_support.py -- -k TestWindowlessGatewayRestartSpec— 4 passedgit diff --check -- hermes_cli/main.py hermes_cli/gateway_windows.py tests/hermes_cli/test_update_concurrent_quarantine.py tests/tools/test_windows_native_support.pyThe local test runner printed a post-summary cp1252 progress callback traceback when rendering a checkmark, but both focused test invocations exited 0 and reported 100% pass.