[Bug]: Frequent cmd.exe / conhost flashes on Windows from provider probe & VCS process kill paths
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
- Install T3 Code v0.0.22 on Windows 11.
- Open a project with at least one provider (Claude/Cursor/Codex/OpenCode) configured, and a git repo.
- Leave the app running and watch the screen.
Expected behavior
No transient cmd.exe / conhost windows appear during normal idle / agent activity.
Actual behavior
Roughly every 5 minutes, a burst of console windows briefly flashes. With Process Monitor I captured 43 short-lived cmd.exe processes in a ~1 second window, all of the form:
C:\WINDOWS\system32\cmd.exe /d /s /c "taskkill /pid <N> /T /F"
In the same window: 114 git.exe → conhost.exe and 9 gh.exe → conhost.exe events from interleaved checkpoint / SCM activity.
Root cause
All flashes route through Effect's NodeChildProcessSpawner.killProcessGroup on Windows, which uses child_process.exec("taskkill /pid <pid> /T /F") — and exec always wraps in %ComSpec% /d /s /c … (no windowsHide). Anything that calls child.kill() on a handle from Effect's spawner produces the flash.
Three v0.0.22 changes added new code paths that hit this on a regular cadence; v0.0.21 didn't:
1. OpenCode probe was rewritten with shell:true + dual SIGTERM/SIGKILL kill (commit 35822884, "Stop OpenCode refresh from leaking serve processes").
apps/server/src/provider/opencodeRuntime.ts:
- Spawn now sets
shell: process.platform === "win32"onopencode serve(was direct in v0.0.21) → cmd.exe wrapper flash on every 5-minute refresh. - New
terminateChildfinalizer unconditionally fires SIGTERM viachild.kill({ killSignal, forceKillAfter: "1s" }), sleeps 1s, then SIGKILL — both routed throughkillProcessGroupon Windows, both flash. Total: 3 cmd.exe events per OpenCode probe, where v0.0.21 had 0.
2. VcsProcess adds an explicit child.kill() finalizer on every git invocation (commit 6d7fe2ee, "Introduce pluggable VCS driver foundation").
apps/server/src/vcs/VcsProcess.ts:155:
const child = yield* spawner.spawn(...);
yield* Effect.addFinalizer(() => child.kill().pipe(Effect.ignore));
Effect.addFinalizer(child.kill()) runs on every git scope close, even when git already exited cleanly. child.kill() always tries killProcessGroup first → cmd.exe + taskkill flash per git command. Active consumers via VcsDriverRegistry.make → GitVcsDriver.makeVcsDriverShape():
apps/server/src/checkpointing/Layers/CheckpointStore.ts:40-42— every checkpoint capture (5–10 git commands per agent turn).apps/server/src/sourceControl/SourceControlProviderRegistry.ts:159-171,apps/server/src/sourceControl/BitbucketApi.ts:435,apps/server/src/git/GitWorkflowService.ts(ensureGit,detectGitRepositoryForStatus).apps/server/src/vcs/VcsProvisioningService.ts:46.
The v0.0.21 equivalent (apps/server/src/git/Layers/GitCore.ts) only added an addFinalizer for the trace2 monitor — never an explicit child.kill(). The spawner's internal acquireRelease already no-ops on clean exit, so the new explicit finalizer is redundant on the success path and is what fires the flash on every git command.
3. SSH + Tailscale integrations shell-wrap on Windows (commit 3772fa12, "feat: Hosted Frontend, Tailscale Integration & SSH Lancher").
packages/tailscale/src/tailscale.ts:134,213, packages/ssh/src/command.ts:177, packages/ssh/src/tunnel.ts:982 all spawn with shell: process.platform === "win32" and rely on the same kill path on scope close.
Why the 5-minute burst
makeManagedServerProvider (apps/server/src/provider/makeManagedServerProvider.ts:133-138) has each provider run Effect.forever(sleep(5min) >> refreshSnapshot()).pipe(Effect.forkScoped). All four providers' loops start together at boot and fire roughly synchronously every 5 minutes. ProviderSessionReaper (apps/server/src/provider/Layers/ProviderSessionReaper.ts:12,115) sweeps idle sessions on the same 5-minute cadence, each reaped OpenCode session adding 2 more cmd.exe events. The Multi-Provider per-instance refactor (commit 08e6d4cf) means probe count scales with configured instance count.
Suggested fixes (all local; no Effect changes required)
apps/server/src/provider/opencodeRuntime.ts:337— dropshell: process.platform === "win32"from theopencode servespawn (v0.0.21 didn't have it).apps/server/src/provider/opencodeRuntime.ts:356-369— guardterminateChildso it skips when the child has already exited, or replacekillProcessGroupwith a plainchildProcess.kill(signal)on Windows when the process is still alive.apps/server/src/vcs/VcsProcess.ts:155— remove the explicitEffect.addFinalizer(() => child.kill()). The spawner's internalacquireReleasealready cleans up non-zero / still-running children.- Broader cleanup: stop passing
shell: process.platform === "win32"for binaries that aren't.cmd/.batshims (most call sites — seeprocessRunner.ts:155,tailscale.ts,ssh/command.ts,ssh/tunnel.ts, theCursor/Claude/Codexprovider probes), and consider upstreaming awindowsHide: trueoption into@effect/platform-node-shared'sNodeChildProcessSpawner.spawn.
Impact
Slows me down / annoying
Version or commit
0.0.22
Environment
T3 Code Desktop v0.0.22 on Windows 11 Enterprise 26200 (10.0.26200).
Logs or stack traces
Process Monitor capture (60s window): 43× cmd.exe /d /s /c "taskkill /pid <N> /T /F", 114× git.exe → conhost.exe, 9× gh.exe → conhost.exe.
Screenshots, recordings, or supporting files
No response
Workaround
No response
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with apps/server/src/provider/opencodeRuntime.ts and apps/server/src/vcs/VcsProcess.ts, then trace the listed provider, checkpointing, SSH, and Tailscale call sites. Reproduce the Windows 11 flashes while observing the documented taskkill, git, and gh processes. Done means normal provider, VCS, SSH, and Tailscale activity no longer opens transient console windows while process cleanup still works.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, typescript
- Domain
- backend, devtools, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100