Skip to content

fix(merge): respect C# type visibility across repos - #3534

Closed
DevChiniwala wants to merge 1 commit into
Graphify-Labs:v8from
DevChiniwala:fix/guard-third-party-cross-repo-calls
Closed

DevChiniwala wants to merge 1 commit into
Graphify-Labs:v8from
DevChiniwala:fix/guard-third-party-cross-repo-calls

Conversation

@DevChiniwala

Copy link
Copy Markdown
Contributor

Fixes #3360

Summary

  • prevent merge-graphs from binding a parked C# call to an unrelated same-named type
  • reuse the existing C# namespace, using, alias, and lexical-scope resolver
  • preserve valid cross-repository calls when the target type is actually visible

Problem

The cross-repository member-call pass intentionally parks calls whose receiver type is absent from the current repository, then resolves them after graph composition. Its candidate guard checked the candidate's language, source file, repository, and uniqueness, but matched only the bare receiver name for C#.

That allowed an unresolved third-party type such as FluentValidation.IValidator to bind to an unrelated Lib.Domain.Interfaces.IValidator from another merged repository. The resulting calls edge is well-formed and plausible, but fabricates a dependency that does not exist.

Fix

For parked C# calls, resolve the receiver through CsharpNameResolver before emitting the cross-repository edge. The resolver already owns Graphify's C# namespace/using/alias visibility rules, so the merge pass now accepts only the exact candidate selected by those rules. Other parked languages retain their existing behavior, and source-backed C# calls resolved within a repository are unchanged.

Validation

  • python -m pytest tests\\test_cross_repo_external_call_guards.py -q — 3 passed
  • python -m pytest tests\\test_cross_repo_member_calls.py tests\\test_cross_repo_external_call_guards.py tests\\test_csharp_type_resolution.py tests\\test_global_graph.py tests\\test_merge_graphs_cli.py -q — 89 passed
  • ruff check graphify\\cross_repo_calls.py tests\\test_cross_repo_external_call_guards.py — passed
  • python -m compileall -q graphify — passed
  • git diff --check — passed

The regression test first failed on the current v8 implementation (1 failed, 1 passed): the unrelated third-party receiver incorrectly resolved as one cross-repository call. It passes after the guard, including a real two-root C# extraction case.

Copilot AI lite review requested due to automatic review settings September 13, 2026 13:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Formal verification. 1 change(s) tested, no difference found (not proven).


Graphify review — findings

Applies C# namespace/using visibility rules when linking cross-repo member calls, so an unresolved receiver like FluentValidation.IValidator only binds to a candidate type when the caller's namespace, using, or alias resolves that exact name to it (#3360). link_cross_repo_member_calls builds a CsharpNameResolver from the merged graph on demand—only when parked C# entries exist—and rejects candidates whose resolved type doesn't match; non-C# languages keep their existing suffix guard.

No blocking issues surfaced. 7 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 222 functions depend on the 22 functions this change touches.

Health — this change adds coupling hotspots:

  • new: dispatch_command() — 2 callers, 124 callees
  • new: link_cross_repo_member_calls() — 21 callers, 8 callees
  • new: global_add() — 12 callers, 9 callees
  • new: test_a_cpp_header_declaration_answers_through_defines() — 0 callers, 6 callees
  • new: test_a_parked_call_binds_to_the_one_declaration_in_another_repo() — 0 callers, 6 callees
  • new: test_a_repo_that_stops_declaring_the_type_loses_the_edge() — 0 callers, 6 callees
  • new: test_the_definition_answers_before_a_same_named_declaration() — 0 callers, 6 callees
  • new: test_two_repos_declaring_the_same_name_bind_nothing() — 0 callers, 6 callees
  • …and 1 more — each is listed as a finding

Verification — 222 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 49 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

4 of 276 test file(s) selected (1%) via static blast radius.

  • tests/test_cross_repo_external_call_guards.py — impact, changed-test
  • tests/test_cross_repo_member_calls.py — impact
  • tests/test_global_add_tag_inference.py — impact
  • tests/test_global_graph.py — impact

Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.

Formal verification

No difference found (not proven): No behavior difference found in link\_cross\_repo\_member\_calls (not a proof).

The verifier ran both versions of link\_cross\_repo\_member\_calls on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

· 9 more finding(s) on lines outside this diff (see the check run).

@safishamsi

Copy link
Copy Markdown
Collaborator

Shipped in v0.9.62 (now on PyPI: uv tool install graphifyy==0.9.62). Your commits were cherry-picked with authorship preserved, so this shows up under your GitHub contributions. Thanks @DevChiniwala!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

merge-graphs: a third-party receiver type binds to a same-named repo-local declaration, fabricating cross-repo calls edges

3 participants