# Ask The Map, Keep It Fresh

> The closing page: remember Graphify as a reusable project map, then use query, path, explain, MCP, global graphs, update, and watch flows to keep that map useful after the first build.

- Repository: safishamsi/graphify
- GitHub: https://github.com/safishamsi/graphify
- Human wiki: https://grok-wiki.com/public/wiki/safishamsi-graphify-af19ef9fd72d
- Complete Markdown: https://grok-wiki.com/public/wiki/safishamsi-graphify-af19ef9fd72d/llms-full.txt

## Source Files

- `graphify/serve.py`
- `graphify/watch.py`
- `graphify/global_graph.py`
- `graphify/affected.py`
- `graphify/prs.py`
- `graphify/security.py`
- `tests/test_query_cli.py`
- `tests/test_watch.py`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [graphify/__main__.py](graphify/__main__.py)
- [graphify/serve.py](graphify/serve.py)
- [graphify/watch.py](graphify/watch.py)
- [graphify/global_graph.py](graphify/global_graph.py)
- [graphify/affected.py](graphify/affected.py)
- [graphify/prs.py](graphify/prs.py)
- [graphify/security.py](graphify/security.py)
- [tests/test_query_cli.py](tests/test_query_cli.py)
- [tests/test_watch.py](tests/test_watch.py)
- [tests/test_path_cli.py](tests/test_path_cli.py)
- [tests/test_explain_cli.py](tests/test_explain_cli.py)
- [tests/test_global_graph.py](tests/test_global_graph.py)
</details>

# Ask The Map, Keep It Fresh

Graphify is easiest to remember as a reusable project map. First it builds `graphify-out/graph.json`; after that, the useful habit is to ask the map before opening the whole codebase, then refresh the map when the code changes.

This page is the closing workflow: use `query`, `path`, `explain`, MCP tools, PR impact, global graphs, `update`, and `watch` as small loops. The goal is simple: keep answers grounded in the graph, but keep the graph cheap and fresh enough that people actually use it.

Source note: this page uses repository code as the source of truth. The requested Compound Engineering profile was applied as page-shaping guidance from the provided bundled snapshot metadata; no local `STRATEGY.md`, `docs/solutions/`, or `graphify-out/` context was present in this checkout.

Sources: [graphify/__main__.py:1365-1444](), [graphify/serve.py:447-569]()

## The Small Mental Model

Think of Graphify like a city map for a codebase. You can ask, "Where is this place?", "How do these two places connect?", "What roads lead into this building?", or "Which neighborhoods changed?" The map is not the city. You still inspect source code before editing. But the map helps you choose the right source files first.

```text
Build once:
  repository files -> graphify-out/graph.json -> report/wiki/views

Use many times:
  query      -> nearby context for a question
  path       -> shortest relationship between two concepts
  explain    -> one node and its direct connections
  MCP        -> the same map exposed to agents
  prs        -> changed files mapped to graph communities
  global     -> many repo maps merged under repo tags

Keep fresh:
  update     -> AST-only code refresh, no LLM needed
  watch      -> automatic code refresh; flag docs/images for semantic update
```

The CLI help makes these workflows explicit: `query`, `path`, `explain`, `affected`, `watch`, `update`, `extract`, and `global` are all first-class commands, not hidden internals. The installed agent instructions also tell assistants to prefer `query`, `path`, and `explain` before broad source browsing when a graph exists.

Sources: [graphify/__main__.py:395-424](), [graphify/__main__.py:1365-1444]()

## Ask The Map First

### Use `query` for "where should I look?"

`graphify query "<question>"` loads a graph JSON file, scores nodes from the query terms, picks seed nodes, and returns a bounded text subgraph. It defaults to `graphify-out/graph.json`, supports `--graph`, supports `--budget`, and can use `--dfs` instead of the default breadth-first traversal.

The important product behavior is that the answer is scoped. Instead of handing an agent the whole repository, Graphify returns matching nodes, source locations, communities, and related edges under an approximate token budget.

Sources: [graphify/__main__.py:1703-1772](), [graphify/serve.py:314-339]()

Example:

```bash
graphify query "who calls extract" --context call
```

