perf(query): fix diffImpact latency regression (#904)#905
perf(query): fix diffImpact latency regression (#904)#905carlos-alm wants to merge 4 commits intomainfrom
Conversation
`semverCompare('3.9.3-dev.6', '3.9.1')` returned -1 (less than) because
`Number('3-dev')` is NaN, which the `|| 0` fallback turned into 0,
making the comparison `0 < 1`. This caused `shouldSkipNativeOrchestrator`
to flag all pre-release builds as "buggy", disabling the native
orchestrator fast path introduced in #897.
Strip `-<prerelease>` before splitting on `.` so the numeric comparison
sees `3.9.3` vs `3.9.1` correctly.
Skip co-change, ownership, and boundary lookups when findAffectedFunctions returns empty — all callers return early on this case anyway. Also pass the already-loaded config to checkBoundaryViolations to avoid a redundant loadConfig call. Saves ~2-3ms of fixed overhead per diffImpact invocation when the diff touches no function bodies (the common case for comment/import/type-only changes and the benchmark probe). Closes #904
|
Claude finished @carlos-alm's task —— View job Reviewing PR #905: diffImpact latency regression fix
|
Greptile SummaryThis PR eliminates a latency regression in Confidence Score: 5/5Safe to merge — all three fixes are correct and the prior boundary-violation concern has been resolved. The short-circuit is logically sound and the return shape aligns with existing early-exit paths (summary: null). Boundary checks are preserved for file-only changes. The config pass-through fix is straightforward. semverCompare now correctly strips pre-release suffixes. No remaining P0/P1 findings. No files require special attention.
|
| Filename | Overview |
|---|---|
| src/domain/analysis/diff-impact.ts | Short-circuit added for empty affectedFunctions case; boundary checks correctly preserved; config pass-through eliminates duplicate loadConfig call. Prior review concern about suppressed boundary violations is resolved. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[diffImpactData called] --> B{Git repo?}
B -- No --> ERR[return error]
B -- Yes --> C[runGitDiff]
C --> D{Output empty?}
D -- Yes --> E[return changedFiles:0, summary:null]
D -- No --> F[parseGitDiff]
F --> G{changedRanges empty?}
G -- Yes --> E
G -- No --> H[findAffectedFunctions]
H --> I{affectedFunctions.length === 0?}
I -- Yes NEW --> J[checkBoundaryViolations file-scoped]
J --> K[return with historicallyCoupled:empty, ownership:null, summary:null]
I -- No --> L[buildFunctionImpactResults BFS]
L --> M[lookupCoChanges]
M --> N[lookupOwnership]
N --> O[checkBoundaryViolations file-scoped]
O --> P[return full result with summary object]
Reviews (3): Last reviewed commit: "Merge branch 'main' into fix/semver-prer..." | Re-trigger Greptile
| if (affectedFunctions.length === 0) { | ||
| const base = { | ||
| changedFiles: changedRanges.size, | ||
| newFiles: [...newFiles], | ||
| affectedFunctions: [] as unknown[], | ||
| affectedFiles: [] as string[], | ||
| historicallyCoupled: [] as unknown[], | ||
| ownership: null, | ||
| boundaryViolations: [] as unknown[], | ||
| boundaryViolationCount: 0, | ||
| summary: { | ||
| functionsChanged: 0, | ||
| callersAffected: 0, | ||
| filesAffected: 0, | ||
| historicallyCoupledCount: 0, | ||
| ownersAffected: 0, | ||
| boundaryViolationCount: 0, | ||
| }, | ||
| }; | ||
| return paginateResult(base, 'affectedFunctions', { limit: opts.limit, offset: opts.offset }); | ||
| } |
There was a problem hiding this comment.
Boundary checks silently skipped for file-only changes
checkBoundaryViolations is scoped to files (scopeFiles: [...changedRanges.keys()]), not functions — so it can surface violations from pure import/type-alias changes even when no function body was touched. The new early return hardcodes boundaryViolations: [] / boundaryViolationCount: 0 for that case, silently suppressing any real violations.
If a caller uses summary.boundaryViolationCount (rather than checking functionsChanged first), it will see 0 even when architectural boundaries are actually violated. The PR description asserts callers return early on empty affectedFunctions, but that claim should be validated before skipping a check that is semantically independent of function-level changes. At minimum, consider preserving the boundary check in this branch:
const { boundaryViolations, boundaryViolationCount } = checkBoundaryViolations(
db,
changedRanges,
noTests,
{ ...opts, config },
repoRoot,
);
const base = {
...
boundaryViolations,
boundaryViolationCount,
summary: {
...
boundaryViolationCount,
},
};
return paginateResult(base, 'affectedFunctions', { limit: opts.limit, offset: opts.offset });
There was a problem hiding this comment.
Fixed in 14bf7c4 — the short-circuit path now preserves the boundary check since checkBoundaryViolations is file-scoped (via scopeFiles: [...changedRanges.keys()]) and can surface real violations even when no function bodies were touched. The return shape is also aligned: summary: null now matches the two existing early-exit paths, so callers that branch on summary === null behave consistently.
Codegraph Impact Analysis1 functions changed → 3 callers affected across 3 files
|
The short-circuit path was hardcoding boundaryViolations: [] when no functions were affected. Since boundary checks are file-scoped (not function-scoped), import or type-alias changes can still produce real violations. Preserve the check and align the return shape (summary: null) with the two existing early-exit paths.
|
Addressed both Greptile findings in 14bf7c4:
|

Summary
diffImpactDatawhen no functions are affected — skips BFS traversal, co-change lookup, ownership lookup, and boundary checks. All callers (CLI, MCP, benchmark) already return early on emptyaffectedFunctions, so these lookups were wasted work. Saves ~2-3ms of fixed overhead per invocation.checkBoundaryViolations— eliminates redundantloadConfig(repoRoot)call (was being called twice with different cwd keys, bypassing the cache).semverCompare—semverCompare('3.9.3-dev.6', '3.9.1')returned-1becauseNumber('3-dev')→NaN→0, causingshouldSkipNativeOrchestratorto flag all pre-release builds as buggy.Investigation findings
The 3.9.2 regression (WASM +94%, native +28%) is primarily CI variance on a low baseline —
diffImpactDatacode is identical between 3.9.1 and 3.9.2, andaffectedFunctions: 0in both versions means BFS never runs. The entire measurement is fixed overhead:execFileSync('git diff')(~3-8ms) plus unnecessary lookups (~2-3ms). The WASM +94% (+6.1ms absolute) vs native +28% (+2.1ms) disparity reflects process spawn variance across CI runners.Closes #904
Test plan
tsc --noEmitpassesbiome checkpasses on changed filesemverCompareunit tests pass (23/23)