diff --git a/src/domain/graph/builder/helpers.ts b/src/domain/graph/builder/helpers.ts index 4dfd6f46..c0eba2d4 100644 --- a/src/domain/graph/builder/helpers.ts +++ b/src/domain/graph/builder/helpers.ts @@ -48,6 +48,13 @@ export const BUILTIN_RECEIVERS: Set = new Set([ 'require', ]); +/** Phase 8.5: confidence penalty applied to CHA-dispatch edges. */ +export const CHA_DISPATCH_PENALTY = 0.1; +/** Phase 8.5: fixed confidence for typed-receiver (interface/CHA) dispatch edges. + * File proximity is not meaningful for virtual dispatch — all three engine paths + * (WASM inline, WASM post-pass, native post-pass) must agree on this value. */ +export const CHA_TYPED_DISPATCH_CONFIDENCE = 0.8; + /** Check if a directory entry should be skipped (ignored dirs, dotfiles). */ function shouldSkipEntry(entry: fs.Dirent, extraIgnore: Set | null): boolean { if (entry.name.startsWith('.') && entry.name !== '.') { @@ -517,7 +524,14 @@ export function runChaPostPass(db: BetterSqlite3Database): number { const key = `${source_id}|${methodNode.id}`; if (seen.has(key)) continue; seen.add(key); - newEdges.push([source_id, methodNode.id, 'calls', CHA_DISPATCH_CONFIDENCE, 0, 'cha']); + newEdges.push([ + source_id, + methodNode.id, + 'calls', + CHA_TYPED_DISPATCH_CONFIDENCE, + 0, + 'cha', + ]); } } diff --git a/src/domain/graph/builder/stages/build-edges.ts b/src/domain/graph/builder/stages/build-edges.ts index 29cc4100..a60c3acf 100644 --- a/src/domain/graph/builder/stages/build-edges.ts +++ b/src/domain/graph/builder/stages/build-edges.ts @@ -45,7 +45,13 @@ import { import type { ChaContext } from '../cha.js'; import { buildChaContext, resolveChaTargets, resolveThisDispatch } from '../cha.js'; import type { PipelineContext } from '../context.js'; -import { BUILTIN_RECEIVERS, batchInsertEdges, runChaPostPass } from '../helpers.js'; +import { + BUILTIN_RECEIVERS, + batchInsertEdges, + CHA_DISPATCH_PENALTY, + CHA_TYPED_DISPATCH_CONFIDENCE, + runChaPostPass, +} from '../helpers.js'; import { getResolved, isBarrelFile, resolveBarrelExportCached } from './resolve-imports.js'; // ── Local types ────────────────────────────────────────────────────────── @@ -101,9 +107,6 @@ interface NativeEdge { dynamic: number; } -/** Phase 8.5: confidence penalty applied to CHA-dispatch edges. */ -export const CHA_DISPATCH_PENALTY = 0.1; - // ── Node lookup setup ─────────────────────────────────────────────────── function makeGetNodeIdStmt(db: BetterSqlite3Database): NodeIdStmt { @@ -735,13 +738,12 @@ function buildChaPostPass( for (const t of chaTargets) { const edgeKey = `${caller.id}|${t.id}`; if (t.id !== caller.id && !seenByPair.has(edgeKey)) { - // Typed-receiver (interface/CHA) dispatch: use the same hardcoded 0.8 that - // runChaPostPass (helpers.ts) and runPostNativeCha (native-orchestrator.ts) - // use — file proximity is not meaningful for virtual dispatch confidence. + // Typed-receiver (interface/CHA) dispatch: use CHA_TYPED_DISPATCH_CONFIDENCE + // — file proximity is not meaningful for virtual dispatch confidence. // this/super dispatch keeps computeConfidence-based proximity scoring to // match runPostNativeThisDispatch (native-orchestrator.ts). const conf = isTypedReceiverDispatch - ? 0.8 + ? CHA_TYPED_DISPATCH_CONFIDENCE : computeConfidence(relPath, t.file, null) - CHA_DISPATCH_PENALTY; if (conf > 0) { seenByPair.add(edgeKey); @@ -1327,13 +1329,12 @@ function buildFileCallEdges( for (const t of chaTargets) { const edgeKey = `${caller.id}|${t.id}`; if (t.id !== caller.id && !seenCallEdges.has(edgeKey) && !ptsEdgeRows.has(edgeKey)) { - // Typed-receiver (interface/CHA) dispatch: use the same hardcoded 0.8 that - // runChaPostPass (helpers.ts) and runPostNativeCha (native-orchestrator.ts) - // use — file proximity is not meaningful for virtual dispatch confidence. + // Typed-receiver (interface/CHA) dispatch: use CHA_TYPED_DISPATCH_CONFIDENCE + // — file proximity is not meaningful for virtual dispatch confidence. // this/super dispatch keeps computeConfidence-based proximity scoring to - // match runPostNativeThisDispatch (native-orchestrator.ts line 906). + // match runPostNativeThisDispatch (native-orchestrator.ts). const conf = isTypedReceiverDispatch - ? 0.8 + ? CHA_TYPED_DISPATCH_CONFIDENCE : computeConfidence(relPath, t.file, null) - CHA_DISPATCH_PENALTY; if (conf > 0) { seenCallEdges.add(edgeKey); diff --git a/src/domain/graph/builder/stages/native-orchestrator.ts b/src/domain/graph/builder/stages/native-orchestrator.ts index bdfcd588..f6dd1212 100644 --- a/src/domain/graph/builder/stages/native-orchestrator.ts +++ b/src/domain/graph/builder/stages/native-orchestrator.ts @@ -49,14 +49,14 @@ import type { PipelineContext } from '../context.js'; import { batchInsertEdges, batchInsertNodes, - CHA_DISPATCH_CONFIDENCE, + CHA_DISPATCH_PENALTY, + CHA_TYPED_DISPATCH_CONFIDENCE, collectFiles as collectFilesUtil, fileHash, fileStat, readFileSafe, } from '../helpers.js'; import { NativeDbProxy } from '../native-db-proxy.js'; -import { CHA_DISPATCH_PENALTY } from './build-edges.js'; import { closeNativeDb } from './native-db-lifecycle.js'; // ── Native orchestrator types ────────────────────────────────────────── @@ -572,7 +572,7 @@ function runPostNativeCha( // Find existing call edges targeting qualified methods (e.g., 'IWorker.doWork'). // Include caller_file and method_file so affectedFiles can be populated for - // incremental role reclassification; confidence is CHA_DISPATCH_CONFIDENCE matching runChaPostPass. + // incremental role reclassification; confidence uses CHA_TYPED_DISPATCH_CONFIDENCE matching runChaPostPass. // When scopeToChangedFiles is true, restrict to call sites in the changed files // (safe because no hierarchy or RTA evidence changed outside those files). let callToMethods: Array<{ source_id: number; method_name: string; caller_file: string | null }>; @@ -668,7 +668,7 @@ function runPostNativeCha( const key = `${source_id}|${methodNode.id}`; if (seen.has(key)) continue; seen.add(key); - const conf = CHA_DISPATCH_CONFIDENCE; + const conf = CHA_TYPED_DISPATCH_CONFIDENCE; newEdges.push([source_id, methodNode.id, 'calls', conf, 0, 'cha']); newEdgeCount++; if (caller_file) affectedFiles.add(caller_file); @@ -955,14 +955,6 @@ interface PostPassTimings { techniqueBackfillMs: number; } -interface PostPassTimings { - gapDetectMs: number; - chaMs: number; - thisDispatchMs: number; - reclassifyMs: number; - techniqueBackfillMs: number; -} - /** Format timing result from native orchestrator phases + JS post-processing. */ function formatNativeTimingResult( p: Record,