Skip to content

Hide console windows for every background child the daemon spawns - #30

Merged
dbisina merged 1 commit into
mainfrom
fix/hide-all-background-child-consoles
Jul 30, 2026
Merged

dbisina merged 1 commit into
mainfrom
fix/hide-all-background-child-consoles

Conversation

@dbisina

@dbisina dbisina commented Jul 30, 2026

Copy link
Copy Markdown
Owner

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.HideWindow anywhere. Go's exec.Command never 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

  • Running the provider CLI to do the actual task (claude.go, stub.go)
  • Every git snapshot during a session or handoff (worktree.go, durability.go)
  • The detection scan's process-listing probe (process_windows.go)
  • Verification commands after a task completes (verify.go)

Fix

internal/process already had the right shape: SetupChildProcess (Windows) configures SysProcAttr for the agent CLI child both adapters spawn, so HideWindow was added there directly. For simpler one-shot commands that don't need the rest of that machinery, a lighter process.HideWindow(cmd) does the same for just that spawn. Unix gets a no-op.

Deliberately not touched: cmd/relay/providers.go's openInTerminal (opens the account sign-in terminal on purpose, must stay visible) and cmd/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

  • Full daemon suite green (143 tests, 22 packages), go vet clean
  • Manual: run an actual task through the desktop app, confirm no console flash when the provider CLI starts or a snapshot is taken

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.
@dbisina
dbisina merged commit 2f45b84 into main Jul 30, 2026
8 checks passed
@dbisina
dbisina deleted the fix/hide-all-background-child-consoles branch July 30, 2026 12:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant