Skip to content

get_architecture hotspots report inflated fan_in that contradicts actual graph in-degree (same-name collision across languages) #725

Closed

Description

@martingarramon

Version: v0.8.1 (linux-amd64)

Summary

get_architecture reports a hotspot fan_in that does not match the node's real in-degree in the graph. For short identifiers shared across languages (e.g. commit, parse, add), the reported fan_in appears to aggregate by bare name rather than by resolved node — producing large over-counts, and attributing the count to a node that actually has in-degree 0.

Environment

  • Mixed Python + TypeScript/JavaScript monorepo, indexed with mode: full.
  • Two distinct symbols named commit: a JS arrow function commit (defined in a React page component) and a Python method Store.commit.

Observed

get_architecture →

hotspots: [{ name: "commit", qualified_name: "…/web/src/pages/Editor.commit", fan_in: 234 }, …]

But that JS commit has one local call-site, and querying the graph directly shows it has zero inbound edges:

query_graph:  MATCH (n {name:'commit'})<-[r]-(c)
              RETURN n.qualified_name, count(r)
→ only the Python Store.commit, in_degree 20.
  The JS Editor.commit node returns nothing (in_degree 0).

So fan_in: 234 is not reproducible from CALLS/USAGE edges.

Hotspots whose names are class-qualified Python methods matched their real in-degree correctly (a function reported with fan_in: 46 had graph in_degree 46), so the inflation is specific to short bare names shared across the Python/JS boundary.

Expected

Hotspot fan_in should equal the node's resolved inbound CALLS/USAGE in-degree (the value query_graph / search_code already report per node), not a count aggregated by bare name.

Impact

get_architecture is the first-call "understand this codebase" tool; an inflated headline metric on the wrong node is misleading. The qualitative structure (clusters, layers, routes, languages) is accurate — only the hotspot fan_in figures are affected.

Possibly related to #558 (Class in_degree/out_degree = 0) — both are node-level degree-metric correctness issues.

