Skip to content

Simple-Graph build silently drops 471 of 3178 edges (57 pairs differ in relation type); collapse is only reported after graph.json is written #2791

Description

@cdtatarevich

Environment

  • graphifyy 0.9.39, macOS 15, 116-file Python corpus, undirected build (skill default)

What happens

build_from_json produces a simple networkx.Graph, so every additional edge between an already-connected pair is silently dropped. Step 4.5 measures the loss accurately — but only after Step 4 has already written graph.json:

raw_edges: 3425
valid_candidate_edges: 3178
undirected_unique_endpoint_pairs: 2707
undirected_same_endpoint_collapsed_edges: 471
same_endpoint_group_count: 160
relation_variant_groups: 57
source_location_variant_groups: 96
post_build_graph_type: Graph
post_build_edges: 2707

471 edges (~15% of valid candidates) are gone from the artifact, across 160 endpoint pairs. Example from the report:

tests_test_channels_testchannelregistry -> agent_reach_channels_bilibili_bilibilichannel
  edges=7 relations=['uses'] locations=['L1198','L1233','L1252','L1266','L1289','L1299','L13']

Seven distinct call sites become one edge with one source_location. And relation_variant_groups: 57 means 57 pairs collapsed edges that did not even share a relation type — a calls and a references between the same two nodes keep only whichever landed first.

Why it matters beyond tidiness

  • Weight is lost. "Called from 7 places" and "called once" are the same edge afterwards, so anything ranking by coupling strength (god nodes, betweenness, suggest_questions) is computed on a flattened graph.
  • Provenance is lost. source_location for the surviving edge is arbitrary, so "where exactly does A use B" has one answer out of seven, with nothing marking it as partial.
  • The warning is unactionable as written. It says graph may be incomplete/corrupt after the fact; there is no flag to re-run without the loss, so the only response is to accept it.

Suggested fix, in order of cost

  1. Aggregate rather than drop. On collision, keep one edge but set weight (or count) to the number of merged edges and carry source_locations as a list. No API change for consumers that ignore the extra fields, and the diagnostic drops to zero for the common case.
  2. Do not merge across relation types — those 57 groups are a different fact being discarded, not a duplicate.
  3. Offer --multigraph to build a MultiGraph/MultiDiGraph for users who want every edge. Note graphify explain/path still crash on MultiGraph edge access #796 and merge-graphs: unhandled NetworkXError on mixed directed/multigraph inputs, and exit code 0 on crash #1606 show explain/path/merge-graphs have had MultiGraph edge-access bugs before, so this is the expensive option and (1) probably delivers most of the value.

diagnose multigraph already computes everything needed for (1) — the numbers above came straight out of it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions