Skip to content

fix(calls): make CALLS resolution deterministic and stop dropping Fil… - #1445

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/parity-calls-determinism-and-file-edges
Jul 26, 2026
Merged

Shashankss1205 merged 1 commit into
mainfrom
fix/parity-calls-determinism-and-file-edges

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

…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.

…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]>
@vercel

vercel Bot commented Jul 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
code-graph-context-pack Building Building Preview, Comment Jul 26, 2026 6:59pm

@github-actions

Copy link
Copy Markdown
Contributor

Hi! 👋 Join our CodeGraphContext Discord channel to collaborate: https://discord.gg/dR4QY32uYQ

@Shashankss1205
Shashankss1205 merged commit 11a86d4 into main Jul 26, 2026
16 of 17 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog tasks to Done in CGC Progress Board Jul 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR Code Graph Analysis

fix(calls): make CALLS resolution deterministic and stop dropping Fil… (#1445)

📊 Interactive Visualization

View the blast radius graph: PR Reviewer Dashboard

📦 Artifacts

The graph JSON has been uploaded as a build artifact: pr-code-graph-1445


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

1 active deployment
Preview — 2904df57 Deployed Jul 26, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant