fix: capture removed files' cross-directory neighbors before purge#1983
Conversation
The small-incremental fast path's directory-metrics refresh (refreshAffectedDirectoryMetrics / refresh_affected_directory_metrics) discovers cross-directory neighbors by querying live import edges from the affected directories. This works for added/modified files but not for removed files, since purgeFilesFromGraph/purgeFilesData deletes both directions of their edges before the structure stage runs, leaving no live evidence to discover a lone connecting neighbor directory from. Add captureRemovedFileNeighbors (WASM) / capture_removed_file_neighbors (native), which reads a removed file's forward+reverse import-neighbor file set in detectChanges/detect_changes, before the purge step, and threads it through to the directory-metrics refresh so the neighbor directory's ancestor chain still gets folded into the affected set. Fixes #1839 Impact: 6 functions changed, 10 affected
Greptile SummaryThis PR fixes stale directory metrics after deleting files in the incremental graph build. The main changes are:
Confidence Score: 4/5The scoped JS rebuild path can still leave directory metrics stale after a file deletion.
src/domain/graph/builder/stages/detect-changes.ts Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Detect removed files] --> B[Capture import neighbors before purge]
B --> C[Purge removed file graph data]
C --> D[Run structure refresh]
D --> E[Include changed, removed, and captured neighbor ancestors]
E --> F[Recompute directory metrics]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[Detect removed files] --> B[Capture import neighbors before purge]
B --> C[Purge removed file graph data]
C --> D[Run structure refresh]
D --> E[Include changed, removed, and captured neighbor ancestors]
E --> F[Recompute directory metrics]
Reviews (1): Last reviewed commit: "fix: capture removed files' cross-direct..." | Re-trigger Greptile |
| @@ -579,6 +614,7 @@ function handleScopedBuild(ctx: PipelineContext): void { | |||
| const changedRelPaths = new Set<string>([...changePaths, ...ctx.removed]); | |||
There was a problem hiding this comment.
Scoped Removals Lose Neighbors
When a scoped incremental rebuild deletes a file, this call captures neighbors from ctx.removed, but the scoped path does not go through the normal branch that populates that list from change detection. removedFileNeighbors stays empty before the purge, so cross-directory metrics can remain stale for the deleted file's import neighbors in scoped builds.
Codegraph Impact Analysis6 functions changed → 10 callers affected across 5 files
|
Summary
Follow-up to #1738/#1840. The incremental small-file fast path's directory-metrics refresh (
refreshAffectedDirectoryMetricsinsrc/domain/graph/builder/stages/build-structure.ts, mirrored byrefresh_affected_directory_metricsincrates/codegraph-core/src/features/structure.rs) discovers cross-directory neighbors by querying live import edges from the affected directories — one hop out from the ancestor directories of the touched files. This works correctly for added/modified files (their edges are freshly rebuilt and still present), but not for removed files:purgeFilesFromGraph/purgeFilesDatadeletes both directions of a removed file's edges before the structure stage runs, so there is no live evidence left to discover a neighbor directory that was connected only through that file.Root cause:
detectChanges/detect_changesnever captured a removed file's cross-directory import neighbors before purging it.Fix: Added
captureRemovedFileNeighbors(WASM, indetect-changes.ts) andcapture_removed_file_neighbors(native, indetect_changes.rs), which read a removed file's forward+reverse import-neighbor file set before the purge step runs (mirroring the existing save-then-purge pattern already used for reverse-dep edge reconnection). The result is threaded throughPipelineContext.removedFileNeighbors(JS) / an extra return value fromsave_and_purge_changed(Rust) intorefreshAffectedDirectoryMetrics/refresh_affected_directory_metrics, which now unions the neighbor files' ancestor directories into the affected-directory set before recomputing metrics.Repro (from the issue)
Full build →
pkgBreportsfanIn: 1(correct). Deletea1.js, run an incremental rebuild (fast path fires) →pkgBstill reportedfanIn: 1(stale) before this fix; now correctly reportsfanIn: 0, matching a full rebuild. Verified on both engines.Test plan
tests/integration/issue-1839-directory-fanin-fanout-stale-removal.test.tsreproduces the exact repro above for both the WASM and native engines, diffing the incremental result against an independent from-scratch full build (ground truth). Verified the test fails without the fix (reverted each side independently and confirmed the corresponding engine's test fails) and passes with it.capture_removed_file_neighbors(forward+reverse directions, empty input, and exclusion of other files in the same removal batch).npm test— full suite green (232 files, 3779 tests passed)npm run lint— clean (files touched by this change)cargo check -p codegraph-core/cargo test -p codegraph-core --lib detect_changes— cleancodegraph diff-impact --staged -T— contained blast radius (6 functions changed, 10 transitive callers, all within the builder pipeline)Stacked on #1838 (base branch
fix/issue-1838-bug-track-edits-sh-posttooluse-hook) — only this issue's diff-metrics-refresh change is included.Fixes #1839