## Code Graph (Phase 1 Step 2.6 + Phase 7 Step 3)

A deterministic, LLM-free map of what a repo declares and what refers to what,
written to `~/.claude/knowledge/<project>/code-graph.json`. Gated by
`prefs.global.codeGraph.enabled` (default `false`); with it off, Phase 1 and
Phase 7 behave exactly as they did before. Design, trade and measurements:
`docs/adr/0010-own-code-graph.md`.

### Phase 1 Step 2.6  -  query before dispatching Explore

Runs only when a rule file exists for `detectedStack`. A stack with no rule file
is reported as unsupported, never guessed at.

```bash
GRAPH_PATH="$HOME/.claude/knowledge/$(basename "$PROJECT_ROOT")/code-graph.json"

node $HOME/.claude/scripts/graph-report.mjs --graph "$GRAPH_PATH" --status
node $HOME/.claude/scripts/graph-build.mjs --root "$PROJECT_ROOT" --stack "$STACK" \
  --max-nodes "${prefs_codeGraph_maxNodes:-200000}" --json
node $HOME/.claude/scripts/validate-code-graph.mjs "$GRAPH_PATH"
node $HOME/.claude/scripts/graph-query.mjs "$TASK_TITLE $TASK_DESCRIPTION" \
  --graph "$GRAPH_PATH" --budget "${prefs_codeGraph_queryBudget:-2000}" --json
node $HOME/.claude/scripts/graph-affected.mjs "<symbol the task names>" \
  --graph "$GRAPH_PATH" --depth 2 --json
```

`GRAPH_PATH` is derived once and passed to every call. The query, affected and
report scripts default it from the CWD's basename, and a run happens in a
worktree whose basename need not equal the repo's, so a defaulted path can point
at a graph that was never built. Pass it.

Build when there is no graph at all, and when `baseCommit` no longer matches
HEAD. The missing case is the one that matters in practice: `architecture.md`
and its siblings are written in Phase 7, which is the phase a run is least
likely to reach, so a repo can have a long history of tasks and an empty
knowledge directory. The graph must not inherit that. `--status` exits 1 on a
missing file, and that exit means build, not skip.

A build is not usable until
`validate-code-graph.mjs` exits 0: a graph whose edges point at missing nodes
truncates traversals silently, so a non-zero exit means skip the injection and
run Explore as if no graph existed.

`graph-query` output becomes the Explore agents' starting file set;
`graph-affected` output feeds `analysis.touchedAreas[]`.

Use it to narrow an open-ended search, not to replace a grep for a name the task
already spells out. Measured on a 4,300-file Swift app at a fixed 30k retrieval
budget, it roughly doubled coverage at under half the cost on domain-word
questions and lost narrowly to `grep -lw` on exact type names.

### Phase 7 Step 3  -  refresh after the branch changed code

Runs when `prefs.global.codeGraph.enabled` and `prefs.global.codeGraph.autoRefresh`
are both true.

```bash
GRAPH_PATH="$HOME/.claude/knowledge/$(basename "$PROJECT_ROOT")/code-graph.json"

node $HOME/.claude/scripts/graph-build.mjs --root "$PROJECT_ROOT" --stack "$STACK" \
  --max-nodes "${prefs_codeGraph_maxNodes:-200000}" --json
node $HOME/.claude/scripts/validate-code-graph.mjs "$GRAPH_PATH"
node $HOME/.claude/scripts/graph-report.mjs --graph "$GRAPH_PATH"
```

The rebuild costs no API tokens, so it runs every task rather than on a staleness
heuristic. A non-zero validator exit keeps the previous graph and logs
`knowledge.graph_invalid`; it never fails the run  -  a stale graph is a degraded
Phase 1, not a broken deliverable.
