Measured
Fixture: getArbor-dev/arbor-torture, file evil/ring500.py — 500 functions in a closed call ring, one caller each.
At engine v3.0.0:
500 of 500 ring members score above 90% centrality
best rank: #28 of 22,330 nodes
Every member of a closed cycle lands near the maximum. The 2-node circular import in app/utils/circular_{a,b}.py shows the same thing at #3–#4.
Why it matters
PageRank has no damping escape from a cycle with no outbound edges, so rank accumulates inside it. Mutually recursive clusters are ordinary in real code — recursive-descent parsers, AST and tree walkers, state machines, visitor patterns. Any repository containing one will have that cluster crowd out genuinely load-bearing symbols, and the PR comment's "High-Risk Symbols" section fills with cycle members.
Suggested fix
Either a rank-sink correction, or collapse each strongly-connected component to a single node, rank that, and distribute the score back across members.
Reproduce
analyze-local /path/to/arbor-torture --top 25000 > out.txt
python grade.py out.txt # check F
Measured
Fixture:
getArbor-dev/arbor-torture, fileevil/ring500.py— 500 functions in a closed call ring, one caller each.At engine v3.0.0:
Every member of a closed cycle lands near the maximum. The 2-node circular import in
app/utils/circular_{a,b}.pyshows the same thing at #3–#4.Why it matters
PageRank has no damping escape from a cycle with no outbound edges, so rank accumulates inside it. Mutually recursive clusters are ordinary in real code — recursive-descent parsers, AST and tree walkers, state machines, visitor patterns. Any repository containing one will have that cluster crowd out genuinely load-bearing symbols, and the PR comment's "High-Risk Symbols" section fills with cycle members.
Suggested fix
Either a rank-sink correction, or collapse each strongly-connected component to a single node, rank that, and distribute the score back across members.
Reproduce