Repository navigation
Hide console windows for every background child the daemon spawns - #30
Merged
Merged
Conversation
The earlier fix (linking the desktop-bundled daemon as a Windows GUI-subsystem binary) stopped Electron's spawn of relay.exe itself from flashing a console. It did not stop relay.exe's OWN child spawns from doing the same thing one level down, and made the risk worse in one sense: now that the daemon has no console of its own to inherit, any console-subsystem child it starts without CREATE_NO_WINDOW gets a fresh one auto-allocated by the Windows loader. Nothing in this codebase set SysProcAttr.HideWindow anywhere. Go's exec.Command never hides a child's window on its own; that field has to be set explicitly per spawn, and every spawn here was missing it. This affected every background child the daemon starts on its own, tied to real session activity rather than app launch, which is why it showed up later and only once at a time: running the provider CLI to do the actual task, every git snapshot taken during a session or handoff, the detection scan's process-listing probe, and verification commands after a task completes. internal/process already had the right shape for this: SetupChildProcess (Windows) configures SysProcAttr for the agent CLI child claude.go and stub.go spawn, so HideWindow was added there directly. For the simpler one-shot commands elsewhere (git in worktree.go/durability.go, the detection scan's powershell probe, verify.go's cmd /C branch) that don't need SetupChildProcess's process-group/Job Object machinery, a lighter internal/process.HideWindow(cmd) does the same for just that one spawn. Unix builds get a no-op, since only Windows auto-allocates a console for a spawned console-subsystem process with no console to inherit. Deliberately NOT touched: cmd/relay/providers.go's openInTerminal, which opens the account sign-in terminal on purpose and must stay visible, and cmd/relay/tui.go's spawns, which already inherit the TUI's own console in normal interactive use. Every changed call site captures output through Go-managed pipes (StdoutPipe/CombinedOutput/ Output), never console passthrough, so hiding the window cannot break output capture in either the desktop app or CLI/TUI daemon mode. Full daemon suite green (143 tests, 22 packages), go vet clean.
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.
Summary
The earlier fix (linking the desktop-bundled daemon as a Windows GUI-subsystem binary) stopped Electron's spawn of relay.exe itself from flashing a console. It didn't stop relay.exe's OWN child spawns from doing the same thing one level down, and arguably made it more likely: now that the daemon has no console of its own to inherit, any console-subsystem child it starts without CREATE_NO_WINDOW gets a fresh one auto-allocated by the Windows loader.
Nothing in this codebase set
SysProcAttr.HideWindowanywhere. Go'sexec.Commandnever hides a child's window on its own. This affected every background child the daemon starts on its own, tied to real session activity rather than app launch, which explains the reported symptom exactly: one flash, at a longer interval, not a burst at startup.Affected spawns
claude.go,stub.go)worktree.go,durability.go)process_windows.go)verify.go)Fix
internal/processalready had the right shape:SetupChildProcess(Windows) configuresSysProcAttrfor the agent CLI child both adapters spawn, soHideWindowwas added there directly. For simpler one-shot commands that don't need the rest of that machinery, a lighterprocess.HideWindow(cmd)does the same for just that spawn. Unix gets a no-op.Deliberately not touched:
cmd/relay/providers.go'sopenInTerminal(opens the account sign-in terminal on purpose, must stay visible) andcmd/relay/tui.go's spawns (already inherit the TUI's own console in normal interactive use).Every changed call site captures output through Go-managed pipes (
StdoutPipe/CombinedOutput/Output), never console passthrough, so hiding the window cannot break output capture in either the desktop app or CLI/TUI daemon mode.Test plan
go vetclean