Writing /Scry /2026-09-25

What Can a Knowledge Graph Tell You That Grep Can't?

Grep finds matching text. A project graph can connect a failing endpoint to its handler, database table, commit history, coverage, and owner without making the agent reconstruct every relationship from files.

Written by
Read time
5 minutes
Topic
Scry

I still use rg constantly. Scry hasn’t made text search obsolete, and I don’t think it should try.

Text search answers a precise question: where do these bytes appear? It is fast, transparent, and great for error messages, configuration keys, prose, and code that no indexer understands.

The trouble starts when the question is about a relationship rather than a string.

“What connects this endpoint to the payments table?” isn’t one search. Neither is “who knows this subsystem?” or “what should I test after changing this function?”

An agent can answer those questions with grep and file reads. I have watched agents do it. They open a controller, chase three imports, inspect a migration, forget the original question, and use a pile of context to recover relationships the project already contains.

Consider a method named save. A large application may contain hundreds of calls with that spelling. Searching for save( returns models, repositories, cache clients, test doubles, framework code, and comments.

A symbol index knows the containing type. UserRepository::save can resolve to references for that method instead of every save token.

This is about as simple as a graph query gets. The nodes are symbols. Definitions and references provide the edges. Callers, callees, and implementations are traversals over the same indexed structure.

Grep remains the fallback when the index is missing a language or the thing being searched is inside a string. Scry reports stale, empty, and partial indexes so an agent doesn’t mistake silence for proof that a symbol is unused.

Cross-domain paths do more work

Scry’s repository graph contains nodes from code, git, schema, and observed HTTP traffic. It derives edges such as:

function -> calls -> function
class -> implements -> interface
function -> queries -> table
table -> foreign_key -> table
function -> serves -> endpoint
function -> authored_by -> author
commit -> migrates -> table
function -> tested_by -> test

Now the endpoint question can become a path:

/api/orders
  -> OrderController.store
  -> OrderService.create
  -> orders table
  -> customers table

The agent receives the nodes, edge types, and confidence. It can open OrderService.create once it knows that is the relevant implementation.

A pile of grep results could contain all the same raw evidence. The path is the part grep doesn’t provide.

Git relationships expose hidden coupling

Imports describe explicit dependencies. Git history can reveal dependencies maintained by convention.

Scry’s co-change index counts files that appear together in commits. A mobile API type and a backend response serializer may live in separate repositories or have no direct import relationship. If they repeatedly change together, that pattern is worth showing an agent before it edits one side.

This edge is probabilistic. Files can change together because a developer happened to bundle unrelated work. Scry records the derivation rather than presenting co-change as a compiler fact.

Grep has no useful way to answer that question.

Runtime traffic corrects static assumptions

Framework routing can be dynamic. Configuration can redirect requests. A frontend may call an endpoint that static analysis doesn’t connect cleanly to its handler.

Captured HTTP traffic gives Scry observed route nodes. The proxy records the request, response, status, and timing. A graph build can connect runtime evidence to handler functions where the framework information is available.

This turns “which endpoint failed?” and “what code serves it?” into adjacent questions. The agent can inspect the actual payload before changing validation logic based on a guessed request shape.

Grep is useful after that. Search the exact error string or configuration key once the graph has narrowed the subsystem.

Graph reports replace the blind opening move

When an agent enters an unfamiliar repository, its default behavior is usually to list files, read the README, and search for likely entry points.

Scry precomputes a graph report with high-degree nodes, architectural communities, cross-domain edges, and suggested queries. It isn’t a generated architecture document. The communities come from graph structure, currently using Louvain clustering, and labels come from the paths and nodes inside them.

The report can be rough. On a small Go service it may identify file clusters more clearly than domain names. It still gives the agent a map based on actual connections before it picks files to read.

I normally use the graph report first, run one or two targeted relationship queries, and then open source files. The source is still where the implementation lives.

Where grep wins

Scry doesn’t index everything.

Text inside comments and documentation belongs to text search. Dynamically constructed method names may not appear in SCIP relationships. Go call edges are less complete than TypeScript because of differences in enclosing-range data. A repository can be stale or partially indexed after an indexer failure.

rg also has essentially no setup. Scry needs an index and a running local daemon. For a tiny repository or a one-off string search, graph traversal would be ceremony.

The tools answer different classes of question:

QuestionBetter first tool
Where does this error text appear?rg
Who calls this method?Scry references or callers
What connects this route to this table?Scry graph path
Where is this phrase used in documentation?rg
Which files change with this one?Scry co-change
Is this exact config value present?rg

I built Scry because agents were using text search for all six. I still reach for rg every day, just not when the question is really about a path through the system.

Start a project conversation

Email Jeff

Write your note here, then open it in your email app. This site sends nothing: your email app opens with this draft and you press send there. No email app? Copy the address or the draft instead.

Open in your email app ↗

To jeff@hooton.codes · Closing this window discards the draft.