The query system can filter by edge context. A user can pass `--context call`, or Graphify can infer a call filter from a question like "who calls extract". The tests check both the explicit and heuristic cases: the `call` edge is kept, while an unrelated `import` edge is excluded.

Sources: [graphify/serve.py:143-185](), [graphify/serve.py:188-202](), [tests/test_query_cli.py:24-51]()

### Use `path` for "how are these connected?"

`graphify path "A" "B"` finds the shortest route between two matched nodes. It uses an undirected view for path finding so the user can ask in either order, but it renders arrows using the stored edge direction. That matters because "A calls B" and "B is called by A" are the same relationship viewed from opposite ends, not the same arrow.

Sources: [graphify/__main__.py:1852-1934](), [tests/test_path_cli.py:36-48]()

Example output shape:

```text
Shortest path (1 hops):
  createPatchHandler() --calls [EXTRACTED]--> validateSanitySession()
```

### Use `explain` for "what is this one thing?"

`graphify explain "X"` prints one matched node with its ID, source file, file type, community, degree, and up to 20 sorted connections. Like `path`, it preserves direction: outgoing neighbors use `-->`, incoming callers use `<--`.

Sources: [graphify/__main__.py:1936-1990](), [tests/test_explain_cli.py:42-56]()

## Ask Through MCP When An Agent Needs Tools

The MCP server exposes the same graph as agent-callable tools. It starts from a graph JSON file, loads it into NetworkX, and registers tools such as `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`, `list_prs`, `get_pr_impact`, and `triage_prs`.

Sources: [graphify/serve.py:395-445](), [graphify/serve.py:447-569]()

The MCP flow stays provider-neutral. The server is a local stdio server over MCP; it reads `graphify-out/graph.json` and returns text/resources. It does not require a specific model provider. Any agent or UI that can speak MCP can use the same map.

The server also hot-reloads when `graph.json` changes. It tracks file `mtime_ns` and size, reloads under a lock, and keeps serving the previous graph if a transient reload fails.

Sources: [graphify/serve.py:410-443]()

## Look Backward With `affected`

`graphify affected "X"` answers a slightly different question: "What depends on this?" It resolves a seed by node ID, exact label, exact source file, or a single substring match. Then it walks incoming edges for impact-style relations such as `calls`, `references`, `imports`, `inherits`, `implements`, `uses`, and `embeds`.

Sources: [graphify/affected.py:11-23](), [graphify/affected.py:46-80](), [graphify/affected.py:86-140]()

Use it when you are about to change a function, class, component, or file and want likely dependents before editing.

## Use PR Impact To Review The Right Changes First

`graphify prs` combines GitHub PR metadata with graph impact when needed. Its data model includes CI status, review decision, staleness, worktree path, changed files, touched communities, and affected node count. The "blast radius" string is built from node and community counts.

Sources: [graphify/prs.py:57-90](), [graphify/prs.py:94-113]()

The graph-native impact calculation builds a file-to-community index from graph nodes, matches changed PR files to `source_file`, and returns `(communities_touched, nodes_affected)`. The CLI only fetches impact for deeper flows such as a PR detail, triage, or conflict detection, rather than every dashboard render.

Sources: [graphify/prs.py:243-273](), [graphify/prs.py:668-748]()

## Keep More Than One Repo On The Map

A local project graph is useful. A global graph is useful when work crosses repositories.

`graphify global add <graph.json> --as <tag>` stores graphs under `~/.graphify/global-graph.json` and tracks source hashes in `~/.graphify/global-manifest.json`. When a graph has not changed, `global_add` skips the merge. When it has changed, Graphify prefixes repo-local node IDs, prunes stale nodes for that repo tag, and merges the new graph.

Sources: [graphify/global_graph.py:10-27](), [graphify/global_graph.py:58-133](), [graphify/__main__.py:2715-2769]()

Tests verify the design point: two repositories can both have a node like `userservice`, and the global graph keeps `repoA::userservice` separate from `repoB::userservice` instead of silently merging them.

Sources: [tests/test_global_graph.py:40-67](), [tests/test_global_graph.py:129-150]()

## Keep The Map Fresh

### Use `update` after code edits

`graphify update .` is the everyday refresh command after code changes. It calls `_rebuild_code`, prints that code files are being re-extracted, blocks on the per-repo lock for an explicit user-run update, and reports that doc, paper, or image changes still need the fuller semantic update flow.

