Found during dogfooding v3.16.0
Severity: Medium
Command: codegraph build (incremental)
Reproduction
codegraph build . # full baseline build
sqlite3 .codegraph/graph.db "SELECT n1.file,n1.name,n1.line,e.kind,n2.file,n2.name,n2.line \
FROM edges e JOIN nodes n1 ON e.source_id=n1.id JOIN nodes n2 ON e.target_id=n2.id \
ORDER BY 1,2,3,4,5,6,7;" | sort > /tmp/edges_baseline.txt
# Edit a COMPLETELY UNRELATED file — src/domain/graph/builder/pipeline.ts,
# which has no direct relationship to src/domain/parser.ts or wasm-worker-pool.ts:
echo "" >> src/domain/graph/builder/pipeline.ts
codegraph build . # incremental: "1 changed, 0 removed"
# Revert the edit back to the exact original bytes:
git show HEAD:src/domain/graph/builder/pipeline.ts > src/domain/graph/builder/pipeline.ts
codegraph build . # incremental again: "1 changed, 0 removed"
sqlite3 .codegraph/graph.db "SELECT ... same query ..." | sort > /tmp/edges_after.txt
comm -23 /tmp/edges_baseline.txt /tmp/edges_after.txt
Expected behavior
Editing (and then reverting to byte-identical content) a file with no relationship to parser.ts/wasm-worker-pool.ts should leave every edge involving those two files completely unchanged after incremental rebuild. Edge count and edge set should match a full --no-incremental rebuild of the same tree exactly.
Actual behavior
The graph permanently loses exactly 10 edges (43195 → 43185, confirmed via full edge-set diff, not just count) and does not recover them even after reverting the triggering file back to its original content:
src/domain/parser.ts|backfillTypeMap|1049|calls|src/domain/wasm-worker-pool.ts|WasmWorkerPool.parse|183
src/domain/parser.ts|backfillTypeMap|1049|receiver|src/domain/wasm-worker-pool.ts|WasmWorkerPool|158
src/domain/parser.ts|backfillTypeMapBatch|1142|calls|src/domain/wasm-worker-pool.ts|WasmWorkerPool.parse|183
src/domain/parser.ts|backfillTypeMapBatch|1142|receiver|src/domain/wasm-worker-pool.ts|WasmWorkerPool|158
src/domain/parser.ts|ensureWasmTrees|369|calls|src/domain/wasm-worker-pool.ts|WasmWorkerPool.parse|183
src/domain/parser.ts|ensureWasmTrees|369|receiver|src/domain/wasm-worker-pool.ts|WasmWorkerPool|158
src/domain/parser.ts|parseFileAuto|1112|calls|src/domain/wasm-worker-pool.ts|WasmWorkerPool.parse|183
src/domain/parser.ts|parseFileAuto|1112|receiver|src/domain/wasm-worker-pool.ts|WasmWorkerPool|158
src/domain/parser.ts|parseFilesWasm|1181|calls|src/domain/wasm-worker-pool.ts|WasmWorkerPool.parse|183
src/domain/parser.ts|parseFilesWasm|1181|receiver|src/domain/wasm-worker-pool.ts|WasmWorkerPool|158
All 10 are calls -> WasmWorkerPool.parse (method call) or receiver -> WasmWorkerPool (constructor/receiver-type binding) edges from 5 caller functions in parser.ts, all targeting the WasmWorkerPool class in wasm-worker-pool.ts. Notably, the sibling calls -> getWasmWorkerPool edge from each of those same 5 callers survives untouched — only the receiver-typed dispatch to the WasmWorkerPool class itself is lost.
The --verbose log for the triggering incremental build shows:
[codegraph DEBUG] runPostNativeCha: Gate A (hierarchy) fired — running full scan
suggesting the incremental CHA/receiver-dispatch re-resolution ("Gate A" full scan) doesn't reproduce the same WasmWorkerPool receiver-dispatch edges that a genuine --no-incremental build computes, even though it's not scoped to only the touched file.
Once lost, a further no-op incremental build does not recover them — only --no-incremental restores the correct 43195-edge graph.
Root cause
Likely in the native incremental orchestrator's CHA/receiver-dispatch re-resolution path (runPostNativeCha / "Gate A" hierarchy full-scan, per the debug log), which appears to run a resolution pass over class-hierarchy/receiver-typed dispatch edges but produces a different (smaller) result set than the equivalent full-build pass for the WasmWorkerPool class specifically.
Suggested fix
Compare the "Gate A full scan" receiver-dispatch resolution logic against the full-build equivalent for WasmWorkerPool-style singleton/class receiver patterns (getWasmWorkerPool() returning a WasmWorkerPool instance, .parse() called on it) and find where the incremental path fails to emit or fails to preserve the receiver/calls edges targeting the class's own methods.
Found during dogfooding v3.16.0
Severity: Medium
Command:
codegraph build(incremental)Reproduction
Expected behavior
Editing (and then reverting to byte-identical content) a file with no relationship to
parser.ts/wasm-worker-pool.tsshould leave every edge involving those two files completely unchanged after incremental rebuild. Edge count and edge set should match a full--no-incrementalrebuild of the same tree exactly.Actual behavior
The graph permanently loses exactly 10 edges (43195 → 43185, confirmed via full edge-set diff, not just count) and does not recover them even after reverting the triggering file back to its original content:
All 10 are
calls -> WasmWorkerPool.parse(method call) orreceiver -> WasmWorkerPool(constructor/receiver-type binding) edges from 5 caller functions inparser.ts, all targeting theWasmWorkerPoolclass inwasm-worker-pool.ts. Notably, the siblingcalls -> getWasmWorkerPooledge from each of those same 5 callers survives untouched — only the receiver-typed dispatch to theWasmWorkerPoolclass itself is lost.The
--verboselog for the triggering incremental build shows:suggesting the incremental CHA/receiver-dispatch re-resolution ("Gate A" full scan) doesn't reproduce the same
WasmWorkerPoolreceiver-dispatch edges that a genuine--no-incrementalbuild computes, even though it's not scoped to only the touched file.Once lost, a further no-op incremental build does not recover them — only
--no-incrementalrestores the correct 43195-edge graph.Root cause
Likely in the native incremental orchestrator's CHA/receiver-dispatch re-resolution path (
runPostNativeCha/ "Gate A" hierarchy full-scan, per the debug log), which appears to run a resolution pass over class-hierarchy/receiver-typed dispatch edges but produces a different (smaller) result set than the equivalent full-build pass for theWasmWorkerPoolclass specifically.Suggested fix
Compare the "Gate A full scan" receiver-dispatch resolution logic against the full-build equivalent for
WasmWorkerPool-style singleton/class receiver patterns (getWasmWorkerPool()returning aWasmWorkerPoolinstance,.parse()called on it) and find where the incremental path fails to emit or fails to preserve thereceiver/callsedges targeting the class's own methods.