Skip to content

fix: preserve edge direction in merge-graphs - #2271

Open
hopstreax wants to merge 1 commit into
Graphify-Labs:v8from
hopstreax:fix/2261-merge-graph-edge-remap
Open

fix: preserve edge direction in merge-graphs#2271
hopstreax wants to merge 1 commit into
Graphify-Labs:v8from
hopstreax:fix/2261-merge-graph-edge-remap

Conversation

@hopstreax

Copy link
Copy Markdown
Contributor

Summary

Fixes #2261.

graphify merge-graphs could silently rewrite the direction of relationship edges when merging persisted graph.json files. Although the merged graph preserved the correct node and edge counts, directional relationships such as imports and imports_from could be reversed, producing incorrect dependency information (for example, a file appearing to import itself instead of the intended module).

This change preserves the original edge direction throughout the merge process while keeping the merged graph format unchanged (directed: false), matching the existing merge-graphs contract.

Root Cause

Persisted graph.json files are stored as undirected graphs ("directed": false). During merge-graphs, they were loaded into an undirected nx.Graph, which does not preserve edge orientation. When the merged graph was serialized back to JSON, NetworkX emitted edge endpoints according to its internal ordering, causing some directional relationships to be written with swapped source and target values.

Solution

  • Preserve directional edge metadata during the merge workflow.
  • Update prefix_graph_for_global() to remap directional edge metadata alongside prefixed node IDs, ensuring internal consistency after relabeling.
  • Restore the original edge direction before writing the merged graph back to disk.
  • Document the helper's behavior to reflect the updated contract.

Tests

Added regression coverage for Issue #2261 by verifying that:

  • import edge direction is preserved after merge-graphs,
  • repo prefixes are still applied correctly,
  • node and edge counts remain unchanged,
  • import edges are not rewritten into self-imports or reversed relationships.

Also added a focused unit test for prefix_graph_for_global() to verify that directional edge metadata is correctly updated when node IDs are prefixed.

Validation

Ran:

uv run pytest tests/test_merge_graphs_cli.py -v
uv run pytest tests/test_global_graph.py -v

Both test suites pass successfully.

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

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

Graphify reviewed this change.

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


Graphify review — findings

This PR modifies edge direction handling in the global graph merge flow. In prefix_graph_for_global (build.py), it extends the node-relabeling logic to also rewrite the _src/_tgt directional edge attributes to use the newly prefixed node IDs. In the merge-graphs CLI dispatch (cli.py), it adds logic to stash _src/_tgt markers when loading graphs into an undirected graph and to restore original source/target from those markers when serializing the merged output. The changes add two new tests: one asserting that prefixed graphs update directional edge attributes, and one end-to-end test of merge-graphs covering node prefixing, counts, and edge direction preservation across two repos.

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

Analysis details — impact, health, verification

Impact & health

graphify review

Impact — 769 function(s) in the blast radius of 139 changed node(s).

Health — grade B; 10 existing offender(s) in the touched area:

  • [high] _rebuild_code() (Ca=55 Ce=48)
  • [high] build_from_json() (Ca=138 Ce=13)
  • [high] dispatch_command() (Ca=2 Ce=109)
  • [high] build_merge() (Ca=23 Ce=7)
  • [high] main() (Ca=79 Ce=2)
  • [high] generate() (Ca=29 Ce=5)
  • [high] to_obsidian() (Ca=24 Ce=6)
  • [high] _make_graph() (Ca=24 Ce=6)
  • [high] to_wiki() (Ca=33 Ce=4)
  • [high] diagnose_extraction() (Ca=12 Ce=9)

Verification — 769 function(s) need re-proving (callee-first):
graphify_build_edge_data → graphify_wiki_god_node_article → graphify_wiki_community_article → graphify_wiki_to_wiki → tests_test_wiki_test_wiki_special_characters_in_label_resolve → tests_test_wiki_test_wiki_links_use_collision_suffixed_slug → tests_test_wiki_test_wiki_links_to_nodes_without_articles_are_plain_text → tests_test_wiki_test_wiki_links_resolve_to_real_files → tests_test_wiki_test_wiki_link_with_bracketed_label_resolves → tests_test_wiki_test_wiki_link_display_keeps_label_but_target_is_filename …

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not enforced this run (re-run with --prove to verify the blast radius)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 712 function(s) in blast radius — re-run with --prove to formally verify them

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 silently rewires import edges to the importing file's own node

1 participant