Sources: [graphify/__main__.py:2223-2273]()

The rebuild path is AST-only for code. It detects code files, extracts them, preserves previous semantic nodes and edges where possible, evicts changed or deleted sources, rebuilds the graph, reclusters when topology changes, writes `graph.json`, `GRAPH_REPORT.md`, labels, and optionally `graph.html`.

Sources: [graphify/watch.py:274-305](), [graphify/watch.py:336-424](), [graphify/watch.py:478-604]()

### Use `watch` when the map should follow you

`graphify watch <path>` listens for file changes with `watchdog`. Code changes trigger a rebuild after debounce. Doc, paper, and image changes write `graphify-out/needs_update` and tell the user to run the semantic update flow, because those changes may require LLM-backed extraction.

Sources: [graphify/watch.py:641-657](), [graphify/watch.py:701-719]()

The watcher filters noise before doing work. It loads `.graphifyignore` once at startup, skips ignored paths, skips unsupported extensions, skips dot paths, and skips files under the Graphify output directory. Tests cover ignored `node_modules` and `build` writes, and verify that the ignore file is not reparsed for every event.

Sources: [graphify/watch.py:663-690](), [tests/test_watch.py:225-260](), [tests/test_watch.py:263-296]()

### Use `check-update` for a lightweight reminder

`graphify check-update <path>` checks whether `graphify-out/needs_update` exists. It prints a reminder but does not clear the flag. Tests confirm the flag is created idempotently by `_notify_only`, and `check_update` leaves it in place.

Sources: [graphify/watch.py:611-634](), [tests/test_watch.py:13-29](), [tests/test_watch.py:65-84]()

## Safety Rails

Graphify treats graph files and corpus-derived labels as untrusted inputs. Query-like commands reject oversized `graph.json` files before parsing, using a 512 MiB cap. The query tests explicitly patch the cap lower and assert that the CLI fails before loading the graph.

Sources: [graphify/security.py:21-25](), [graphify/security.py:239-260](), [tests/test_query_cli.py:54-70]()

MCP output sanitizes node labels, source files, locations, communities, relations, and confidence strings before concatenating them into tool output. That keeps corpus-controlled text from becoming terminal control sequences or prompt-injection-shaped context.

Sources: [graphify/serve.py:261-311]()

For URL ingestion, Graphify only allows HTTP and HTTPS, blocks private/reserved/link-local/cloud metadata targets, revalidates DNS during fetch, and blocks redirect-based SSRF paths.

Sources: [graphify/security.py:17-25](), [graphify/security.py:42-86](), [graphify/security.py:89-125]()

## Provider-Neutral Integration Guidance

A Grok-Wiki or agent UI should treat Graphify as a portable file-backed map, not as a model-provider feature.

| Flow | Portable design |
| --- | --- |
| Ask | Read `graphify-out/graph.json`, then call CLI or MCP query tools. |
| Navigate | Prefer `graphify-out/wiki/index.md` when present, then source files. |
| Explain | Use `explain`, `get_node`, and `get_neighbors` for focused concepts. |
| Relate | Use `path` or MCP `shortest_path` for relationship questions. |
| Refresh | Run `graphify update .` after code edits; use `watch` for continuous refresh. |
| Cross-repo | Add local graphs into the global graph under explicit repo tags. |
| Skill sources | Load skills from file, repository, or catalog sources; do not bind the workflow to one hosted model or connector. |

This is BYOC/BYOK friendly because the stable interface is local files plus CLI/MCP. Semantic extraction can be supplied by whichever backend the deployment chooses, while AST-only `update` and watch rebuilds do not require an LLM call.

Sources: [graphify/__main__.py:1427-1438](), [graphify/__main__.py:2223-2273](), [graphify/serve.py:395-405]()

## Closing Habit

The durable habit is: ask the map, inspect the cited source, change the code, then refresh the map. `query`, `path`, `explain`, MCP tools, PR impact, and global graphs make the map reusable; `update`, `watch`, and `check-update` keep it current after the first build.

Sources: [graphify/__main__.py:395-424](), [graphify/watch.py:592-604]()
