pingdotgg / pingdotgg/t3code

[Bug]: Frequent cmd.exe / conhost flashes on Windows from provider probe & VCS process kill paths

Open
#2,537 9 comments 3 reactions 0 assignees View on GitHub

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
  1. Install T3 Code v0.0.22 on Windows 11.
  2. Open a project with at least one provider (Claude/Cursor/Codex/OpenCode) configured, and a git repo.
  3. 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" on opencode serve (was direct in v0.0.21) → cmd.exe wrapper flash on every 5-minute refresh.
  • New terminateChild finalizer unconditionally fires SIGTERM via child.kill({ killSignal, forceKillAfter: "1s" }), sleeps 1s, then SIGKILL — both routed through killProcessGroup on 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.makeGitVcsDriver.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)
  1. apps/server/src/provider/opencodeRuntime.ts:337 — drop shell: process.platform === "win32" from the opencode serve spawn (v0.0.21 didn't have it).
  2. apps/server/src/provider/opencodeRuntime.ts:356-369 — guard terminateChild so it skips when the child has already exited, or replace killProcessGroup with a plain childProcess.kill(signal) on Windows when the process is still alive.
  3. apps/server/src/vcs/VcsProcess.ts:155 — remove the explicit Effect.addFinalizer(() => child.kill()). The spawner's internal acquireRelease already cleans up non-zero / still-running children.
  4. Broader cleanup: stop passing shell: process.platform === "win32" for binaries that aren't .cmd/.bat shims (most call sites — see processRunner.ts:155, tailscale.ts, ssh/command.ts, ssh/tunnel.ts, the Cursor/Claude/Codex provider probes), and consider upstreaming a windowsHide: true option into @effect/platform-node-shared's NodeChildProcessSpawner.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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.