Skip to content

fix(#492): don't park ruby launches on an initialize response rdbg never sends - #507

Merged
debugmcpdev merged 1 commit into
mainfrom
fix/492-initialize-response-optional
Aug 27, 2026
Merged

fix(#492): don't park ruby launches on an initialize response rdbg never sends#507
debugmcpdev merged 1 commit into
mainfrom
fix/492-initialize-response-optional

Conversation

@debugmcpdev

Copy link
Copy Markdown
Collaborator

Summary

rdbg can process the initialize request — provably, since it emits the initialized event 34ms later — yet never write the response frame. The worker awaited that response unconditionally (dap-proxy-worker.ts initialize step), so the launch sat silent until the parent's 30s deadline killed the session blaming adapter startup, even though an immediate retry always worked (3 occurrences across the last 48-combo sweep; full evidence in #492).

The fix

  • New InitializationBehavior.initializeResponseOptional flag, declared only by RubyAdapterPolicy — launch mode only, attach keeps the strict await (ruby attach shares the policy, so the worker guard also excludes attach explicitly).
  • Worker: awaitInitializeResponse() races the response against the already-armed initialized event plus a 2s grace period (matching the existing Phase-1 wait convention). Event wins ⇒ warn naming exactly what happened (greppable, references Ruby/rdbg: 30s proxy-init timeout recurs after #470 — initialize response never arrives, with no framing error this time #492) and proceed with capabilities = undefined — a documented-legal value every consumer already guards. A late response still captures capabilities; its eventual timeout rejection is swallowed so it can't become an unhandled rejection. The grace timer is cleared when the response wins — no stray timers.
  • Removed the istanbul ignore on the launch-before-config branch — the new tests exercise it.

Why proceeding is safe

The DAP spec makes initialized strictly follow successful initialize processing, so the event is proof the request was served; a dropped response will never arrive (DAP has no resend). Nothing downstream hard-requires capabilities: the worker's config sequence derives from policy, and every parent consumer guards absence (supportsExceptionInfoRequest?., logpoint downgrade check, mirror ?? undefined).

Residual: an rdbg wedged for all responses (launch too) still hits the 30s deadline — that case is what #493's diagnostics (next PR) will name honestly.

Tests

  • Red-then-green regression: ruby launch + never-resolving initializeSession + fired initialized ⇒ launch/configurationDone proceed, warn logged, no capabilities status (timed out before the fix).
  • Late response after the event won ⇒ capabilities still captured.
  • Healthy ruby launch unchanged (response wins the race; no warn).
  • Gating: go (no flag) and ruby attach still block — pinned.
  • Live: against a response-dropping fake rdbg, start_debugging completes in ~4s (2s = grace) with the recovery warn in the proxy log, instead of failing at 30s with the misleading message.

Fixes #492

🤖 Generated with Claude Code

…ver sends

rdbg can process the initialize request — provably, since it emits the
'initialized' event 34ms later — yet never write the response frame (its
DAP send silently skips writing when the socket slot is momentarily
unset). The worker awaited that response unconditionally, so the launch
sat silent until the parent's 30s deadline killed the session with an
error blaming adapter startup, even though an immediate retry always
worked (3 occurrences across the last 48-combo sweep).

Launch initialization for policies declaring the new
initializeResponseOptional behavior (ruby only) now races the response
against the already-armed 'initialized' event plus a 2s grace period.
When the event wins, the launch proceeds with unknown capabilities — a
documented-legal value every consumer already guards — a warning names
exactly what happened, and a late response still captures capabilities
(its eventual timeout rejection is swallowed). Attach sessions and other
adapters keep the strict await.

Verified end to end against a response-dropping fake rdbg: start_debugging
now completes in ~4s (2s of it the grace period) instead of failing at 30s.

Fixes #492

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.29630% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/proxy/dap-proxy-worker.ts 96.29% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@debugmcpdev
debugmcpdev merged commit 309c210 into main Aug 27, 2026
10 checks passed
@debugmcpdev
debugmcpdev deleted the fix/492-initialize-response-optional branch August 27, 2026 04:35
debugmcpdev added a commit that referenced this pull request Aug 27, 2026
…#510)

Self-debugging RCA writeup of the #492 Ruby launch stall: the incident
log, why #470's decoder paths were ruled out, mcp-debugger stepping
through its own compiled MinimalDapClient (statement breakpoints in
dist/, evaluate_expression against pendingRequests, the stack of the
event-delivery moment), the fake-rdbg replay with its DAP trace, the
rdbg 1.11.0 source analysis (silent `if sock = @sock` drop guard,
unsynchronized cleanup_reader) with the seq discriminator for the next
live capture, and what #507/#509 changed. Honest about proven vs.
hypothesized.

Also: troubleshooting gains a section decoding the stage-aware 30s
init-timeout message (post-#493), the ruby guide's troubleshooting
table maps the new recovery warning to the case study, and the README
documentation index links it.

Co-authored-by: JF <john.franklin@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ruby/rdbg: 30s proxy-init timeout recurs after #470 — initialize response never arrives, with no framing error this time

2 participants