fix(#529): latch child 'initialized' events so a same-chunk arrival can't stall adoption - #531
Merged
Merged
Conversation
…an't stall adoption The post-attach 'initialized' event can share a socket chunk with the attach response. MinimalDapClient dispatches events synchronously while the response's awaiter is still parked in the microtask queue, so the waitForEvent listener that handlePostAttachInit registers afterwards never sees it — the 3s wait times out in full, and the CDP function-breakpoint bridge attaches only after the forced entry pause has been auto-continued. Pre-launch function breakpoints on ESM module-scoped names then silently never arm (they can only resolve at a pause, and the missed entry pause was the last one). Count 'initialized' events from wireChildEvents onward and have both waiters (initializeChild's and handlePostAttachInit's) consume the latch against a baseline snapshotted before their triggering request is sent — same shape as the #515 init-ACK latch. The latch check and waitForEvent's listener registration share one synchronous frame, so no event can slip between them. Fixes #529 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiTRwc8dshMYgmbp5o5m5t
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 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.
Fixes #529.
What happened
The e2e test
mcp-server-smoke-js-function-bp.test.ts > "honors conditions evaluated in the callee scope"failed in a full-suite run: no pause was ever observed, because the pre-launch function breakpoint oncomputenever armed and the fixture ran to completion.The per-session logs of that exact run tell the story (full timeline in #529): the child's post-attach
initializedevent arrived in the same socket chunk as the attach response (18.056 vs 18.055).MinimalDapClientdispatches events synchronously while the response's awaiter is still parked in the microtask queue, so thewaitForEvent('initialized', 3000)listener thathandlePostAttachInitregisters after the response resolves never saw it. Adoption stalled for the full 3 s,cdpBridge.attachToChildran only at 21.058, and by then the forced entry pause — the one pause where an ESM module-scoped name likecomputecan be resolved viaDebugger.evaluateOnCallFrame— had been held 300 ms for a CDP pause record that could never arrive, forwarded, and auto-continued. Nothing armed; zero stops; silent run-through.The bridge's own guards (
processStoppedEventawaitsattachInFlightandwaitForResolution()) were bypassed because the attach hadn't even started when the entry stop passed through.The fix
Latch child
initializedevents:wireChildEventscounts them from connect time, and both waiters (initializeChild's 12 s wait andhandlePostAttachInit's 3 s wait) consume the latch against a baseline snapshotted before their triggering request is sent. Same shape as the #515 init-ACK latch. The latch check andwaitForEvent's listener registration share one synchronous frame, so no event can slip between them.With the stall gone, the bridge attaches ~150 ms after the child attach (as in passing runs),
attachInFlightis set before the entry stop is processed, and the existing stickyDebugger.pausedreplay + resolution-await machinery makes the entry-pause bind deterministic.Tests
emitInitializedSyncOnreproduces same-chunk delivery (event emitted synchronously insidesendRequest, before the caller'sawaitresumes).does not stall adoption when the post-attach initialized rides the attach response (issue #529)— fails without the fix (adoption still pending at t+1 s), passes with it; also asserts the post-attach mirror re-send ran, proving the latch (not the timeout) resolved the wait.does not stall adoption when initialized rides the initialize response (issue #529)— same race at theinitializerequest (12 s wait).tests/e2e/mcp-server-smoke-js-function-bp.test.ts— full file (6 tests) run 3× against the rebuilt server: green.🤖 Generated with Claude Code
https://claude.ai/code/session_01RiTRwc8dshMYgmbp5o5m5t