Skip to content

fix(gateway): launch restart watcher windowless on Windows (empty python.exe console after GUI update) - #58152

Closed
iso2kx wants to merge 1 commit into
NousResearch:mainfrom
iso2kx:fix/windows-restart-watcher-windowless
Closed

fix(gateway): launch restart watcher windowless on Windows (empty python.exe console after GUI update)#58152
iso2kx wants to merge 1 commit into
NousResearch:mainfrom
iso2kx:fix/windows-restart-watcher-windowless

Conversation

@iso2kx

@iso2kx iso2kx commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

The post-update gateway respawn chain has two process legs: the watcher that polls the old gateway PID, and the gateway it respawns once that PID exits. #52239 fixed the gateway leg (routing its argv through windowless_gateway_restart_spec), but the watcher itself is still launched via bare sys.executable — the venv's console python.exe:

watcher_argv = [
    sys.executable,   # venv console python.exe
    "-c",
    watcher,
    ...
]

Under a uv-managed venv (the default Desktop install), that launcher re-execs the base console interpreter. The re-exec is a fresh CreateProcess that inherits none of the watcher's DETACHED_PROCESS | CREATE_NO_WINDOW flags, so a blank visible python.exe console sits on screen for the whole drain window (up to ~2 minutes) after every Desktop GUI update. This is the surviving leg of the bug #53850 diagnosed before the #53853 revert ("will fix from a real Windows repro").

Real Windows repro

Windows 11, uv-managed venv, Desktop GUI auto-update (v0.18.0):

  1. Launch the Hermes desktop app; the bootstrap updater runs hermes update --gateway.
  2. The updater log reaches draining gateway PID <n> (up to 195s)....
  3. An empty console window titled ...python.exe appears and stays until the old gateway exits and the watcher respawns it, then self-closes.

Process-tree evidence during the window: venv Scripts\python.exe (spawned detached, no window) with a child base ...\uv\python\cpython-3.11...\python.exe owning a fresh console — the re-exec leg the creationflags never reach.

Fix

_spawn_gateway_restart_watcher: route watcher_argv through the same windowless_gateway_restart_spec rewrite the gateway leg already uses, and thread the resulting cwd + VIRTUAL_ENV/PYTHONPATH overlay into the watcher Popen (both the breakaway and no-breakaway branches) so the base pythonw.exe can import hermes_cli / gateway.status without the venv launcher. Best-effort with fallback to the original argv — a visible watcher window beats a restart that never happens. No-op on POSIX.

Tests

  • test_watcher_process_itself_is_windowless — static check mirroring the existing test_watcher_rewrites_console_python_to_windowless pattern.
  • test_watcher_popen_carries_windowless_interpreter_and_overlay — behavioral: mocks the spec + Popen under a win32 platform patch and asserts the watcher argv leads with the windowless interpreter and carries the cwd/env overlay.

pytest tests/tools/test_windows_native_support.py — 66 passed; the 2 failures (TestConfigureWindowsStdio::test_no_op_on_posix, TestSigkillFallback::test_getattr_fallback_prefers_sigkill_when_present) are pre-existing on a native Windows runner (they simulate POSIX and pass on Linux CI, where this suite is designed to run).

Fixes #51661. Supersedes the watcher-leg half of #53850.

The post-update respawn chain has two process legs: the watcher that
polls the old gateway PID, and the gateway it respawns. NousResearch#52239 rewrote
the gateway leg through windowless_gateway_restart_spec, but the
watcher itself still launched via bare sys.executable — the venv's
console python.exe. Under a uv venv that launcher re-execs the base
console interpreter with default creationflags, a fresh CreateProcess
that inherits none of the watcher's DETACHED / CREATE_NO_WINDOW flags,
so a blank console window stays on screen for the whole drain window
(up to ~2 minutes) after every GUI update.

Route watcher_argv through the same windowless_gateway_restart_spec
rewrite and thread the cwd + VIRTUAL_ENV/PYTHONPATH overlay into the
watcher Popen so the base pythonw.exe can import hermes_cli and
gateway.status without the venv launcher.

Reproduced on Windows 11 (uv-managed venv, Desktop GUI auto-update):
before the fix an empty python.exe console appears when the updater
drains the old gateway and closes when the watcher exits; with the
rewrite the whole chain is windowless.

Same diagnosis as the reverted NousResearch#53850, adapted to the current
_spawn_gateway_restart_watcher structure. Fixes NousResearch#51661.
@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard platform/windows Native Windows-specific behavior or breakage P2 Medium — degraded but workaround exists sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Jul 4, 2026
@iso2kx
iso2kx force-pushed the fix/windows-restart-watcher-windowless branch from 05cc2e6 to 7a3a4de Compare July 4, 2026 08:45

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Windows GUI fix: launches restart watcher windowless to avoid empty python.exe console. 3 files, targeted fix. No issues.


Reviewed by Hermes Agent

@iso2kx
iso2kx force-pushed the fix/windows-restart-watcher-windowless branch from 7a3a4de to 9753400 Compare July 10, 2026 12:37
@teknium1

Copy link
Copy Markdown
Contributor

Thanks — the watcher-leg premise is confirmed on current main. hermes_cli/gateway.py:844-860 still builds the watcher with bare sys.executable and starts it without the windowless interpreter rewrite or its cwd/environment overlay. The existing helper at hermes_cli/gateway_windows.py:812-870 provides exactly that mechanism, so the production change is narrowly targeted.

Problems

  • The newly added test_watcher_process_itself_is_windowless reads hermes_cli/gateway.py and asserts source text. This conflicts with the repository's explicit ban on source-reading tests (AGENTS.md:1358-1411): it tests implementation shape rather than executing the watcher behavior.

Suggested changes

  • Remove that static test and retain the added behavioral Popen test, which calls _spawn_gateway_restart_watcher and verifies the rewritten executable plus cwd/environment overlay.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 15, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Closing after the class-level fix in PR #70205 (merged as 0dbf639), which supersedes this approach.

Your symptom diagnosis was on point — the uv venv launcher re-exec did leave a visible console during the drain window. But the root cause turned out to be one layer up: windows_detach_popen_kwargs() carried DETACHED_PROCESS, and per the Win32 spec CREATE_NO_WINDOW is ignored when combined with it — so the watcher ran console-less and its re-exec child had to allocate a visible console. #70205 removes DETACHED_PROCESS from the detach bundles: the watcher now owns a single hidden console that the uv re-exec child inherits, so nothing flashes without any pythonw rewrite (which would have re-created the per-descendant flash for the watcher's other children).

If you still see a flashing/persistent console on this leg on current main, please re-open with a repro — that would be new signal. Thanks for the careful writeup of the spawn chain; it helped confirm the inventory for the class fix.

@teknium1 teknium1 closed this Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists platform/windows Native Windows-specific behavior or breakage sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: get_python_path() returns python.exe instead of pythonw.exe, causing visible console window on gateway restart after update

4 participants