Skip to content

v3.0.0 — The Right Node

Latest

Choose a tag to compare

@Anandb71 Anandb71 released this 10 Aug 21:09
· 1 commit to main since this release
335c03f

v2.6.0 stopped dropping colliding symbols. It did not stop resolving them to the wrong one.

What was wrong

When a bare name matched definitions in several modules, resolve_ref fell through to SameDir and attached the edge to whichever definition happened to sit in the caller's own directory. Not a dropped edge — a confidently misrouted one, stamped at 0.55 confidence.

On a fixture with ten layers each defining value_m14, every caller in l1/ resolved to l1/m14.py rather than the l0/m14.py it had actually imported. The intended target ended with no callers at all while an unrelated sibling inherited its centrality.

Measured

260 modules across 10 layers, each layer reusing the same 26 function names. Ground truth is derived from the generator's own edge list, so the expected answer is exact rather than estimated.

True downstream v2.6.0 v3.0.0
179 0 163
178 0 161
161 0 133
143 22 133
122 22 119
36 22 61
16 22 46

Previously flat at about 22 regardless of the real answer. Now it tracks. Risk on the largest hub moves from LOW to CRITICAL.

Total edge count barely moves (1335 → 1334) — the signature of misrouting rather than loss. The edges were always there, pointing at the wrong nodes.

Fixture: getArbor-dev/arbor-torture

The change

GraphBuilder already kept a per-file import map, but only apply_import_validation read it, and that scores an edge after one has been chosen — so it never saw the references going to the wrong node.

Consulting it between the same-file and same-directory checks keeps a local definition shadowing an import, while letting a written import beat mere adjacency. Resolution::ViaImport scores 0.93, above SameDir's 0.55.

Breaking

  • Resolution gains a ViaImport variant — an exhaustive match will not compile
  • Edges land on different nodes, so cached graphs, stored node ids, and centrality baselines from 2.6.0 will differ

Known and still open

  • Small targets now over-report (36 → 61, 16 → 46). Safer direction than silence, but not correct.
  • PageRank has no escape from a closed cycle. All 500 members of a 500-function ring score above 90% centrality on one caller each, so mutually recursive clusters crowd the top of any ranking.
  • Inheritance produces no edges. class Middle(Base) is invisible, so changing a base class shows zero blast radius.
  • Dynamic and reflective imports are unresolvable by construction and are documented as expected misses in the fixture rather than counted as defects.

Full workspace tests green on Linux, macOS and Windows. 149 tests in arbor-graph, four of them new — including one that pins the misrouting itself, so a regression fails loudly instead of quietly returning a wrong number.

🤖 Arbor MCP Quick Install

Use Arbor as a local MCP server in your AI client:

Claude Code (project-scoped)

claude mcp add --transport stdio --scope project arbor -- arbor bridge
claude mcp list

In Claude Code, run:

/mcp

Cursor MCP config (.cursor/mcp.json)

{
  "mcpServers": {
    "arbor": {
      "command": "arbor",
      "args": ["bridge"]
    }
  }
}

VS Code MCP config (.vscode/mcp.json)

{
  "servers": {
    "arbor": {
      "type": "stdio",
      "command": "arbor",
      "args": ["bridge"]
    }
  },
  "inputs": []
}

MCP Directories