fix(js-debug): tag vsDebugServer argv and reap stranded instances (#431) - #432
Merged
Conversation
js-debug DAP servers are spawned detached+unref'd by the proxy worker, so a hard-killed worker (win32 TerminateProcess) stranded them outside every tree-kill path; they accumulated as orphans holding file locks on the vendor tree. Two-prong fix: - Tag the vsDebugServer argv with the same inert --mcp-owner-pid / --mcp-session-id markers the proxy worker carries (vsDebugServer reads only argv[2]/argv[3]; trailing tokens are ignored), and teach the startup janitor a third matcher over its existing process scan that reaps marked instances whose owner is dead - taskkill /T /F also sweeps the debuggee/watchdog children while the vsDebugServer parent is still alive. Marker constants move to @debugmcp/shared so the adapter package can import them. - On win32, ProxyProcessAdapter.kill() now tree-kills the worker first (taskkill /PID <pid> /T /F) while it is still alive, so ProxyManager's force-kill escalations no longer strand the detached adapter subtree. Guarded by the adapter's tracked exit state to avoid PID-reuse kills. Closes #431 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
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.
Problem
Fixes #431. js-debug DAP servers (
vsDebugServer.cjs) are spawneddetached:true+unref()by the proxy worker, and their PID never leaves the worker's memory. When the worker dies hard (win32TerminateProcess— force-kill escalations, interrupted test runs), nothing can reach them: the startup orphan reaper's matcher requires theproxy-bootstrapmarker, and win32 has no process-group kill. On the reporting dev box 14 of them had accumulated, holding file locks intovendor/js-debug/that brokepnpm run clean:vendorwithEPERM.Fix (two prongs)
Cleanup — make stranded instances findable and reapable:
JavascriptDebugAdapter.buildAdapterCommandnow appends--mcp-owner-pid=<pid>/--mcp-session-id=<id>after the host argument.vsDebugServer.cjsreads onlyargv[2](port) andargv[3](host) and ignores trailing tokens, so the markers are inert at runtime (same trick as the [FEATURE] Startup reaper for orphaned proxy chains (owner-PID tag), modeled on the JVM orphan reaper #343 proxy-worker tagging). Tokens are whitespace-free (the win32 scan splitsCommandLineon whitespace) and never contain--help(which would make vsDebugServer print usage instead of serving).parseJsDebugAdapterArgs): two-factor — avsDebugServertoken AND a valid owner marker — so VS Code's own js-debug instances can never match. Reaping reuses the proxy reaper's kill paths; on win32 that'staskkill /PID <pid> /T /F, which also sweeps the debuggee/watchdog children js-debug spawned beneath the stranded server.@debugmcp/shared(process-markers.ts) so the adapter package can import them; re-exported fromproxy-orphan-reaper.tsso existing importers are untouched.Prevention — stop creating the orphans in the first place:
ProxyProcessAdapter.kill()now firestaskkill /PID <workerPid> /T /Fbefore the plain kill, while the worker is still alive (taskkill/Tcan only discover children through a live parent). This covers all three ProxyManager hard-kill sites with one seam. Guarded by the adapter's tracked exit state so an already-exited (possibly recycled) PID is never tree-killed. On win32 everychildProcess.kill()signal was already a hardTerminateProcess, so tree semantics is strictly what was intended; POSIX behavior is unchanged.Notes / accepted scope
killProcesscommand path and the IPC-test path — both mean "kill the worker", so this is intended./Tkilling the vsDebugServer before the adapter reaper gets to it) is benign:killWindowstreats taskkill exit 128/1 as already-gone.SIGTERM→SIGKILL); a process-group sweep for its children is a possible follow-up, but the observed issue is win32-scoped.Verification
parseJsDebugAdapterArgsmatcher (5), janitor third-matcher wiring (3 extended), adapter argv markers (3), win32 tree-kill inkill()(5).vsDebugServerprocesses remained.🤖 Generated with Claude Code