packages/adapter-java/src/java-debug-adapter.ts:218-222 resolves the JDI bridge like this:
let bridgeDir = resolveJdiBridgeClassDir();
if (!bridgeDir) {
bridgeDir = ensureJdiBridgeCompiled();
}
resolveJdiBridgeClassDir (utils/jdi-resolver.ts:20-53) is a pure existsSync(JdiDapServer.class) walk with no staleness check. ensureJdiBridgeCompiled (jdi-resolver.ts:89-138) does carry an mtime guard (isClassStale) — with a comment explicitly about stale bridges dropping newly-added launch args — but the guard is unreachable from the launch path: buildAdapterCommand only calls it when the resolver already returned null, i.e. when no .class exists at all, so there is never anything to be stale.
validateEnvironment (java-debug-adapter.ts:165-172) has the same shape: resolve-only, warn if missing, never compile.
Consequence: edit JdiDapServer.java, start a debug session, and the old compiled bridge is served silently — new launch keys are dropped, new DAP behavior is absent, and nothing says why. I hit this while fixing #642 (the bridge-side cwd/env support): without a manual pnpm --filter @debugmcp/adapter-java run build:adapter, the freshly-edited bridge source has no effect. The build script's own mtime check (scripts/compile-jdi-bridge.js:58-65) is correct — but only runs on build:adapter, not at session time.
Fix shape: make the launch path staleness-aware — e.g. buildAdapterCommand calls ensureJdiBridgeCompiled() unconditionally when the source file is resolvable (it already early-returns on a fresh class, so the cost is one pair of stats), or resolveJdiBridgeClassDir learns to reject a class older than its adjacent source. Either way the existing isClassStale logic is the right primitive; it just needs to be reachable.
Found while working #642, whose PR ships the bridge fix that made the stale-class serving visible.
packages/adapter-java/src/java-debug-adapter.ts:218-222resolves the JDI bridge like this:resolveJdiBridgeClassDir(utils/jdi-resolver.ts:20-53) is a pureexistsSync(JdiDapServer.class)walk with no staleness check.ensureJdiBridgeCompiled(jdi-resolver.ts:89-138) does carry an mtime guard (isClassStale) — with a comment explicitly about stale bridges dropping newly-added launch args — but the guard is unreachable from the launch path:buildAdapterCommandonly calls it when the resolver already returnednull, i.e. when no.classexists at all, so there is never anything to be stale.validateEnvironment(java-debug-adapter.ts:165-172) has the same shape: resolve-only, warn if missing, never compile.Consequence: edit
JdiDapServer.java, start a debug session, and the old compiled bridge is served silently — new launch keys are dropped, new DAP behavior is absent, and nothing says why. I hit this while fixing #642 (the bridge-sidecwd/envsupport): without a manualpnpm --filter @debugmcp/adapter-java run build:adapter, the freshly-edited bridge source has no effect. The build script's own mtime check (scripts/compile-jdi-bridge.js:58-65) is correct — but only runs onbuild:adapter, not at session time.Fix shape: make the launch path staleness-aware — e.g.
buildAdapterCommandcallsensureJdiBridgeCompiled()unconditionally when the source file is resolvable (it already early-returns on a fresh class, so the cost is one pair of stats), orresolveJdiBridgeClassDirlearns to reject a class older than its adjacent source. Either way the existingisClassStalelogic is the right primitive; it just needs to be reachable.Found while working #642, whose PR ships the bridge fix that made the stale-class serving visible.