Skip to content

cross-repo-intelligence returns 0 edges for a byte-identical call/route #523

Open

Description

@azuttre

I may be missing a required input here, so this is partly a question.

Running index_repository with mode=cross-repo-intelligence and an explicit target_projects, where the consumer has an HTTP_CALLS edge whose url_path is byte-identical to a provider's served Route name (/v2/orders/{id} on both sides), returns projects_scanned: 1, total_cross_edges: 0. I could not get the pass to emit a single cross edge under any input I tried, including this identical-string case. On a larger multi-service codebase it also returned 0 across all categories (http/grpc/async/channel).

My actual question: what is the minimum a call and a route need to share to be linked? If the matcher needs more than a matching path (a method, a confidence value, a node property the extractor sets), I could not find it in the docs.

Repro (self-contained): https://gist.github.com/azuttre/21e5e85f7590261721e3af7aa079dbfb → issue-3-cross-repo-zero-edges.sh

Related note, possibly the cause: the route/call extractor did not produce Route or HTTP_CALLS nodes from the minimal gin, FastAPI, and Express services I tried. A @app.get(...) decorator was captured but no route. That is why the repro inserts the nodes directly, to give the matcher unambiguous input. If that minimal-extraction gap is expected (for example it needs resolvable dependencies), I would value a pointer.

Env: v0.8.1, macOS (arm64).

Activity

  1. RithvikReddy0-0 commented on Jun 20, 2026

    @RithvikReddy0-0
    Contributor

    Spent a while reproducing this from a source build. Short version: the matcher is doing what it's supposed to : the zero edges come from two separate things upstream of it, and the one that actually bites you ("0 across all categories on a real codebase") isn't the matcher at all.

    1. Why the repro returns 0

    The matcher never looks at a Route's name. For each HTTP_CALLS edge it pulls url_path + method from the edge props, rebuilds the Route QN as __route__<METHOD>__<url_path>, and looks that up:

    qualified_name = ? AND label = 'Route'   -- with a __route__ANY__… fallback

    Your inserted Route has qualified_name = 'route:/v2/orders/{id}', so the lookup misses even though the name strings are byte-identical. To match by hand the Route would need to be QN'd __route__GET__/v2/orders/{id}.

    2. The extractor is fine for providers

    A real decorator service does produce routes. I indexed:

    @app.get("/v2/orders/{id}")
    def get_order(id): ...

    and got a Route node __route__GET__/v2/orders/{id} plus a HANDLES edge from get_order. The def handler(): pass files in issue-3 have no decorator and no call, so they produce nothing — which is correct, not a bug.

    3. The real cause: consumer HTTP calls get dropped unless the client lib is indexed

    This is the one that matters. On a normal repo, requests.get("http://order-service:8080/v2/orders/123") produces no edge at all not HTTP_CALLS, not even a plain CALLS.

    In pass_calls.c::resolve_single_call, a call only becomes an edge if its callee resolves to a node that already exists in the graph (cbm_gbuf_find_by_qn returns non-NULL). requests is an external dep whose source isn't indexed, so the call is thrown out before it ever reaches HTTP classification.

    Confirmed it by dropping a local requests/__init__.py stub into the repo the HTTP_CALLS edge shows up immediately. That's why a real multi-service codebase, where every HTTP client is a pip/npm dependency, returns 0 across http/grpc/async/channel.

    4. And even once the edge exists, a second mismatch

    When an HTTP_CALLS edge is created, its url_path is the full URL (http://order-service:8080/v2/orders/123), but the provider Route is the bare templated path (/v2/orders/{id}). The matcher does an exact-string QN lookup — no host stripping, no path-param normalization so 123 vs {id} and the scheme/host/port both block the match.


    So the minimum to link two services today is pretty narrow: a consumer HTTP_CALLS edge whose url_path is byte-identical to the provider Route path, plus a provider Route QN'd __route__METHOD__path with a HANDLES edge. In practice that only lines up if the HTTP client is vendored into the consumer and both sides use identical literal paths.

    Happy to split (3) into its own issue it's the actual blocker for the real-world case and it's independent of the matcher. I can also take a crack at a fix if that's welcome.

  2. azuttre commented on Jun 20, 2026

    @azuttre
    Author

    Thanks, this is a thorough write-up. You're right on the QN: my inserted Route used route:/v2/orders/{id}, not __route__GET__/v2/orders/{id}, so the lookup was never going to hit. That one's on me.

    3 and 4 are the useful part. On the real multi-service codebase I was testing the consumer side did produce HTTP_CALLS edges, so what bit me was 4: the call's url_path carried a different version prefix and param style than the provider route, and the exact QN lookup never matched. Both causes are real, just showing up on different repos.

    Yes, splitting 3 into its own issue makes sense, it's independent of the matcher and it's the one that silently returns 0 on a normal repo. A fix for either would be very welcome, and I'm happy to test whatever you land.

  3. RithvikReddy0-0 commented on Jun 20, 2026

    @RithvikReddy0-0
    Contributor

    Hey @azuttre , so I worked on it, I opened the 536 which is covering the 3 root causes : 1, the Unindexed Client Lib Drops
    2, URL normalization
    3, reverse-direction matching

  4. DeusData commented on Jun 20, 2026

    @DeusData
    Owner

    Thanks guys for the analysis. Will review ASAP

  5. added
    bugSomething isn't working
    parsing/qualityGraph extraction bugs, false positives, missing edges
    on Jun 23, 2026
  6. added a commit that references this issue on Jun 25, 2026
  7. 9 remaining items

  8. DeusData commented on Jul 8, 2026

    @DeusData
    Owner

    v0.9.0 includes project resolution and trace/cross-edge fixes that may affect this cross-repo repro. Could you retest on v0.9.0 with the same byte-identical route/call setup and confirm whether it still returns zero? Marking awaiting-reporter.

  9. added this to the 0.9.1-rc milestone on Jul 8, 2026
  10. removed
    awaiting-reporterWaiting on the reporter for info/repro; stale bot will warn then close
    on Jul 10, 2026
  11. niebloomj commented on Jul 28, 2026

    @niebloomj

    Retesting on v0.9.0 (macOS arm64) as @DeusData requested. Cross-repo still returns 0 across all 8 indexed repos. Adding concrete data points for the three root causes @RithvikReddy0-0 identified — confirms each one and adds a new fourth observation.

    Setup

    • 8 sibling repos, all main branch, fresh mode=full reindex immediately before the cross-repo pass
    • Three provider–consumer pairs that should link:
      • FrumFinanceCore (FastAPI lambdas) ↔ FrumFinanceUI (axios client)
      • FrumFinanceCore (FastAPI lambdas) ↔ SlackApp (urllib calls)
      • FrumFinanceCore (FastAPI lambdas) ↔ SlackTriageApp (httpx, but actually only used for OpenRouter/Slack Web API; RightCapital goes through the Python SDK import, not HTTP)
    • Cross-repo command per repo: cli index_repository --repo-path <P> --mode cross-repo-intelligence --target-projects ["*"]
    • All 8 ran successfully via CLI in 50ms–1s each. No crashes via CLI (the MCP stdio wrapper crashes on the same calls — separate issue, worked around). The behavior is correct, the result is 0.

    Observations matching the three root causes

    (1) External client libs get dropped — confirmed

    FrumFinanceUI ships with 8 HTTP_CALLS edges in the graph, all of which come from tests/e2e/acceptance/*.spec.ts (URL strings in test files). The real API client — src/api/budget.tsx, src/api/auth.ts, etc. — uses axios through src/api/axiosInstance.ts, and none of those calls produce HTTP_CALLS edges. The client-side HTTP_CALLS count is 8/100s of expected. This matches root cause #3 exactly.

    (2) No HANDLES edges on FastAPI handler functions — confirmed, fresh reindex

    Even after a forced reindex (touched auth.py to bust the incremental cache, ran mode=full, confirmed expected_nodes=4332 / expected_edges=19232 against actual 4326 / 19109 — i.e. extraction was re-run), FastAPI handler functions have no HANDLES edge to their Route nodes. Cypher against the live graph:

    MATCH (f:Function {qualified_name: "...lambdas.budget_insights_lambda.api.handlers.user.auth.user_login_handler"})
          -[h:HANDLES]->(r:Route)
    RETURN f, r
    // returns 0 rows

    The Route node /user/login exists, the function user_login_handler exists, but no edge between them. So the provider side of the cross-repo match is also broken, independently of the consumer-side lib-dropping. The handler→route wiring is the gap the #667 fix needs to close.

    The actual Route nodes that DO have HANDLES edges in our graph are coming from test fixtures (tests/advisor/test_network_utils.py calling FastAPI's TestClient("/")), not from the production FastAPI decorators in lambdas/budget_insights_lambda/api/main.py or .../handlers/user/auth.py.

    (3) URL normalization not happening — N/A for us, but a related new finding

    Our client URL strings in tests/e2e/acceptance/*.spec.ts happen to be the same literal /user/budgets, /user/insights as the provider Route node names (FastAPI doesn't use :param style, it uses {param}, and the UI tests use literal concrete paths). So the exact-string QN lookup should work for the 8 edges we have — but it still returns 0, because (per your analysis) the lookup compares against the __route__METHOD__path QN, and the routes created from string-literal URL callsites get QN'd __route__ANY__/user/budgets while provider-side routes are QN'd __route__ANY__/user/budgets too — so the QN does match for these 8, but the matcher isn't using HTTP_CALLS edges in its source set, or there's a second filter.

    (Checking the live graph: the FrumFinanceCore graph contains Route nodes with name /user/login but their qualified_name is __route__ANY__/user/login, and the FrumFinanceUI graph contains Route nodes with name /user/budgets with qualified_name __route__ANY__/user/budgets. They have identical QN shape but live in separate project graphs and are not deduplicated/joined across projects, which is what the cross-repo matcher is supposed to do.)

    (4) New finding: manage_adr is exposed by the binary but missing from MCP tool catalog

    /Users/Jacob/.local/bin/codebase-memory-mcp 0.9.0 --help lists 14 tools: index_repository, search_graph, query_graph, trace_path, get_code_snippet, get_graph_schema, get_architecture, search_code, list_projects, delete_project, index_status, detect_changes, manage_adr, ingest_traces. Only the first 8 appear in the MCP tools/list response to clients. manage_adr(mode='update') (referenced in every index_repository adr_hint) and ingest_traces (the contributor's mentioned workaround for missing edges — currently returns "Runtime edge creation from traces not yet implemented") are unreachable through normal MCP usage. Workaround: codebase-memory-mcp cli manage_adr ... from the shell. Filing this separately if useful; flagging here because the #667 verification workflow may want to call manage_adr programmatically.

    Environment

    • codebase-memory-mcp 0.9.0 (macOS arm64, ui variant, GitHub release archive install)
    • 8 sibling repos, 3,000–4,500 nodes / 4,200–19,000 edges each after full reindex
    • No HANDLES edges on any FastAPI handler function in the lambdas layer
    • HTTP_CALLS only emitted from test files, not from real src/api/*.tsx

    Happy to dump the full query_graph output for any specific node if helpful — the graph is queryable via cli query_graph '{"project": "...", "query": "..."}'.

  12. 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 Sep 5, 2026
  13. DeusData commented on Sep 25, 2026

    @DeusData
    Owner

    Thank you, @azuttre, for the careful repro, @RithvikReddy0-0 for the excellent root-cause breakdown, and @niebloomj for the v0.9.0 data! We retested current main against all of it:

    • External client calls (requests and similar) now produce HTTP_CALLS.
    • FastAPI handlers get HANDLES edges.
    • The matcher links http://order-service:8080/v2/orders/123 to GET /v2/orders/{id}, stripping the host and normalizing the params.

    Two gaps remain. A Python f-string URL (requests.get(f"http://…/{order_id}")) keeps its f"…" wrapper, so it can never match. And a prefix given via include_router(..., prefix="/user") isn't composed into the route path. We'll track both as focused follow-ups. @RithvikReddy0-0, you'd be very welcome to take either one if you're interested. The axios-instance case is covered by fixes in review, and manage_adr is exposed in the MCP tool list on v0.11.0.

  14. DeusData commented on Oct 25, 2026

    @DeusData
    Owner

    Thank you for the detailed reproductions and the later retest. The last maintainer update leaves f-string URLs and composed router prefixes as focused follow-ups, alongside the axios-instance work. I have no newly verified resolution for those remaining cases; no repeat of the existing evidence is needed. Sorry for the delay—this is waiting on maintainer follow-up, and I’ll follow up soon.

  15. DeusData commented on Oct 28, 2026

    @DeusData
    Owner

    Progress on this issue: #2774 by @RithvikReddy0-0 is merged as 1f934b6. Python string URLs written with a prefix (r"", b"", u"", an f"" with no field) or with triple quotes now fold to the bare URL, so calls like requests.get(f"http://svc/v1/items") can match their route. The issue stays open for the remaining cross-repo cases.

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