fix: use CREATE_NO_WINDOW so Windows hook miner spawns don't flash a console (#1783)#1848
fix: use CREATE_NO_WINDOW so Windows hook miner spawns don't flash a console (#1783)#1848eldar702 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request replaces the use of 'DETACHED_PROCESS' with 'CREATE_NO_WINDOW' on Windows in 'mempalace/hooks_cli.py' to prevent console flashing in descendant processes while avoiding hangs, and updates the corresponding tests. The reviewer points out that this same pattern is present in 'mempalace/daemon.py' and advises against applying a one-off fix to maintain repository-wide consistency, in accordance with the rule to address such common patterns across sibling implementations in a dedicated change.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if os.name == "nt": | ||
| flags = 0 | ||
| for name in ("DETACHED_PROCESS", "CREATE_NEW_PROCESS_GROUP", "CREATE_BREAKAWAY_FROM_JOB"): | ||
| for name in ("CREATE_NO_WINDOW", "CREATE_NEW_PROCESS_GROUP", "CREATE_BREAKAWAY_FROM_JOB"): |
There was a problem hiding this comment.
This change replaces DETACHED_PROCESS with CREATE_NO_WINDOW to prevent console flashing on Windows. However, the exact same pattern of using DETACHED_PROCESS is present in mempalace/daemon.py inside _detached_kwargs (line 978):
for name in ("DETACHED_PROCESS", "CREATE_NEW_PROCESS_GROUP", "CREATE_BREAKAWAY_FROM_JOB"):According to the repository's general rules, when addressing a common issue or pattern that is present in multiple sibling implementations across the codebase, we should avoid applying a one-off fix to a single instance. Instead, we should maintain repository-wide consistency by deferring the fix to a dedicated change that addresses all occurrences together.
References
- When addressing a common issue or pattern (such as unsafe SQLite URI path encoding) that is present in multiple sibling implementations across the codebase, avoid applying a one-off fix to a single instance. Instead, maintain repository-wide consistency by deferring the fix to a dedicated change that addresses all occurrences together.
There was a problem hiding this comment.
Thanks for flagging this. I looked at daemon.py's _detached_kwargs, and I think the two cases are genuinely different rather than one shared pattern:
hooks_cli.py._detached_popen_kwargsspawns a short-lived hook child and does not redirect stdout/stderr to a file. The bug (Stop hook hangs on Windows: missing process.exit(0) after stdout.write in mempalace-stop.js #1268 / Windows: hook-spawned miner flashes visible console windows - DETACHED_PROCESS should be CREATE_NO_WINDOW in _detached_popen_kwargs #1783) was thatDETACHED_PROCESSon Windows left the parent holding the inherited stdout/stderr handles → Stop-hook hangs.CREATE_NO_WINDOWremoves the console flash while still letting the hook exit.daemon.py._detached_kwargsspawns a long-running daemon that redirects stdout/stderr to a log file (so there is no inherited-handle hang), and it intentionally wants full detachment to survive the parent exiting —DETACHED_PROCESS(+start_new_session=Trueon POSIX) is the correct choice there. Swapping it toCREATE_NO_WINDOWwould actually regress the daemon: it would create a hidden console and drop the no-console/detached semantics a daemon needs.
So the differing flag is intentional and load-bearing, not a one-off instance of a common pattern. I'd rather keep this PR scoped to the hook-spawn path; happy to add a short comment on the daemon side documenting why it keeps DETACHED_PROCESS if that'd help future readers.
What
In
mempalace/hooks_cli.py,_detached_popen_kwargs()builds thecreationflagsfor the background miner process it spawns at hook time(session-start / stop). On Windows it previously OR'd in
DETACHED_PROCESS.This PR replaces that one flag with
CREATE_NO_WINDOW:The function's one-line docstring summary is updated from "fully detach a Popen
child" to "give a Popen child a hidden console" to keep it honest.
Why
DETACHED_PROCESSgives the child no console at all. When that child laterspawns a console grandchild (the miner shells out), the grandchild has no
console to inherit, so Windows allocates a fresh, visible console window —
the console flash users report in #1783.
CREATE_NO_WINDOWinstead gives the child a real but invisible console thatall descendants inherit, so nothing flashes.
These two flags are mutually exclusive on Win32: per the
CreateProcessdocs,CREATE_NO_WINDOWis ignored when it is OR'd withDETACHED_PROCESS.So the fix has to replace
DETACHED_PROCESS, not add to it — simply addingCREATE_NO_WINDOWalongside the existing flag would be a no-op.What stays intact
This change is scoped to the single
creationflagsflag. The rest of thehang-fix for #1268 is untouched:
stdin=subprocess.DEVNULLandclose_fds=True— unchanged.stdout=log_f, stderr=log_fexplicitly, so mineroutput is still captured to
~/.mempalace/hook_state/…regardless ofcreationflags— stdout/stderr are not dropped.CREATE_NEW_PROCESS_GROUP(signal boundary) andCREATE_BREAKAWAY_FROM_JOB— unchanged.
start_new_session=Trueand nevertouches
creationflags.The sibling loop in
daemon.pyis intentionally not touched — #1783 reportsthe hook-spawn console-flash path only, and the daemon miner is a separate path
out of this issue's scope.
Fixes #1783
Base branch
This PR targets
develop(the repo's default branch).AI-assisted disclosure
This change was prepared with the assistance of an AI coding agent (Claude). A
human reviewed the diff, ran the test suite locally, and verified the RED→GREEN
behavior and the Win32 flag rationale before opening this PR. The repository's
CONTRIBUTING.mdanticipates agentic coding tools (see the "Git identity forcontributions" note); the commit author email is set to a real GitHub-associated
address accordingly.
Test evidence (RED → GREEN)
The existing test
tests/test_hooks_cli.py::test_detached_popen_kwargs_windowswas updated first to assert the new contract (CREATE_NO_WINDOW set,
DETACHED_PROCESS not set), then the source was changed. It uses the existing
monkeypatch.setattrWindows-simulation pattern, so it runs on Linux/macOS CI.RED — updated test run before the source swap (source still yields
DETACHED_PROCESS, value520=0x208=DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP,missing
CREATE_NO_WINDOW=0x08000000=134217728):GREEN — same test after the one-line source swap:
Full file, after the fix:
Lint/format on the changed files: