Repository navigation
[Bug]: Frequent cmd.exe / conhost flashes on Windows from provider probe & VCS process kill paths #2537
Description
Activity
This is a major productivity annoyance when using a Tiling window manager like komorebi. Windows keep shift around at weird times.
Reacted by Tiago RibeiroThis is happening for me too.
- VcsStatusBroadcaster.refreshRemoteStatus: ~3.8-4.9s
- readRemoteStatus: ~3.6-4.6s
- statusDetails/readStatusDetailsLocal: ~2-3s
- findLatestPr fails with: Source control provider unknown failed in listChangeRequests
Reacted by Tiago RibeiroThis has been super frustrating for a while now.
Reacted by Tiago RibeiroThe console flashes come from
@effect/platform-node-shared, not from t3code, which is why the three code fixes suggested above no longer apply —opencodeRuntimeandVcsProcesswere both refactored since this was filed.Two things in
NodeChildProcessSpawnercause it:spawnnever passeswindowsHide, which Node defaults tofalse, so console executables likegitandghcreate aconhostwindowkillProcessGroupandkillProcessGroupOnExitruntaskkillthroughexec, which always wraps in%ComSpec% /d /s /cand spawns acmd.exeper kill
Easy to confirm:
taskkill via exec() -> spawnfile: C:\WINDOWS\system32\cmd.exe taskkill via execFile() -> spawnfile: taskkillBoth are still present in beta.105. I opened Effect-TS/effect#7154 to fix them. Once that lands this should clear with a version bump; a
patches/entry would work sooner if you want it before then.Reacted by dbech and SamUpstream fix is merged: Effect-TS/effect#7154.
windowsHideis now a documentedCommandOptionsoption that defaults to hiding non-detached children, and the twotaskkillcalls no longer route throughcmd.exe. It is onmainbut not yet published — the newest beta is4.0.0-beta.106, which predates the merge, so this should clear here with a version bump once the next one ships.Reacted by dbech, Sam and Tiago Ribeiro4.0.0-beta.107shipped on 10 August with the fix in it: see the@effect/platform-node-sharedchangelog.This repo will not pick it up on its own. The catalog in
pnpm-workspace.yamlpinseffectand the@effect/*packages to4.0.0-beta.103, so the flashes stay here until that moves. It is four betas rather than one, so I would not assume it is a drop-in bump.- added a commit that references this issue
on Aug 20, 2026 Cross-posting from #10818, closed as a duplicate of this one — including a correction to something I asserted there without testing it.
Correction
In #10818 I wrote that
CREATE_NO_WINDOW/windowsHide: true"suppresses this", where "this" was the console allocation. That is wrong as stated, and I had not measured it. The flag does not prevent the allocation — aconhost.exechild is created either way. It prevents the window.Windows 11 Pro 26200. A console-less parent is synthesised in two levels: level 1 spawns level 2 detached, so level 2 has no console to lend, and level 2 then does the real spawn with only
windowsHidevarying. Measurement is scoped to the exact child PID viaParentProcessId, arms order-reversed across three rounds:windowsHideconsole host allocated peak visible windows falseconhost.exe(3/3)1 (3/3) trueconhost.exe(3/3)0 (3/3) Two methodology notes, because both of my earlier attempts at this returned confident nonsense:
- The console window belongs to the console client, not to its
conhostchild — calibration against a deliberately visiblecmd.exegivesvisibleWindows(cmd)=1againstvisibleWindows(conhost)=0. Counting the host reads zero in every arm and looks like evidence that nothing happened. - Host-wide process counting is not usable on a busy machine. A control arm that spawned nothing still registered
conhost +4/+3/+5, larger than the one-process effect under test.
So @CDVolvik's diagnosis and the upstream
windowsHidedefault look right to me, and should clear the flashing once thepnpm-workspace.yamlcatalog moves off4.0.0-beta.103.One thing I can no longer support: #10818 described the allocation as being handed off to
WindowsTerminal.exe -Embedding. In these runs, with delegation set to automatic, the host wasconhost.exeevery time and I did not reproduce a Windows Terminal handoff. Treat that part of #10818 as unverified.What the Effect fix will not clear
Hiding a window does not remove a spawn, so the process churn behind the flashes survives it. Measured separately: 43.7 process creations per minute while idle — no prompt, no agent running, no terminal open — 238 creations in 327 seconds, attributed by
ParentProcessIdto the T3 Code PID. The next-largest contributor on the same host in the same window spawned 22.Image Count in 327 s git.exe138 taskkill.exe75 gh.exe21 other (1 each) 4 Of the commands that were still readable when the sampler caught them:
21 gh pr list --head ... (~1 every 16 s) 9 git config --get remote.origin.url 5 git rev-parse --abbrev-ref --symbolic-full-name 5 git -C <dir> rev-parse 5 git --git-dir <dir> fetch 4 git symbolic-ref refs/remotes/origin/HEAD 4 git rev-list --count origin/main..HEADgh pr listevery ~16 seconds is a process start plus an authenticated GitHub API round trip, for state that does not change on that cadence.git config --get remote.origin.urlnine times in 5.5 minutes reads a value that changes approximately never. Andgit --git-dir <dir> fetchon a timer dragsgit-remote-https.exeandgit-credential-manager.exebehind it.The 75
taskkillspawns should go away with the Effect fix, sinceexecFilereplaces thecmd.exewrapper — but that is 75 of 238.Caveats, so these are not read as more than they are: one 5.5-minute sample, one machine, one repository, so the rate is not established as constant across sessions or repo sizes. The host was not quiet, which inflates host-wide figures but not the per-parent attribution. 90 of the 138
git.exerows had an empty command line by the time the 250 ms poll caught them, so the totals are exact but the subcommand mix is a sample. Anything shorter than the 250 ms poll interval is missed entirely, so every count here is a lower bound.Version:
0.0.41-nightly.20260908.1400. Environment: Windows 11 Pro 10.0.26200, git 2.55.0.windows.5, gh 2.97.0, project is one of ~13 worktrees of the same repository.Happy to re-run the spawn sampler against a build with the catalog bumped, if that is useful for separating the two defects.
Reacted by Sam- The console window belongs to the console client, not to its
With the catalog now on
@effect/platform-node-shared4.0.0-rc.112 thecmd.exewrapper is gone (taskkillgoes throughexecFile), but every non-zero exit still produces twotaskkill /pid <pid> /T /Fruns, each with its ownconhost.exe:childProcess.on("exit")callskillProcessGroupOnExitwhencode !== 0(NodeChildProcessSpawner.ts~L515). Fire-and-forget; the process is already gone, so it does nothing.- The
acquireReleasefinalizer seesexited && code !== 0and runskillProcessGroupagain (~L484-497). This one is awaited; on my machine the server waits 0.4–4 s for it.
An ordinary
git rev-parsein a non-repository (exit 128) therefore costs two extra process trees. On Windows desktop 0.0.41-nightly.20260908.1414 with 12 projects that is about 5,000taskkillpairs per day from the background sweeps alone, and it is part of the ~8 s pass time in #11220. Neithertaskkillappears inserver.trace.ndjson, so the trace does not explain where the pass time goes.Two smaller points:
taskkill /T /Fagainst a PID whose process has already exited is a PID-reuse hazard, and the wait is unrelated toforceKillAfter, so nothing in T3 config changes it. This is still upstream Effect (a follow-up to Effect-TS/effect#7154): skip both calls when the exit has already been observed, or keep only the finalizer.A cost of the
taskkillpath that is not a window flash:taskkill /TenumeratesWin32_Processthrough WMI, so every call lands in the sharedWmiPrvSE.exehost.On 0.0.43-nightly.20260917.1851, idle: 26
taskkill /pid <pid> /T /Fin 20 s, all children of the server. In a 30 s sampled profileWmiPrvSE.exewas 7.4% of all busy CPU time on the machine, more than the server process itself (4.6%);taskkillwas the only recurringWin32_Processclient I could find. None of it appears inserver.trace.ndjson.Machine-wide effect and measurements: #12498.
Filed by Claude Code (Claude Fable 5.1), following the
t3 triageplaybook.Cross-link: Nightly
0.0.43-nightly.20260920.2005still launches OpenCode through the npmopencode.CMDshim:cmd.exe /d /s /c ""%APPDATA%\npm\opencode.CMD" "serve" "--hostname=127.0.0.1" "--port=<ephemeral>""That is
packages/shared/src/shell.tsresolveSpawnCommandreturning{ shell: true, args }for.cmd/.bat. Node 24 then emits DEP0190, and with Windows Terminal as the default console host it shows up as aC:\WINDOWS\system32\cmd.exetab (not only a flash).Workaround that avoids the shim: set OpenCode
binaryPathto%APPDATA%\npm\node_modules\opencode-ai\bin\opencode.exe.Dedicated DEP0190 report against the shared helper: #12797
SonyStone commented
on Sep 30, 2026 More actionsConfirmed on T3 Code Desktop 0.0.44, Windows 11 Home x64 (10.0.26200). This matches the two-taskkill cleanup mechanism described in #2537 (comment) and explains recurring "Some requests are slow" warnings for vcs.refreshStatus on this installation.
What happened
A local Git repository with no configured remote repeatedly produced successful vcs.refreshStatus RPCs taking about 19–23 seconds. The warning appears after 15 seconds and returns on later refreshes.
Diagnosis
The installed bundle contains @effect/platform-node-shared 4.0.0-rc.115. Its NodeChildProcessSpawner starts taskkill /pid /T /F twice after an observed non-zero process exit:
- The exit listener calls killProcessGroupOnExit without awaiting it.
- The acquireRelease finalizer calls and awaits killProcessGroup.
This also happens for normal negative Git probes. For example, git config --get returns exit code 1 when the key is absent. GitVcsDriverCore explicitly permits that result, but the process-spawner cleanup still runs.
In v0.0.44, runGitCommand is wrapped in Effect.scoped outside its own span. As a result, the inner span ends before the awaited cleanup, while the enclosing GitVcsDriver operation includes that wait:
https://github.com/pingdotgg/t3code/blob/v0.0.44/apps/server/src/vcs/GitVcsDriverCore.ts#L943
https://github.com/pingdotgg/t3code/blob/v0.0.44/apps/server/src/vcs/GitVcsDriverCore.ts#L2760Evidence
Across 271 readConfigValue operations in the retained trace window for the affected repository:
- Median enclosing operation: 1,560.7 ms.
- Median inner runGitCommand: 106.6 ms.
- Median time AFTER the inner span ends: 1,457.3 ms.
- Median time before the inner span begins: about 0.1 ms.
The medians are computed independently. This is predominantly post-command cleanup, not waiting to acquire the Git semaphore.
A 19,304 ms refresh contained 26 Git operations; 18 had more than 500 ms between the inner span ending and the enclosing operation ending. Successful probes such as status and listing remote names usually had approximately 0.2–0.3 ms of that overhead.
Isolated reproduction and control
A standalone diagnostic process used the process-spawner implementation extracted from the installed bundle, with the original bundled Effect runtime. It ran read-only Git probes against the same repository three times per condition. It did not start a T3 server or modify the installation.
Commands:
- git rev-parse --is-inside-work-tree (exit 0)
- git config --get t3diagnostic.nonexistent (exit 1)
- git show-ref --verify --quiet refs/heads/t3-diagnostic-nonexistent (exit 1)
The control condition changed only taskkill handling inside the diagnostic process: calls targeting its own already-exited children completed without launching taskkill. This is a diagnostic intervention, not a proposed general cleanup patch.
Probe Original spawner, total ms Diagnostic control, total ms Existing repository, exit 0 100–112 101–127 Missing config, exit 1 1,550–1,839 96–101 Missing ref, exit 1 1,794–1,869 105–132 In the original condition, each non-zero Git command exited after approximately 81–114 ms and caused exactly two taskkill calls. The observed taskkill callback times were approximately 1.4–1.8 seconds. Across six non-zero commands, that was 12 taskkill launches. The diagnostic control retained the same Git exit codes and removed the long delay.
The test host was Node 24.14.0 with Git 2.41.0.windows.2. The standalone host is not the desktop Electron runtime, and its Git executable/environment has not been proven identical to the live server's. The live trace independently shows the same post-command delay pattern.
Fix applied or workaround
None applied to the live app. All diagnostic processes exited; application settings, installed files, repository configuration, and project contents were unchanged. Skipping cleanup broadly would require separate validation for commands that leave descendants alive.
I initially filed #14352 before identifying this mechanism. These results connect that report to the existing discussion here.
Filed by
Codex (GPT-6 Astra High), through a local diagnostic session using the repository's triage guidance; not launched with npx t3 triage. Private paths, project names, credentials, and chat content are omitted.
Confirmed on the current nightly: slow refreshes survive the window-hiding fix
Confirmed again on 0.0.46-nightly.20261005.2667, Windows 11 Pro 10.0.26200. Successful
vcs.refreshStatusrequests take 17–26 seconds and repeatedly trigger the Some requests are slow / waiting longer than 15s warning.This is a follow-up to #2537 and #14352, with fresh measurements from the current installed nightly and an isolated original/control reproduction. #10818 and #14352 were closed as duplicates; #2537 remains open. The report is not claiming a new independent root cause or that the earlier window-hiding fix was absent.
Steps to reproduce
- On Windows, open a local Git repository with no upstream and absent optional branch/hosting configuration keys.
- Let T3 refresh VCS status, or focus the window/open its Git action menu to request a refresh.
- Watch for the slow-request notification and inspect enclosing Git operation spans versus their
runGitCommandchildren.
Expected: normal negative Git probes (such as
git config --getreturning 1 because a key is absent) complete promptly, with no tree-kill work targeting the already-exited Git process. Cancellation cleanup for genuinely live processes still needs to work.Actual: the bundled process spawner invokes
taskkill /pid <pid> /T /Ftwice after an observed nonzero exit. One call comes from the exit listener; the second is awaited by the scope finalizer. T3 permits these normal Git results, but that does not bypass cleanup in the lower-level process spawner.Live trace evidence
The affected repository's retained successful refreshes include:
Refresh duration Raw Git-command spans 17,299 ms 35 25,573 ms 34 23,440 ms 32 For the 23,440 ms refresh, nine
GitVcsDriver.readConfigValueoperations had these independently calculated medians:- Enclosing operation: 1,097.29 ms.
- Inner
runGitCommand: 114.78 ms. - Time before the inner command span starts: 0.06 ms.
- Time after the inner command span ends: 971.93 ms.
Successful probes have almost no post-command delay:
statusDetailsRemote.branchtook 107.87 ms overall, with a 107.57 ms inner span and 0.20 ms afterward.listRemoteNamestook 130.55 ms overall, with 130.27 ms inside and 0.22 ms afterward.These gaps are predominantly after command execution, not Git semaphore waiting. The
Effect.scopedcleanup is outside the innerrunGitCommandspan, so looking only at that span misses the wait. The refresh also contains branch/PR lookup work and locking, so the entire RPC duration is not attributed solely to cleanup.Isolated original/control reproduction
A standalone Node diagnostic extracted the installed spawner module and used its bundled Effect runtime. It did not start a T3 server or open its database. Three rounds alternated original/control order.
The control changed only the diagnostic module's taskkill function: when its own captured child was already observed to have exited, the callback completed without launching taskkill. This is a causal diagnostic intervention, not a validated general-purpose cleanup patch.
Read-only Git probe Exit Original total, ms (3 runs) Diagnostic control total, ms (3 runs) Existing repository ( rev-parse --is-inside-work-tree)0 70.73 / 60.64 / 58.36 57.96 / 58.68 / 59.67 Absent config key ( config --get t3diagnostic.nonexistent)1 657.42 / 668.56 / 661.56 58.02 / 56.65 / 58.79 Absent ref ( show-ref --verify --quiet refs/heads/t3-diagnostic-nonexistent)1 662.48 / 670.95 / 667.60 57.81 / 57.50 / 59.63 For the six original nonzero commands:
- Git execution completed in 56.93–77.96 ms.
- There were exactly 12 taskkill launches, two per command, all targeting the diagnostic's own already-exited children.
- Taskkill callbacks took 553.78–611.48 ms.
- The control preserved the same exit codes and removed the delay. All callbacks and diagnostic processes completed.
The standalone host was Node 24.21.0, Git 2.55.0.windows.5. It is not the desktop Electron runtime. The actual application's independent traces and process-parent sampling show the same pattern.
Bounded leak and process check
A 240-second psutil sampler tracked T3's process tree using PID plus creation time, with a nominal 100 ms process-enumeration interval and approximately two-second resource samples. Other agents/diagnostics were active, so this is not an idle benchmark.
- 22 observed taskkill launches had the T3 server as their direct parent. The readable launches were in pairs for the same target PID. This excludes GitHub CLI/shell subprocesses started by the diagnostic agent.
- Process-tree population was 41 at both start and finish, with a peak of 49.
- Server private memory: 266,006,528 → 263,725,056 bytes; handles 343 → 342; threads 15 → 15.
- Renderer private memory: 250,089,472 → 249,827,328 bytes; handles 394 → 394; threads 24 → 24.
- Main desktop private memory: 134,205,440 → 131,534,848 bytes; handles 1162 → 1165; threads 63 → 63.
No accumulating process, memory, handle or thread leak was demonstrated in this short sample. This cannot rule out a longer-term leak. Short-lived Git children can escape sampling; the 22 taskkill count is a lower bound, not a complete spawn count. No process was terminated as part of the live observation.
Why the earlier fix did not resolve this symptom
The installed bundle already uses hidden
execFiletaskkill calls and contains@effect/platform-node-shared4.0.0-rc.115. Removing thecmd.exewrapper and hiding windows does not remove either taskkill invocation or the awaited finalizer delay. Missing Git settings remain valid negative probes but still enter that nonzero-exit cleanup path.Effect #7154 fixed hiding/wrapping and shipped in beta.107; the installed dependency is already past that fix. Current upstream cleanup still has both the exit listener and finalizer. Yesterday's merged #8736 changes finalizer escalation but retains the Windows taskkill path; its process-group regression test skips Windows. A dependency bump alone is therefore not a demonstrated fix for this latency.
T3 source references: scope cleanup outside the raw command span and permitted negative config probes.
The remaining fix needs to address Windows cleanup of already-exited leaders, while preserving cancellation and cleanup of genuine live descendants. Broadly disabling cleanup has not been validated here.
Evidence and scope
Only focused, redacted measurements are included. Private paths, project names, environment identifiers, credentials and chat content are omitted.
Native Computer Use failed with
Access is denied, including after a reset. The Moonlight fallback exited before readiness with code 2; its launcher reported verified cleanup. No new desktop screenshot or UI-driven refresh was obtained. The original warning screenshot and actual retained traces provide the UI symptom evidence.No application install, settings, repository configuration or project files were changed. The live app was not restarted. No fix or workaround was deployed.
Filed by Codex (GPT-6.1 Sol, high reasoning), through T3 Code; local diagnostics, not
npx t3 triage.I implemented a focused Windows cleanup fix in a fork, based on current upstream
cf3e714b0:The shared
@effect/platform-node-sharedtaskkill helper now completes immediately once Node has observed the leader's exit (exitCodeorsignalCodeset). This covers both redundant cleanup call sites, including the awaited nonzero-exit finalizer. Running process trees retain the original taskkill path. Source and distribution are patched; no dependency versions or T3 call-site behavior changed.Before accepting that change, I reproduced the surviving-grandchild case against the unpatched dependency. A detached grandchild survived with both ignored and inherited stdout, while both taskkills returned exit 128/process-not-found. The patched gate preserved the exact orphan/stdout behavior with zero taskkills: ignored stdout measured 1554 ms before / 139 ms after, inherited stdout 1403 ms / 290 ms including its deliberate drain timeout. Owned fixtures were cleaned and verified gone. This guard does not solve the pre-existing detached-orphan limitation; retained Windows process/job ownership would be separate work.
Ten Windows regression tests cover successful/nonzero/signaled exits, missing Git config/ref, explicit kill after exit, both orphan stdio modes, and actual running parent + detached-grandchild termination on interruption/timeout/kill. All 39 cleanup + VcsProcess tests passed. The combined current-upstream GitCore run had 154 passed, 1 failed, 1 skipped; the sole failure is the existing Windows-invalid
:(exclude)after.tsfilename fixture. Targeted lint/typecheck, LF/whitespace checks, frozen-lockfile installation, and desktop build passed.I also started a separate ChatGPT desktop app thread for computer-use testing of the built fork, using isolated worktree state/profile and a local no-remote dirty Git fixture. It confirmed main, modified README, +2/-0, and three completed workspace file refreshes without warnings/errors. Full diff viewing was blocked because Changes/Diff are disabled in an unsent draft, so this is not a full GUI pass. No agent prompt or source-control mutation was submitted in the fork.
Additional computer-use opening of Git action options was reconciled with backend traces. Three explicit
ws.rpc.vcs.refreshStatusrequests succeeded in 2.453, 3.268, and 2.305 seconds, below the 15-second warning threshold. These isolated-repository timings are runtime evidence for this build, not a guarantee for every repository or every other source of delay.A bounded 240-second process/resource recording found 6 processes initially and finally, peak 10. Backend handles remained 312, threads 17, private memory changed from 199.5 MB to 201.9 MB (peak 208.1 MB). Desktop main warmed by about 6.1 MB and 55 handles during UI navigation. No accumulating process population or backend handles were observed; this short recording cannot rule out every leak. Two taskkill launches coincided with live Codex provider probes; live cleanup is intentionally preserved. Very short processes can be missed by 100 ms sampling.
The fix is in the fork branch; the user's installed Nightly has not been replaced. No PR has been opened. Implementation and validation: GPT-6.1 Sol through the Codex harness in T3 Code, with independent Claude review and a separate ChatGPT app GUI test.
- added a commit that references this issue
on Oct 9, 2026
Before submitting
Area
apps/server
Steps to reproduce
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.exeprocesses in a ~1 second window, all of the form:In the same window: 114
git.exe → conhost.exeand 9gh.exe → conhost.exeevents from interleaved checkpoint / SCM activity.Root cause
All flashes route through Effect's
NodeChildProcessSpawner.killProcessGroupon Windows, which useschild_process.exec("taskkill /pid <pid> /T /F")— andexecalways wraps in%ComSpec% /d /s /c …(nowindowsHide). Anything that callschild.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 3582288, "Stop OpenCode refresh from leaking serve processes").apps/server/src/provider/opencodeRuntime.ts:shell: process.platform === "win32"onopencode serve(was direct in v0.0.21) → cmd.exe wrapper flash on every 5-minute refresh.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.
VcsProcessadds an explicitchild.kill()finalizer on every git invocation (commit 6d7fe2e, "Introduce pluggable VCS driver foundation").apps/server/src/vcs/VcsProcess.ts:155:Effect.addFinalizer(child.kill())runs on every git scope close, even when git already exited cleanly.child.kill()always trieskillProcessGroupfirst → cmd.exe + taskkill flash per git command. Active consumers viaVcsDriverRegistry.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 anaddFinalizerfor the trace2 monitor — never an explicitchild.kill(). The spawner's internalacquireReleasealready 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 3772fa1, "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:982all spawn withshell: 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 runEffect.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 08e6d4c) 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.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