From 1853cec3d9ae89af7b19a853e890b11c5a0f20ba Mon Sep 17 00:00:00 2001 From: Copilot CLI Date: Tue, 9 Jun 2026 14:58:03 +0800 Subject: [PATCH 1/2] fix(lmtool): stop double-counting classNameDetection on every invoke findFullyQualifiedClassName was called twice for every invocation that used a simple class name: 1. inside constructDebugCommand to resolve the actual command 2. immediately after, to build the targetInfo message shown to the model Each call also emitted its own classNameDetection telemetry event, so the classNameDetection success/failure counts in ddtelfiltered were inflated by 2x. This makes the apparent ~49% noPackage failure rate look much worse than reality (the true rate is closer to ~25% before accounting for other root causes addressed in #1650). Fix: resolve the detection once at the caller, emit telemetry once, and pass the resolved name into both constructDebugCommand and the targetInfo branch. Side benefit: removes a redundant file system walk from the hot launch path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/languageModelTool.ts | 61 +++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/src/languageModelTool.ts b/src/languageModelTool.ts index 85b28855..6efb4bcf 100644 --- a/src/languageModelTool.ts +++ b/src/languageModelTool.ts @@ -194,7 +194,27 @@ async function debugJavaApplication( } // Step 3: Construct and execute the debugjava command - const debugCommand = constructDebugCommand(input, projectType); + // + // For simple class names (no dot), resolve the fully-qualified class once + // here so we can reuse the result for both the command construction and + // the user-facing targetInfo message below. Previously these two paths + // each called findFullyQualifiedClassName independently, which: + // - duplicated the file system walk on the hot launch path + // - emitted two classNameDetection events per invoke, inflating the + // detection failure rate in telemetry by 2x + let detectedClassName: string | null = null; + if (!input.target.endsWith('.jar') + && !input.target.startsWith('-') + && !input.target.includes('.')) { + detectedClassName = findFullyQualifiedClassName(input.workspacePath, input.target, projectType); + recordLaunchInternal({ + name: 'classNameDetection', + projectType, + detected: !!detectedClassName, + }); + } + + const debugCommand = constructDebugCommand(input, projectType, detectedClassName); // Validate that we can construct a valid command if (!debugCommand || debugCommand === 'debugjava') { @@ -223,8 +243,9 @@ async function debugJavaApplication( } else if (input.target.includes('.')) { targetInfo = input.target; } else { - // Simple class name - check if we successfully detected the full name - const detectedClassName = findFullyQualifiedClassName(input.workspacePath, input.target, projectType); + // Simple class name - reuse the detection result from Step 3 above + // (do NOT call findFullyQualifiedClassName again — it walks the FS + // and was double-emitting the classNameDetection telemetry event). if (detectedClassName) { targetInfo = `${detectedClassName} (detected from ${input.target})`; } else { @@ -584,10 +605,17 @@ async function ensureVSCodeCompilation(workspaceUri: vscode.Uri): Promise Date: Tue, 9 Jun 2026 16:25:42 +0800 Subject: [PATCH 2/2] review: reword dedup comments to drop incorrect '2x telemetry' claim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewer correctly pointed out that the original main branch only had a duplicate findFullyQualifiedClassName FS walk in the targetInfo branch; the recordLaunchInternal({name:'classNameDetection'}) emission was only inside constructDebugCommand, not duplicated. The 'inflates noPackage by 2x' rationale was wrong — observed metrics are not inflated by the dup. Reworded both comments to focus on the FS-walk dedup + ownership clarity. Functional code unchanged; only comments updated. --- src/languageModelTool.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/languageModelTool.ts b/src/languageModelTool.ts index 6efb4bcf..d6f58528 100644 --- a/src/languageModelTool.ts +++ b/src/languageModelTool.ts @@ -199,9 +199,15 @@ async function debugJavaApplication( // here so we can reuse the result for both the command construction and // the user-facing targetInfo message below. Previously these two paths // each called findFullyQualifiedClassName independently, which: - // - duplicated the file system walk on the hot launch path - // - emitted two classNameDetection events per invoke, inflating the - // detection failure rate in telemetry by 2x + // - duplicated the file system walk on the hot launch path (the FS walk + // is the actual user-visible slowdown — it stats every .java file + // under src/main/java up to MAX_FILE_SEARCH_DEPTH) + // - made the call sites harder to reason about, since detection + // ownership was split across constructDebugCommand and the targetInfo + // formatting block + // + // After this refactor, the caller owns detection and its telemetry, + // and constructDebugCommand accepts a pre-resolved name. let detectedClassName: string | null = null; if (!input.target.endsWith('.jar') && !input.target.startsWith('-') @@ -245,7 +251,7 @@ async function debugJavaApplication( } else { // Simple class name - reuse the detection result from Step 3 above // (do NOT call findFullyQualifiedClassName again — it walks the FS - // and was double-emitting the classNameDetection telemetry event). + // and the result is already in `detectedClassName`). if (detectedClassName) { targetInfo = `${detectedClassName} (detected from ${input.target})`; } else {