Skip to content

fix(#496): keep the adopted thread anchor authoritative across DAP responses/events - #504

Merged
debugmcpdev merged 1 commit into
mainfrom
fix/496-thread-anchor-persistence
Aug 27, 2026
Merged

fix(#496): keep the adopted thread anchor authoritative across DAP responses/events#504
debugmcpdev merged 1 commit into
mainfrom
fix/496-thread-anchor-persistence

Conversation

@debugmcpdev

Copy link
Copy Markdown
Collaborator

Summary

Fixes #496list_threads (and in fact any DAP response or event) reverted the thread anchor adopted by get_stack_trace's frameless-thread fallback or by an explicit get_stack_trace {threadId} (#465), making the anchor single-use and breaking the natural stack → threads → locals flow on any adapter whose stopped thread reports no frames (deterministic on .NET attach).

Root cause

Two copies of the anchor exist: the imperative ProxyManager.currentThreadId and the functional-core dapState.currentThreadId (written only by the stopped handler, read by nothing in dap-core). setCurrentThreadId() wrote only the imperative field, while handleProxyMessage got a newState back from the pure core for every message and copied the stale core value (the old stopped thread) back over the imperative field. One restore, both reported symptoms:

  • list_threads → threads response → newState → anchor reverted (the reported clobber);
  • any inspection call's own stackTrace response → same revert (the "anchor is single-use" comment).

Fix

  • Delete the currentThreadId restore from the core-state sync (the isInitialized/adapterConfigured syncs stay — those are load-bearing). The imperative field is authoritative; the stopped fast path in handleDapEvent writes it.
  • Mirror setCurrentThreadId() and the ProxyManager clobbers currentThreadId with threads[0] on every 'threads' response #396 threads[0] adoption into the core snapshot via setCurrentThreadId(state, tid) from dap-core, so the two stores cannot disagree again.
  • No dap-core changes. A genuine stopped event still re-anchors unconditionally.

Tests

New adopted thread anchor persistence (issue #496) suite in tests/unit/proxy/proxy-manager.start.test.ts — written first and confirmed red on main (each failing expected 1 to be 2, the exact clobber):

  • anchor survives a threads response (the list_threads case) — red → green
  • anchor survives an unrelated output event — red → green
  • anchor survives a stackTrace response (not single-use) — red → green
  • a genuine stopped event still re-anchors (overcorrection guard) — green before and after

Regression-checked green: proxy-manager.start / proxy-manager-message-handling / proxy-manager.branch-coverage / dap-core handlers+state / session-manager-dap suites, plus the full unit suite (229 files, 4184 tests).

🤖 Generated with Claude Code

…sponses/events

The functional-core state echo in handleProxyMessage copied
dapState.currentThreadId (written only by the stopped handler) back over
the imperative anchor on every DAP response, every event (even output),
and status messages. Any anchor adopted via setCurrentThreadId() — the
frameless-thread fallback and get_stack_trace {threadId} (#465) — was
reverted by the next proxy message: list_threads clobbered it, and each
inspection call consumed it via its own response.

The imperative currentThreadId is now authoritative (the stopped fast
path writes it; the echo is gone), and both adoption paths mirror into
the functional-core snapshot so the stores cannot disagree. A genuine
stopped event still re-anchors unconditionally.

Closes #496

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

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@debugmcpdev
debugmcpdev merged commit d31ce27 into main Aug 27, 2026
10 checks passed
@debugmcpdev
debugmcpdev deleted the fix/496-thread-anchor-persistence branch August 27, 2026 00:50
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.

list_threads clobbers the frame anchor adopted by get_stack_trace's frameless-thread fallback (#396/#407 regression)

2 participants