PlatformFinding things
Code health and dependencies
Trace dependencies, callers, paths, and blast radius; inspect cycles, unused code, complexity, contradictions, drift, scan history, and trends.
what would break if I change this function?
Callers and dependents, from the graph.
show me this project's code health
Circular dependencies, unused code, and complexity hot spots.
is anything still using the legacy client?
Every dependent, or an honest nothing.
You can ask about the change you want to make. The assistant should recognize when a text lookup is not enough and inspect relationships, callers, and blast radius without waiting for you to name the graph operation.
Why a graph and not a search
Text search finds the name. A graph knows the relationship. "What calls this" is a different question from "what mentions this", and only one of them is safe to base a refactor on.
What it reports
Relationships around one change
| Question | What the graph can return |
|---|---|
| What does this depend on? | Direct and, when useful, transitive dependencies |
| What uses this? | Reverse dependencies and every known usage of a module, function, type, constant, or component |
| What extends or implements this? | The types that extend, implement, or hold a type, and the functions that name it |
| How does A reach B? | A path between two graph nodes, or between two pieces of saved knowledge |
| Who calls this function? | A bounded call path |
| What is related? | Everything linked to a decision, plan, task, doc, ticket, or handoff, and the code it names |
| What would this change affect? | Impact and blast radius for a modification, rename, signature change, or deletion |
| Why was this area shaped this way? | The decisions, lessons, tasks, and docs that refer to a file, function, or type |
Project-wide health
| Signal | What it means |
|---|---|
| Circular dependencies | Cycles between modules—the reason a “small” change spreads |
| Unused code | Functions, types, modules, and constants nothing calls, extends, implements, names, or reads |
| Complexity | Long or highly branching functions that deserve inspection |
| Contradictions | Saved knowledge that conflicts with other current context |
| Code drift | Areas where code and the recorded project understanding have diverged |
Using it before a refactor
The order that saves time:
- Search for the exact symbol or area so the target is unambiguous.
- Check reverse usages and blast radius. If it is larger than expected, that is the first finding.
- Trace the important call or dependency path rather than opening every related file.
- Check cycles, drift, and linked decisions in the area.
- Treat unused code as candidates, then verify before deleting.
A refactor with a real blast radius
Suppose you want to change verifyToken so it returns a typed failure instead
of a boolean. An exact source search identifies the symbol; the graph answers
the questions that decide whether the change is small:
- Which middleware, background jobs, tests, and command-line paths call it?
- Is there a call path from the public request handler to the verifier through wrappers whose names never mention tokens?
- Which modules depend on the current return type?
- Does the auth package already participate in a cycle that this change could deepen?
- Is there a linked decision explaining why one caller bypasses the normal middleware?
If the graph shows twelve callers rather than the expected two, split the work or write a plan before editing. After the refactor, rerun the same checks and save a new quality snapshot. The useful comparison is not “the build passed”; it is “all known callers moved, no new cycle appeared, and the baseline did not regress.”
An “unused” verifyLegacyToken result still needs inspection. It may be loaded
by reflection, called through an interface, or used only by an external
consumer. The graph supplies a candidate and its known relationships, not a
guarantee that deletion is safe.
Calls across services
Within one project, a call path does not stop where one service hands work to another. The graph links:
- An HTTP request to the route that serves it. When the method and path are in the source, as a literal or a constant, the request links to the handler registered for them. A base URL read from configuration is fine; the path is what has to match.
- A published event to its consumers. A publish links to the handlers subscribed to the same topic, queue, subject, event class, or job.
- An in-process signal to its listeners, such as an Unreal delegate, a Swift notification, a Laravel event, a Sidekiq job, or a Roblox remote event.
Each path says how every step is made:
Purchase -> Complete -> GrantItems =http POST /inventory/grant=> Grant -> GrantInventory
Purchase -> Complete =event purchase.completed=> OnPurchaseCompleted -> RecordPurchasePaths cross languages too. A TypeScript front end calling a C# API that publishes to a Python worker is one path, and so is a Roblox purchase that ends in a Go inventory service.
The graph does not link:
- Services in different projects. Index the services together to trace across them.
- A route or event name the source never spells out, because it is assembled at run time from configuration or input.
- An ambiguous match. When two routes answer a request equally well, no link is drawn rather than a guess.
- gRPC, GraphQL, and queues bound only in infrastructure configuration.
Types and constants
Besides calls, the graph records how types relate and where constants are read:
- A type extends or implements another. A class and its base class, a class
and the interfaces it implements, a Rust
impl Trait for Type, a Go struct and the types it embeds, a Ruby class and the modules it includes. - A type holds another. A field, property, or constructor property of another project type.
- A function names a type. In its signature or its body, for example by constructing it.
- A function reads a constant. Module-level constants and globals, and
static members, such as Rust
const, a Go packagevar, a Javastatic finalfield, or a C#define.
So “what implements Repo?”, “what breaks if I change Order?”, and “is
MAX_RETRIES still used?” have answers. A name is matched to the type or
constant it means the way the language scopes it: the same file, then the same
directory or package, then what the file imports. When two candidates remain,
nothing is linked rather than a guess.
Why code is the way it is
Ask for the decisions about a file, function, or type, and the assistant returns the saved knowledge that refers to it:
- decisions, lessons, and constraints that record a reference to the code, or name it, its file, or a distinctive symbol name;
- tasks and notes with a reference to it;
- docs that name its file or its qualified name.
Recorded references rank first. Impact analysis lists the team's decisions, lessons, and constraints about its target, so a change shows its reasons along with its blast radius. Personal notes appear only for their owner.
The knowledge graph
Saved knowledge is a graph of its own. From any decision, lesson, plan, task, note, doc, ticket, or handoff, the assistant can list what links to it, and find the chain of links between two of them:
- relations between pieces of knowledge, and which decision superseded which;
- the events a piece of knowledge was learned from;
- the tasks of a plan, and the tickets under an epic;
- what a plan or a handoff lists;
- the code a decision or task refers to.
A walk never passes through an item you cannot see, such as another person's personal note or a project you do not have access to.
Languages
“Full” means functions, calls resolved the way the language scopes them (packages, imports, and the type a method is called on), types with what they extend, implement, and hold, constants with the functions that read them, and file imports.
| Language | Graph | HTTP and events covered by tests |
|---|---|---|
| Go | Full | chi, net/http requests, NATS |
| C# | Full | ASP.NET Core controllers and minimal APIs, HttpClient, MassTransit |
| Java | Full | Spring MVC, OpenFeign, Spring Kafka |
| Kotlin | Full | Ktor server and client, Spring Kafka |
| Python | Full | FastAPI, requests, kafka-python, Faust |
| PHP | Full | Laravel routes, the Http client, Laravel events and listeners |
| Ruby | Full | Rails routes, Faraday, Sidekiq |
| C++ | Full | Unreal IHttpRequest and delegates, cpp-httplib |
| Swift | Full, without file imports | Vapor, URLRequest, NotificationCenter |
| Lua | Full, without types | Roblox remotes, ProcessReceipt, HttpService |
| TypeScript, JavaScript | Calls matched by name | Express, NestJS, fetch, axios, Kafka |
| Rust | Calls matched by name | axum, reqwest, NATS |
| C | Functions, calls, types, constants, and includes | — |
Other frameworks with the same shapes usually link too; the table lists what the test suite proves.
Snapshots and trends
Ask the assistant to save a code-health baseline after a meaningful scan or cleanup. Later scans can show whether cycles and complexity are going up or down over a quarter — which turns "the codebase feels worse" into a number.
There are three different time views:
- Freshness says whether the dashboard cache is current enough to trust.
- History lists saved scan runs and their state.
- Trends compare counts over time.
A saved snapshot makes a deliberate baseline. It does not refresh the source index by itself.
Building and refreshing the graph
The graph is derived after source indexing. A new project or a large batch of changes may need graph ingestion before relationship queries are complete. Check freshness before treating an empty result as proof that no dependency exists.
Repository text remains the authority for a newly edited file that has not been indexed yet.
Believing "unused"
Reflection, dynamic dispatch, and code called only from outside the repository can look unused. So can a type used only through a name the graph could not tie to one definition, or a constant read only by code outside any function. Treat the list as candidates, not a delete queue — check the blast radius of each before removing it.
If it doesn't work
Empty results on a new project. The graph builds after indexing. Give it a few minutes, then rerun.
It missed a caller. Dynamic dispatch and reflection aren't traceable statically. The graph is a strong signal, not a proof.
A call path stops at an HTTP call or a queue. Check that both services are in the same project, that the request's path or event name appears in the source, and that only one route matches it. Then connect the remaining hop with search.
Complexity flags code I'm happy with. It measures shape, not quality. A long switch statement is fine; it just measures long.
A dashboard count differs from the code I just changed. Check freshness and the latest scan history, refresh the index and graph, then compare again.
A diagram disagrees with the graph. The diagram is a deliberately saved model; the graph is derived from indexed code. Verify the current source and update the stale record.