Skip to content

fix(java): route every launch through the mtime-aware JDI bridge resolution (#646) - #657

Merged
debugmcpdev merged 2 commits into
mainfrom
fix/646-jdi-bridge-staleness
Sep 2, 2026
Merged

fix(java): route every launch through the mtime-aware JDI bridge resolution (#646)#657
debugmcpdev merged 2 commits into
mainfrom
fix/646-jdi-bridge-staleness

Conversation

@debugmcpdev

Copy link
Copy Markdown
Collaborator

Summary

buildAdapterCommand resolved the compiled JDI bridge with a pure existsSync walk and only fell back to ensureJdiBridgeCompiled() — the one function carrying the isClassStale mtime guard — when no JdiDapServer.class existed at all. After editing JdiDapServer.java (or a checkout that touched it) every session silently ran the old class.

  • packages/adapter-java/src/utils/jdi-resolver.ts — new ensureJdiBridge(): JdiBridgeStatus ({dir, sourceFile, stale, recompiled, error}); ensureJdiBridgeCompiled() is a thin wrapper over it, so the existing API keeps working. A fresh class costs two stats; a stale class is recompiled; a stale class that cannot be recompiled is returned with the reason (javac missing, or the compiler's first stderr lines — javac's output is now captured instead of inherited into the proxy worker's stdio). JDI_BRIDGE_DIR is treated as an explicit override: never compared against the in-tree source, never rebuilt. isJdiBridgeStale() is the no-compile probe. An install that ships only the class is never stale (stat failure → not stale, unchanged).
  • java-debug-adapter.tsbuildAdapterCommand always calls ensureJdiBridge(), logs a recompile, and warns with the reason when it has to run a stale class. validateEnvironment adds a JDI_BRIDGE_STALE warning (valid stays true — the launch will recompile).
  • java-adapter-factory.tsvalidate() surfaces the same warning plus details.jdiBridgeStale, so mcp-debugger doctor shows it.
  • Not touched, deliberately: the hard-coded -cp java/out fallback in adapter-policy-java.ts (only reached when no adapterCommand is supplied) is a separate concern.
  • Tests: jdi-resolver.test.ts now mocks statSync (the mtime branch was unreachable in the old suite) and covers fresh / stale+javac / stale+no-javac / stale+compile-error / JDI_BRIDGE_DIR / class-only install; new jdi-bridge-staleness.test.ts covers the adapter and factory wiring with the resolver mocked. Changelog fragment changelog.d/646.fixed.md.

Verified live

touch packages/adapter-java/java/JdiDapServer.java, then:

  • mcp-debugger doctor → java row ⚠️ warn, fix line: JDI bridge source is newer than the compiled class at …; the next launch recompiles it (javac required)…
  • start_debugging examples/java/HelloWorld.java with a breakpoint on line 24 → the session log carries the JDI_BRIDGE_STALE warning from validateEnvironment and then [JavaDebugAdapter] JDI bridge source was newer than the compiled class; recompiled into …/java/out; the class mtime moved past the source; the breakpoint hit and get_local_variables returned x=10, y=20.

Fixes #646

🤖 Generated with Claude Code

https://claude.ai/code/session_01SNhNB42Y55cgsknYTQb2Bm

CI Bot and others added 2 commits September 2, 2026 10:03
…lution (#646)

buildAdapterCommand resolved the compiled bridge with a pure existsSync walk
and only fell back to ensureJdiBridgeCompiled() — the one function carrying
the isClassStale mtime guard — when no JdiDapServer.class existed at all. So
after editing JdiDapServer.java (or a checkout that touched it) every session
silently ran the old class: new launch keys dropped, new DAP behavior absent,
nothing said why.

- jdi-resolver: new ensureJdiBridge() returns {dir, sourceFile, stale,
  recompiled, error}; ensureJdiBridgeCompiled() is now a thin wrapper. A
  fresh class costs two stats; a stale one is recompiled; a stale one that
  cannot be recompiled is returned with the reason (javac missing, or the
  compiler's first stderr lines — captured instead of inherited into the
  proxy worker's stdio). JDI_BRIDGE_DIR is an explicit override: never
  compared, never rebuilt. isJdiBridgeStale() is the no-compile probe.
- java-debug-adapter: buildAdapterCommand always calls ensureJdiBridge(),
  logs the recompile, warns (with the reason) when it must run a stale
  class; validateEnvironment adds a JDI_BRIDGE_STALE warning.
- java-adapter-factory: validate() surfaces the same warning and
  details.jdiBridgeStale, so `mcp-debugger doctor` shows it.

Verified live: touch JdiDapServer.java → doctor warns on the java row →
start_debugging recompiles (logged) and stops at the breakpoint with
locals intact.

Fixes #646

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SNhNB42Y55cgsknYTQb2Bm
@codecov

codecov Bot commented Sep 2, 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 dbefee8 into main Sep 2, 2026
9 checks passed
@debugmcpdev
debugmcpdev deleted the fix/646-jdi-bridge-staleness branch September 2, 2026 10:13
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.

Java: launch path serves a stale JDI bridge .class — the staleness guard exists but is unreachable

1 participant