Repository navigation
fix(calls): make CALLS resolution deterministic and stop dropping Fil… - #1445
Merged
Merged
Conversation
…e-target edges
Database Parity Check has been red because REL_CALLS disagreed across
backends. Four defects, none of them in the database layer.
1. Nondeterministic resolution input (pipeline.py)
all_file_data is appended in asyncio.as_completed order, so the list
order varies with task scheduling. Everything built by iterating it —
global_class_bases, interface_implementors, class_method_index —
inherited that order, and last-writer-wins assignments then produced a
different graph per run. The graph write loop already sorted for this
reason; resolution did not. Sorted before post-processing.
2. Unguarded Java interface-implementor fan-out (calls.py)
After resolve_function_call returns, extra edges are appended for
every implementor of the resolved interface, gated only on
caller_lang == "java". interface_implementors is keyed on the bare
simple class name across the whole index, so Java's nested Processor
and Kotlin's models.Processor collide and a Java call acquired a
Kotlin callee in an unrelated fixture project. Gated on the
implementor's language matching the caller's.
3. Fabricated dynamic-import edges (calls.py)
The TS/JS dynamic-import branch never read the import's own
specifier; it used static_imports[0] — the first *static* import in
the file. `await import(`./tough_dynamic_${name}`)` therefore resolved
to an unrelated module, while modules-namespaces.ts, which has four
statically resolvable literal specifiers and no static imports, got
nothing. Now resolved from the call's own argument, emitting nothing
when the specifier is not a static string literal.
4. File-target CALLS edges silently dropped on Kuzu/Ladybug (writer.py)
The CALLS write emitted
WHERE (row.called_line_number <= 0 OR called.line_number = ...)
for every target label. File nodes have no line_number
(database_embedded_kuzu.py declares File as path, name, relative_path,
package_name, is_dependency). Neo4j and FalkorDB are untyped and
evaluate it to null, so the edge writes; Kuzu and Ladybug fail at BIND
time, and the error is swallowed as a binder exception, discarding the
whole Function->File batch. Verified directly against kuzu 0.11.3:
WITH line predicate -> Binder exception: Cannot find property
line_number for called.
WITHOUT line predicate -> OK, edge written
This was real data loss on the default embedded backends, not just a
parity artefact. The predicate is now built per label, the same way
called_context_clause already was.
Verified locally against an isolated Neo4j:
REL_CALLS | KuzuDB | LadybugDB | FalkorDB | Neo4j | Match?
before | 647 | 648 | 649 | 648 | NO (and it varied run to run)
after | 651 | 651 | 651 | 651 | YES
union=640 common-to-all=640 differing=0
tests/e2e/test_verify_databases_parity.py: 1 passed
tests/unit + tests/integration: 967 passed, 7 skipped, 0 failed.
test_typescript_dynamic_import_calls_static_import_file asserted the
fabricated edge (`assert edge["called_name"] == "tough_circular_b.ts"`),
so it is rewritten to assert that a non-literal specifier produces no
edge, with the history recorded in its docstring.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
Hi! 👋 Join our CodeGraphContext Discord channel to collaborate: https://discord.gg/dR4QY32uYQ |
Contributor
🔍 PR Code Graph Analysisfix(calls): make CALLS resolution deterministic and stop dropping Fil… (#1445) 📊 Interactive VisualizationView the blast radius graph: PR Reviewer Dashboard 📦 ArtifactsThe graph JSON has been uploaded as a build artifact: Generated by CodeGraphContext using FalkorDB Lite |
ben-jamin-chen
added a commit
to ben-jamin-chen/CodeGraphContext
that referenced
this pull request
Jul 31, 2026
Fan CALLS UNWIND batches across a bounded ThreadPoolExecutor (neo4j only, default 8 workers, CGC_CALLS_WRITE_CONCURRENCY override) with one session per worker; batches were previously serialized behind each commit fsync. Dedup happens before slicing so no edge lands in two concurrent batches; deadlocks on hot callee nodes are retried by managed transactions, and queued batches are cancelled if a batch hard-fails. Rebased onto upstream's deterministic CALLS writer (CodeGraphContext#1445): keeps the HEURISTIC_CALLS routing and MERGE semantics, and carves out Parameter targets (keyed by function_line_number) so the generic line predicate cannot silently drop those edges. The original .cgcignore anchor hunk was dropped: upstream CodeGraphContext#1367 ships an equivalent path-segment-boundary anchor. Co-Authored-By: Claude Fable 5 <[email protected]>
ben-jamin-chen
added a commit
to ben-jamin-chen/CodeGraphContext
that referenced
this pull request
Aug 10, 2026
Fan CALLS UNWIND batches across a bounded ThreadPoolExecutor (neo4j only, default 8 workers, CGC_CALLS_WRITE_CONCURRENCY override) with one session per worker; batches were previously serialized behind each commit fsync. Dedup happens before slicing so no edge lands in two concurrent batches; deadlocks on hot callee nodes are retried by managed transactions, and queued batches are cancelled if a batch hard-fails. Rebased onto upstream's deterministic CALLS writer (CodeGraphContext#1445): keeps the HEURISTIC_CALLS routing and MERGE semantics, and carves out Parameter targets (keyed by function_line_number) so the generic line predicate cannot silently drop those edges. The original .cgcignore anchor hunk was dropped: upstream CodeGraphContext#1367 ships an equivalent path-segment-boundary anchor. Co-Authored-By: Claude Fable 5 <[email protected]>
ben-jamin-chen
added a commit
to ben-jamin-chen/CodeGraphContext
that referenced
this pull request
Aug 17, 2026
Fan CALLS UNWIND batches across a bounded ThreadPoolExecutor (neo4j only, default 8 workers, CGC_CALLS_WRITE_CONCURRENCY override) with one session per worker; batches were previously serialized behind each commit fsync. Dedup happens before slicing so no edge lands in two concurrent batches; deadlocks on hot callee nodes are retried by managed transactions, and queued batches are cancelled if a batch hard-fails. Rebased onto upstream's deterministic CALLS writer (CodeGraphContext#1445): keeps the HEURISTIC_CALLS routing and MERGE semantics, and carves out Parameter targets (keyed by function_line_number) so the generic line predicate cannot silently drop those edges. The original .cgcignore anchor hunk was dropped: upstream CodeGraphContext#1367 ships an equivalent path-segment-boundary anchor. Co-Authored-By: Claude Fable 5 <[email protected]>
ben-jamin-chen
added a commit
to ben-jamin-chen/CodeGraphContext
that referenced
this pull request
Aug 21, 2026
Fan CALLS UNWIND batches across a bounded ThreadPoolExecutor (neo4j only, default 8 workers, CGC_CALLS_WRITE_CONCURRENCY override) with one session per worker; batches were previously serialized behind each commit fsync. Dedup happens before slicing so no edge lands in two concurrent batches; deadlocks on hot callee nodes are retried by managed transactions, and queued batches are cancelled if a batch hard-fails. Rebased onto upstream's deterministic CALLS writer (CodeGraphContext#1445): keeps the HEURISTIC_CALLS routing and MERGE semantics, and carves out Parameter targets (keyed by function_line_number) so the generic line predicate cannot silently drop those edges. The original .cgcignore anchor hunk was dropped: upstream CodeGraphContext#1367 ships an equivalent path-segment-boundary anchor. Co-Authored-By: Claude Fable 5 <[email protected]>
ben-jamin-chen
added a commit
to ben-jamin-chen/CodeGraphContext
that referenced
this pull request
Aug 27, 2026
Fan CALLS UNWIND batches across a bounded ThreadPoolExecutor (neo4j only, default 8 workers, CGC_CALLS_WRITE_CONCURRENCY override) with one session per worker; batches were previously serialized behind each commit fsync. Dedup happens before slicing so no edge lands in two concurrent batches; deadlocks on hot callee nodes are retried by managed transactions, and queued batches are cancelled if a batch hard-fails. Rebased onto upstream's deterministic CALLS writer (CodeGraphContext#1445): keeps the HEURISTIC_CALLS routing and MERGE semantics, and carves out Parameter targets (keyed by function_line_number) so the generic line predicate cannot silently drop those edges. The original .cgcignore anchor hunk was dropped: upstream CodeGraphContext#1367 ships an equivalent path-segment-boundary anchor. Co-Authored-By: Claude Fable 5 <[email protected]>
ben-jamin-chen
added a commit
to ben-jamin-chen/CodeGraphContext
that referenced
this pull request
Sep 3, 2026
Fan CALLS UNWIND batches across a bounded ThreadPoolExecutor (neo4j only, default 8 workers, CGC_CALLS_WRITE_CONCURRENCY override) with one session per worker; batches were previously serialized behind each commit fsync. Dedup happens before slicing so no edge lands in two concurrent batches; deadlocks on hot callee nodes are retried by managed transactions, and queued batches are cancelled if a batch hard-fails. Rebased onto upstream's deterministic CALLS writer (CodeGraphContext#1445): keeps the HEURISTIC_CALLS routing and MERGE semantics, and carves out Parameter targets (keyed by function_line_number) so the generic line predicate cannot silently drop those edges. The original .cgcignore anchor hunk was dropped: upstream CodeGraphContext#1367 ships an equivalent path-segment-boundary anchor. Co-Authored-By: Claude Fable 5 <[email protected]>
ben-jamin-chen
added a commit
to ben-jamin-chen/CodeGraphContext
that referenced
this pull request
Sep 7, 2026
Fan CALLS UNWIND batches across a bounded ThreadPoolExecutor (neo4j only, default 8 workers, CGC_CALLS_WRITE_CONCURRENCY override) with one session per worker; batches were previously serialized behind each commit fsync. Dedup happens before slicing so no edge lands in two concurrent batches; deadlocks on hot callee nodes are retried by managed transactions, and queued batches are cancelled if a batch hard-fails. Rebased onto upstream's deterministic CALLS writer (CodeGraphContext#1445): keeps the HEURISTIC_CALLS routing and MERGE semantics, and carves out Parameter targets (keyed by function_line_number) so the generic line predicate cannot silently drop those edges. The original .cgcignore anchor hunk was dropped: upstream CodeGraphContext#1367 ships an equivalent path-segment-boundary anchor. Co-Authored-By: Claude Fable 5 <[email protected]>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…e-target edges
Database Parity Check has been red because REL_CALLS disagreed across backends. Four defects, none of them in the database layer.
Nondeterministic resolution input (pipeline.py) all_file_data is appended in asyncio.as_completed order, so the list order varies with task scheduling. Everything built by iterating it — global_class_bases, interface_implementors, class_method_index — inherited that order, and last-writer-wins assignments then produced a different graph per run. The graph write loop already sorted for this reason; resolution did not. Sorted before post-processing.
Unguarded Java interface-implementor fan-out (calls.py) After resolve_function_call returns, extra edges are appended for every implementor of the resolved interface, gated only on caller_lang == "java". interface_implementors is keyed on the bare simple class name across the whole index, so Java's nested Processor and Kotlin's models.Processor collide and a Java call acquired a Kotlin callee in an unrelated fixture project. Gated on the implementor's language matching the caller's.
Fabricated dynamic-import edges (calls.py) The TS/JS dynamic-import branch never read the import's own specifier; it used static_imports[0] — the first static import in the file.
await import(./tough_dynamic_${name})therefore resolved to an unrelated module, while modules-namespaces.ts, which has four statically resolvable literal specifiers and no static imports, got nothing. Now resolved from the call's own argument, emitting nothing when the specifier is not a static string literal.File-target CALLS edges silently dropped on Kuzu/Ladybug (writer.py) The CALLS write emitted WHERE (row.called_line_number <= 0 OR called.line_number = ...) for every target label. File nodes have no line_number (database_embedded_kuzu.py declares File as path, name, relative_path, package_name, is_dependency). Neo4j and FalkorDB are untyped and evaluate it to null, so the edge writes; Kuzu and Ladybug fail at BIND time, and the error is swallowed as a binder exception, discarding the whole Function->File batch. Verified directly against kuzu 0.11.3:
This was real data loss on the default embedded backends, not just a parity artefact. The predicate is now built per label, the same way called_context_clause already was.
Verified locally against an isolated Neo4j:
tests/unit + tests/integration: 967 passed, 7 skipped, 0 failed.
test_typescript_dynamic_import_calls_static_import_file asserted the fabricated edge (
assert edge["called_name"] == "tough_circular_b.ts"), so it is rewritten to assert that a non-literal specifier produces no edge, with the history recorded in its docstring.