Activity

  1. added
    bugSomething isn't working
    parsing/qualityGraph extraction bugs, false positives, missing edges
    priority/normalStandard review queue; useful PR with ordinary maintainer urgency.
    on Jun 30, 2026
  2. DeusData commented on Jun 30, 2026

    @DeusData
    Owner

    Thanks, this is a useful correctness report. The expected invariant is right: get_architecture hotspots should be node/resolved-symbol based, not bare-name aggregates. I’m tracking it under graph query/reporting correctness; the fix should start with a regression fixture containing same-named Python and JS symbols and assert fan_in matches graph in-degree.

  3. rudi193-cmd commented on Jul 5, 2026

    @rudi193-cmd
    Contributor

    Additional public repro — willow-2.0 (Python monorepo)

    We maintain a fleet structural audit that treats CBM as a discovery instrument and verifies every measurement against source (addendum). Re-checked on v0.8.1, project home-sean-campbell-github-willow-2.0 (~17.8k nodes).

    Case A — get_architecture fan_in matches graph in_degree, but both are inflated by bare-name collapse

    get_architecture hotspot:

    JsonStore.get  fan_in: 789
    ledger.append  fan_in: 672
    

    query_graph:

    MATCH (m:Method) WHERE m.qualified_name CONTAINS 'JsonStore.get'
    RETURN m.qualified_name, m.in_degree
    -- → 789 (matches fan_in exactly)

    So the hotspot metric is internally consistent with node-level in_degree — but that in_degree itself aggregates every .get() / .append()-shaped call in the repo onto one resolved symbol (F-007 in our ledger). Qualitative architecture (clusters, packages) looks fine; hotspot ranking is misleading for common method names.

    Case B — trace_path on bare execute lists cursor.execute callers as inbound to nuke.execute

    Only one Function named execute exists (willow.nuke.execute, in_degree: 66). trace_path(function_name="execute", direction="inbound") returns grove_msg.cmd_send and dozens of scripts as callers.

    Source for cmd_send — calls cur.execute(...) (Postgres cursor), not nuke.execute:

    cur.execute(
        "INSERT INTO grove.messages ...",
        (channel_id, sender, content),
    )

    This is the same bare-name / receiver-blind resolver class as inflated fan-in, seen from the traversal side.

    Links

    Happy to attach a minimal query script if useful for a regression fixture.

  4. added this to the 0.9.1-rc milestone on Jul 8, 2026
  5. anton-matosov commented on Aug 12, 2026

    @anton-matosov

    Another reproduction of this, cross-language but Bash↔Python this time rather than JS↔Python.

    Version: v0.10.2

    Setup: repo mixes Bash and Python. Two distinct mains:

    • .codex/setup-codex-cloud.sh — a Bash script's top-level main
    • py_packages/validate_agent_files/validate_agent_files/main.py:main — a Python CLI entry point, called from ~45 pytest test functions (test_cli_*, test_issue310_*, etc.)

    Observed: get_architecture → hotspots reported:

    workspaces-agent-devcontainer-wortree-2.codex.setup-codex-cloud.main   fan_in: 46
    

    But trace_path(function_name=".codex.setup-codex-cloud.main", direction="inbound", depth=1) shows real callers_total: 1 (just the file itself invoking its own script).

    Querying inbound edges directly onto that qualified node:

    MATCH (a)-[r]->(b:Function {qualified_name: "workspaces-agent-devcontainer-wortree-2.codex.setup-codex-cloud.main"})
    RETURN type(r), a.qualified_name LIMIT 50

    returns the ~45 pytest test_cli_*/test_issue310_* CALLS+TESTS edges that actually target the Python main, not the Bash one — confirming the bare-name aggregation described in this issue, just across a different language pair (Bash/Python instead of JS/Python).

    Same workaround as the OP: trace_path/direct query_graph on the exact qualified name gives the correct in-degree; only the hotspots aspect of get_architecture is affected.

  6. DeusData commented on Aug 12, 2026

    @DeusData
    Owner

    Thank you @anton-matosov. The v0.10.2 Bash/Python reproduction confirms that this resolver class persists beyond the original JavaScript/Python case. Current unique_name resolution is project-wide and language-agnostic, so wrong CALLS edges can inflate a node's in-degree before get_architecture ranks hotspots.

    #1572 now provides a small Python-to-TypeScript instance of the same root behavior, while #1555 covers the multi-candidate receiver case. They are related rather than exact duplicates, so all remain linked under #594.

  7. rudi193-cmd commented on Aug 13, 2026

    @rudi193-cmd
    Contributor

    @DeusData I can pick this up, and finish up #876 , which has been on my list for too long.
    I use this all the time, and would like to give more back.

  8. added
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    and removed
    priority/normalStandard review queue; useful PR with ordinary maintainer urgency.
    on Aug 13, 2026
  9. DeusData commented on Aug 13, 2026

    @DeusData
    Owner

    @rudi193-cmd, thank you for volunteering to take this on. That would be welcome. I have raised #725 to priority/high: the fresh v0.10.2 Bash/Python reproduction confirms that the cross-language, bare-name resolver can still attach real edges to the wrong node and then inflate architecture hotspots.

    Please keep #725 and #876 as separate PRs, with one reproducer and one claim per change. Before starting the resolver change, compare with #1572 and #1555 so the fixes do not compete or encode different ambiguity rules. A focused red case that distinguishes two same-named symbols across languages is the most useful first slice.

    Thank you again for offering to give back on a path you use regularly.

  10. DeusData commented on Aug 17, 2026

    @DeusData
    Owner

    Fixed by #1647 (89f0cd4), landing in the next release: suffix_match CALLS edges are no longer created across language-family boundaries (JS/TS/TSX counting as one family), so same-named symbols in different languages stop inflating hotspot fan_in. unique_name cross-language bleed is tracked separately in #1572.

  11. added a commit that references this issue on Aug 17, 2026
    416ba99
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingparsing/qualityGraph extraction bugs, false positives, missing edgespriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions