DEV Community

Cover image for Codebase Knowledge Base (11): Cross-Repo Scenarios — When One Service Calls Another
WonderLab
WonderLab

Posted on

Codebase Knowledge Base (11): Cross-Repo Scenarios — When One Service Calls Another

An Experiment That Returned Zero

Running cross-repo-intelligence across LightRAG and graphrag:

index_repository(
    repo_path="/path/to/LightRAG",
    mode="cross-repo-intelligence",
    target_projects=["mnt-hdd-...-graphrag"]
)
Enter fullscreen mode Exit fullscreen mode

Output:

status: success
cross_http_calls: 0
cross_async_calls: 0
cross_channel: 0
cross_grpc_calls: 0
total_cross_edges: 0
elapsed_ms: 109
Enter fullscreen mode Exit fullscreen mode

Zero cross-repo edges.

The first instinct is 'the tool found nothing — something failed.' That's wrong. This result is entirely correct, and it's highly informative.

LightRAG and graphrag are two open-source frameworks that do similar things — both graph-based RAG — but they are parallel alternatives, not an integrated system. No LightRAG code calls graphrag's API, and vice versa. cross-repo-intelligence finds integration relationships, not functional similarity. There was never an integration relationship between them; returning 0 is the right answer.

This matters because it draws a clear boundary: cross-repo analysis solves a completely different class of problem than single-repo analysis.


Single-Repo vs Cross-Repo: Two Different Problems

Looking back at the first ten articles, all the questions a single-repo knowledge base can answer live within one repository boundary:

  • 'Where is this function?' (symbol path)
  • 'Who calls it?' (graph path, inbound)
  • 'What does it call?' (graph path, outbound)
  • 'Which functions frequently change together?' (FILE_CHANGES_WITH)

These answers are all in the same codebase. The call graph is complete. BFS from any node works.

But when system scope expands across multiple repositories, a class of questions appears that single-repo analysis physically cannot answer:

  • 'Which interfaces of Service B does Service A call?'
  • 'If I modify Service B's /query endpoint, which upstream services are affected?'
  • 'Service C changed the message format it sends to Service D via message queue — can D detect this?'
  • 'In the entire microservice network, who are the central nodes, who are the islands?'

These answers span repository boundaries. A single-repo call graph stops dead at the service boundary — the graph becomes a map with 'dangling terminal nodes' that call some external URL, but don't know who handles that URL.

cross-repo-intelligence connects those dangling nodes.


How Cross-Repo Edges Are Discovered

The core logic of cross-repo-intelligence mode: match Route nodes against HTTP_CALLS nodes across indexed projects.

Concretely:

  1. Discover server-side routes: Scan all indexed projects for Route nodes (HTTP endpoint definitions). Build a path → project routing table.

  2. Discover client-side calls: Scan HTTP_CALLS edges (HTTP requests in the code). Extract the target URLs or path patterns.

  3. Path matching: For each HTTP_CALLS edge, look for a matching Route in other projects. When found, create a CROSS_HTTP_CALLS edge that crosses the repository boundary, connecting the caller to the callee.

The same logic applies to:

  • CROSS_ASYNC_CALLS: message queues (Kafka topic names, RabbitMQ exchanges, etc.)
  • CROSS_GRPC_CALLS: gRPC service/method names
  • CROSS_CHANNEL: other named channels (WebSocket channels, Redis pub/sub keys)

This explains why LightRAG × graphrag returns 0: no LightRAG code sends HTTP requests to graphrag's routes, and graphrag doesn't call LightRAG's API. The tool didn't fail — it correctly reported that there is no integration relationship between these two systems.


What Each System's Route Profile Actually Looks Like

Even without cross-repo edges, examining each system's routes reveals their roles immediately.

LightRAG: REST API server

Route nodes (lightrag/api/routers/):
  GET  /query
  POST /query
  GET  /query/stream
  POST /query/stream
  GET  /health
  POST /login
  GET  /auth-status
  GET  /graph/label/list
  POST /documents/paginated
  ...
Enter fullscreen mode Exit fullscreen mode

LightRAG exposes a full HTTP service interface — document management, querying, graph browsing, health checks. From the cross-repo perspective, it's a callee: if another service sends requests to http://lightrag-host/query, that request becomes one end of a cross-repo edge.

The corresponding code structure: create_query_routes and create_document_routes are massive route registration functions (1,566 lines, complexity 118, cognitive complexity 266) — among the hardest functions in the project, because they handle all the HTTP boundary cases.

graphrag: LLM client

Route nodes (graphrag/):
  PATCH  /            (API root — to LiteLLM)
  external: https://litellm.ai
  external: https://raw.githubusercontent.com/.../cspell.schema.json
Enter fullscreen mode Exit fullscreen mode

graphrag isn't a service — it's a command-line tool + Python SDK. Its 'routes' are HTTP calls it makes to external LLM services (LiteLLM) and GitHub, not endpoints it exposes. From the cross-repo perspective, it's a pure caller — if you deploy both graphrag and LightRAG in the same system, graphrag might call LiteLLM (creating CROSS_HTTP_CALLS edges), but graphrag won't call LightRAG and LightRAG won't call graphrag.

This role difference is immediately visible from the route profile: one has real REST API routes, the other only has outbound HTTP client calls.


Where Cross-Repo Analysis Has Real Engineering Value

If LightRAG × graphrag has no cross-repo edges, what kind of system does?

A typical microservice architecture:

Frontend App
  └─ CROSS_HTTP_CALLS → API Gateway (gateway-service)
                              └─ CROSS_HTTP_CALLS → User Service
                              └─ CROSS_HTTP_CALLS → Auth Service
                              └─ CROSS_HTTP_CALLS → Order Service
                                    └─ CROSS_ASYNC_CALLS → Kafka: order.created
                                                                └─ CROSS_ASYNC_CALLS → Notification Service
Enter fullscreen mode Exit fullscreen mode

In this architecture, if User Service's GET /users/{id} interface is being modified (say, a returned field is being removed), cross-repo analysis tells you which services have CROSS_HTTP_CALLS edges pointing to that route — and need to be updated in sync. Pure single-repo analysis can't answer this at all.

cross_service mode trace_path:

trace_path(
    function_name="get_user_by_id",
    project="user-service",
    mode="cross_service",
    depth=3
)
Enter fullscreen mode Exit fullscreen mode

This call follows HTTP_CALLS → CROSS_HTTP_CALLS → Route edges across service boundaries, returning the full cross-service call chain — from some frontend handler all the way down to a database query in User Service.


Three Practical Cross-Repo Use Cases

Use case 1: Service dependency map

After indexing all microservice projects, query the cross-repo call graph:

MATCH (a)-[r:CROSS_HTTP_CALLS]->(b)
RETURN a.file_path, r.path, b.file_path
Enter fullscreen mode Exit fullscreen mode

The result is the entire system's service dependency map. Which services are 'hubs' (called by many others), which are 'islands' (no cross-repo calls), whether there are circular dependencies — one query surfaces all of it.

Use case 2: Interface change impact assessment

# 1. Find the interface definition
search_code("GET /api/v2/payments", project="payment-service")

# 2. Find cross-repo callers
trace_path("get_payment", mode="cross_service", direction="inbound")
 returns all upstream services with CROSS_HTTP_CALLS edges to this route
Enter fullscreen mode Exit fullscreen mode

Before modifying /api/v2/payments, know which services depend on it. Without cross-repo analysis, this requires manually grep-ing that URL across every repository in the organization — a process with frequent misses.

Use case 3: Message contract tracing

If services communicate via Kafka:

MATCH (producer)-[r:CROSS_ASYNC_CALLS]->(consumer)
WHERE r.channel = "order.completed"
RETURN producer.file_path, consumer.file_path
Enter fullscreen mode Exit fullscreen mode

Find all producers and consumers of the order.completed message. When the message schema needs to change, the full blast radius is visible immediately.


Zero Cross-Repo Edges Still Has Value: Interface Design Comparison

Back to LightRAG and graphrag — no cross-repo edges, but both indexes exist. Holding two indexed projects simultaneously enables something useful even without integration edges: interface design comparison.

LightRAG's query interface:

# LightRAG
async def aquery(self, query: str, param: QueryParam) -> str
Enter fullscreen mode Exit fullscreen mode

graphrag's query interface:

# graphrag
async def local_search(
    config: GraphRagConfig,
    entities: pd.DataFrame,
    communities: pd.DataFrame,
    community_reports: pd.DataFrame,
    text_units: pd.DataFrame,
    relationships: pd.DataFrame,
    covariates: pd.DataFrame | None,
    community_level: int,
    response_type: str,
    query: str,
) -> tuple[str | dict, str | list[pd.DataFrame] | dict[str, pd.DataFrame]]
Enter fullscreen mode Exit fullscreen mode

Two frameworks doing the same category of thing, with radically different interface philosophies. LightRAG encapsulates all configuration in QueryParam (designed for a service runtime). graphrag expects the caller to manage every DataFrame directly (designed for a batch analytics pipeline).

These interface designs reflect completely different target use cases — and this kind of cross-project interface comparison is one of the most valuable analyses for technology selection decisions. It's also a side-effect of having both projects indexed at the same time, without requiring any cross-repo edges.


Summary

Cross-repo analysis isn't 'single-repo analysis made bigger.' It solves a genuinely new class of problem: connecting knowledge at service boundaries.

Three core conclusions:

  1. Zero cross-repo edges is a meaningful answer: it says two systems have no integration relationship — not that the analysis failed. Don't conflate 'the tool found something' with 'the tool worked correctly.'

  2. Cross-repo analysis works by route matching: it matches Route nodes exposed by one service against HTTP_CALLS / ASYNC_CALLS edges from another. That's what it can find, and only that. Functional similarity, code style comparison, duplicate logic detection — those are different tools' jobs.

  3. Projects with no cross-repo edges still benefit from joint indexing: a cross-repo knowledge base lets you query multiple projects simultaneously. Even without call edges, parallel indexes have analytical value — like interface design comparison.

Next article: we turn to knowledge base evaluation — how do you know your knowledge base is 'good enough'? What metrics matter, how to design evaluation datasets, and what production systems should track when Recall@5 is no longer the only metric that matters.



Check out PrimeSkills — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.

Find more useful knowledge and interesting products on my Homepage

Top comments (0)