feat: restart_debugging — one-call relaunch with breakpoints re-applied + fix live-proxy relaunch destroying the session (fixes #238) - #269
Merged
Conversation
…landmine
- New restart_debugging {sessionId}: terminate the current debuggee (if
any) and replay the last real launch with the same configuration.
Breakpoints re-apply automatically via the initialBreakpoints snapshot;
data.breakpointsReapplied and data.outputReset (read get_output from
since=0) are added to the start_debugging-shaped response. Implemented
uniformly as terminate+relaunch (no adapter advertises native DAP
restart; the spec blesses the emulation), so every launch-mode language
works with no per-adapter wiring. Guards: attach sessions and
never-launched/dry-run-only sessions are refused with clear errors;
reentrancy and INITIALIZING are refused; ERROR state is allowed (crash
recovery). The server tool deliberately skips validateSession's
TERMINATED rejection — restarting after exit is the primary use case.
- LastLaunchSpec captured on ManagedSession at the top of every real
(non-dry-run) launch: effective scriptPath, args, dapLaunchArgs,
adapterLaunchConfig, raw breakOnExceptions (policy defaults re-resolve
on replay). A launch also clears the sticky attachMode flag so
attach-then-launch sessions can restart.
- FIX (latent bug, zero coverage): startDebugging/attachToProcess on a
session with a live proxy called closeSession — which removes the
session from the store — then updated state on the removed session,
destroying the session and reporting "Session not found". Both paths now
use a new session-preserving stopProxyPreservingSession helper
(listeners removed, worker stopped, adapter registry slot released).
- Per-launch reset now clears breakpoint verified/message/adapterId — a
new adapter instance has verified nothing, so relaunches report honest
verification.
- error-messages.ts timeout advice now points at the real
restart_debugging tool.
- Tests: new session-manager-restart unit suite (replay fidelity, landmine
regression, per-launch reset, all guards); server handler suite; the old
"should close existing session" assertion now pins the session-PRESERVING
behavior; e2e tests/e2e/mcp-server-smoke-restart.test.ts across all 8
languages (restart while paused + restart after exit, 8/8 pass locally);
attach-rejection asserted live in the python-attach e2e. Dogfooded via
the dev proxy: a finished session restarted in one call and paused at
the re-applied breakpoint.
- Docs: tool-reference section + TOC, README table, CLAUDE.md tool list
25 -> 26, usage.md, skill copies, CHANGELOG Added + Fixed.
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.
Closes #238. Completes the cluster:breakpoint-tooling trio (#267 → #268 → this).
The edit-rerun loop — fix a line, rerun to the same breakpoints, confirm the state changed — was 4+ calls with breakpoint state lost (close, create, re-set every breakpoint, start). It is now one call.
restart_debugging {sessionId}Terminates the current debuggee (if any) and replays the last real launch with the identical configuration — effective script path, args,
dapLaunchArgs,adapterLaunchConfig, rawbreakOnExceptions(policy defaults re-resolve on replay). Breakpoints re-apply automatically via the existinginitialBreakpointssnapshot (#267 keeps their verified state honest). The response mirrorsstart_debuggingplusdata.breakpointsReappliedanddata.outputReset: true— each launch starts a fresh output buffer, so readget_outputfromsince: 0.Design choices (per the planning session):
restart, and the DAP spec explicitly blesses the emulation — so every launch-mode language works with zero per-adapter wiring. Native restart stays a future optimization.validateSession's TERMINATED rejection (same precedent asget_output); a finished session restarts without being recreated.start_debugging, which itself now works correctly on a live session (below).attachModeflag, so an attach-then-launch session doesn't wrongly refuse restarts forever.🐛 Fixed: the session-destroying relaunch landmine
start_debugging(andattach_to_process) on a session with a live proxy calledcloseSession— which removes the session from the store — and then updated state on the removed session. Net effect: callingstart_debuggingon a paused session silently destroyed it and reported a bogus "Session not found". Zero test coverage (both existing "restart works" tests used dead proxies).Both paths now use a new session-preserving
stopProxyPreservingSessionhelper (listeners detached, worker stopped, adapter-registry slot released — repeated restarts can't exhaust the 10-instance cap), and the relaunch proceeds normally. The per-launch reset also clears breakpointverified/message/adapterId, since a new adapter instance has verified nothing.Tests
session-manager-restartunit suite: replay fidelity (same script/args/config + breakpoints in the snapshot), the landmine regression (restart-while-paused keeps the session), per-launch verified reset (observable via a launch that dies mid-start), and every guard incl. concurrency. The old coverage test that assertedcloseSessiongets called now pins the session-preserving behavior instead.{success: false}; unknown session/missing param errors.tests/e2e/mcp-server-smoke-restart.test.tsacross all 8 languages (toolchain-skip): pause at breakpoint → restart while paused (landmine path) → same breakpoint hits again → bounded-continue to completion → restart from TERMINATED → breakpoint hits again without re-issuingset_breakpoint→ freshget_output. 8/8 pass locally (rust/win32 uses the documented Rust/Windows: continue_execution re-stops at the same breakpoint (never advances without a step_over) #255 step_over workaround). Attach rejection asserted live in the python-attach e2e.breakpointsReapplied: 1, outputReset: true.Docs
tool-reference section (+ TOC), README table, CLAUDE.md tool list (25 → 26), usage.md, both skill copies (the root-cause recipe's "fix, then re-run" step now names the tool),
error-messages.ts's timeout advice points at the real tool, CHANGELOG Added + Fixed.🤖 Generated with Claude Code