GRAPH.md — navigate the code by graph, not by fan-out
GRAPH.md — navigate the code by graph, not by fan-out
The standard for the cheapest way to answer a question about code: ask the graph first. Before an agent (or a person) fans out a wide grep, reads five files whole, or spawns a search subagent to answer "where is X", "what calls Y", "how does Z connect to W", it queries a precomputed code graph and gets back a small, scoped subgraph instead of a pile of raw source. The point is retrieval discipline: pull the few nodes the question needs, not the whole neighbourhood, so the context window holds signal instead of scroll.
Split of responsibility:SESSION-LOOP.mdowns model economy (which brain runs which step, §6) andLOOP.mdowns the mechanical heartbeat (what fires on push and at session start). This file owns one narrow thing they both lean on: how a code question is answered without burning the window. When they overlap, defer to them on cadence and model choice; this file wins only on "reach for the graph before you reach for grep."
The architecture in one line: the graph is a committed-tool artifact built from the code by static analysis (no model, no API cost), refreshed on every edit by a hook, and queried by symbol name. It is a retrieval index, not a source of truth: the code is the truth, the graph is the fast way in.
1. The rule (graph-first for structural questions)
Any question about structure goes to the graph before it goes to fan-out. Structural means: where a symbol is defined, what calls or imports it, the shortest path between two parts of the system, which files cluster together, what the load-bearing hubs are. For those, a graph query returns a handful of file:line nodes and the edges between them. The alternatives cost far more window:
| Question | Fan-out cost | Graph cost |
|---|---|---|
| "Where is this wired, what touches it" | Wide grep, then read several files whole | One query, a scoped subgraph of file:line nodes |
| "How does A reach B" | Read the call chain by hand across files | One shortest-path query |
| "What are the core abstractions here" | Read the whole tree to build a mental map | The graph's hub and community summary |
The win is not that grep is slow, it is that grep hands back everything that matched and the reader pays window for all of it. The graph hands back the few nodes that answer the question and the reader pays for those. On a large repo that is the difference between a readable answer and a scrolled one.
2. Query by symbol, not by prose (the empirical rule)
The one operating lesson, learned the hard way: query with names the code actually contains, not with an English description of the concept. A symbol or filename lands on the right nodes; a fuzzy phrase lands on documentation headings and misses the code entirely.
- Good: the function, type, file, or route name. It resolves to real
file:linenodes and their edges. - Poor: "how does the login flow sanitise input". The phrase matches prose, not symbols, and returns
section titles instead of the handler you wanted.
If you only know the concept and not the name, spend one cheap read to find a name (a route string, an exported function), then let the graph expand from it. Seed with a symbol; let the graph do the reach.
3. Keep it fresh, for free (the hook)
A stale graph is worse than none: it answers confidently about code that moved. Two rules keep it honest.
Refresh on every edit, mechanically. Each repo carries a PostToolUse hook in .claude/settings.json that re-extracts the graph after any file edit. The extraction is static analysis only, so it costs no tokens and no API call and finishes in about a second. The hook is guarded: if the tool is not installed it is a silent no-op, so the repo stays portable.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit|NotebookEdit",
"hooks": [
{
"type": "command",
"command": "command -v graphify >/dev/null 2>&1 && nohup graphify update \"$CLAUDE_PROJECT_DIR\" >/dev/null 2>&1 &"
}
]
}
]
}
}Never commit the artifact. The generated graph output directory is git-ignored in every repo. It is a derived index, rebuilt on demand from the code; committing it would be committing a photograph of a moving thing, and the merge conflicts alone would earn their own standard. The tool, the hook, and this rule are what travel between repos; the graph itself is always local and always freshly built.
4. When not to reach for it (honest limits)
The graph is a structural index, and outside that job it earns nothing.
- Prose and intent questions. "Why was this decided" lives in
docs/,decisions/, and the commit
history, not in a call graph. Reach for those.
- Tiny repos. A few hundred lines is faster to read whole than to index. The graph pays off when the
tree is large enough that a mental map costs real reading.
- Semantic freshness on non-code files. The free refresh re-extracts code. Doc and image changes
need a fuller rebuild, which is a deliberate, occasional step, not the per-edit hook.
- Trusting it as truth. Inferred edges carry a confidence score and can be wrong; isolated nodes may
mean a real gap or just a missing edge. Treat a graph answer as a strong lead to verify in the source, never as the last word. The code is the truth.
If the question is not structural, or the repo is small, skip the graph and read. Retrieval discipline means picking the cheapest tool that answers this question, and sometimes that tool is your eyes.
5. Adoption checklist
Mirrors the shape in AI-REPO-STANDARD.md: one small kit addition per repo.
Day one (a few minutes):
- [ ] Add the
PostToolUsehook from §3 to the repo's.claude/settings.json(merge it in if the file
already has hooks, do not clobber existing ones).
- [ ] Confirm the graph output directory is git-ignored.
- [ ] Build the graph once so day-one queries work. The same refresh command bootstraps from nothing.
Steady state:
- [ ] Structural questions go to the graph before grep, reads, or a search subagent.
- [ ] Queries are seeded with symbol and file names, not English descriptions (§2).
- [ ] The graph is trusted as a lead, verified in source, and never committed.
The proof that this landed: an agent answering "what touches this" pulls back five nodes and a diagram instead of five files and a scroll, and the window it saved goes to the actual work.
Living document. When the retrieval workflow changes, update this file. The graph tooling is a means, not the point: if a better index arrives, this standard describes the discipline, and the tool is swapped underneath it.