Imported from takku1/graphgraph (
.agents/skills/graphgraph/SKILL.md). Install upstream withnpx skills add takku1/graphgraph --skill graphgraph. Copyright stays with the author.
GraphGraph operational contract
Use GraphGraph across Codex, Claude Code, and other MCP/CLI agents for
structural codebase questions before broad source reading.
MCP and CLI are transports over the same
SYNC -> EXTRACT -> NORMALIZE IR -> ANCHOR -> EXPAND -> SELECT -> PACK
instruction set.
For richer workflows, keep the same low-level contract. Use MCP
compile_context or CLI graphgraph platform compile when evidence providers,
bounded inference, or hierarchy are needed. Memory, temporal episodes,
federation, traces, and repair inputs must be projected into normal GraphGraph
nodes/edges before retrieval; do not reason from a parallel store as if it were
structural evidence. Inspect the returned compiler receipt and validate the
packet.
Normal context/query calls automatically use the bounded source planner;
use --source-mode off only for a structural baseline. Evidence compilation
uses versioned per-source CPG IR and exact merge/truncation receipts.
[!IMPORTANT] Check availability. Use
graphgraph/queryfor arbitrary read-only user text when that MCP tool is registered; it compiles to the cheapest lossless expert operator and never infers a mutation or implicit build. Usegraphgraph/query_contextwhen the caller specifically requires the full context packet envelope. Otherwise use the CLI commands below; never translate MCP tool names into guessed CLI flags.graphgraph doctorreports installed frontends and client registration.
[!IMPORTANT] Audit exclusions before building. Before the first build, an intentional rebuild, or indexing a materially changed repository layout:
- Read root and nested
.gitignore/.ignorerules.- Inspect top-level and unusually large path names. Look for generated/build/ cache/coverage outputs, vendors/dependencies, bundles/minified assets, binaries, logs, datasets, copied repos, graph/tool outputs, secret-bearing environment/config files, and temporary investigation corpora. Inspect names/rules without opening secrets.
- Do not blindly exclude tests, fixtures, benchmarks, migrations, or docs; retain them when they provide likely task evidence.
- Record exclusions and reasons. Pass directory names through MCP
build_graph.exclude_dirsor CLIscan --exclude.include_dirs/--includeonly overrides a built-in skip name; it is not an allowlist.- Build only after this audit. Later,
query_contextwithsync: "git"reconciles paths made stale by new ignore rules.
Default query path. For an existing healthy graph, use
queryfor free text and inspect its typed plan receipt. Usequery_contextfor an explicitly packet-shaped retrieval. After edits, pass exactchanged_paths/deleted_paths; if that list was lost, passsync: "git". CLI equivalent:graphgraph context "<query>" --sync git. Leavequery_classand node budgets automatic unless testing a known policy.
Development-loop receipt. For one machine-readable operation, use
graphgraph context "<query>" --changed-files <paths...> --json --validate. The envelope includes refresh and validation state, inferred/explicit scope, plan rationale, packet-quality metrics, affected-test recommendations, and timings. Compound implementation-and-test questions also report per-facet evidence, unfulfilled facets, and the merged path/test intents. A stale warning means refresh with--sync git; dirty does not mean stale when manifest hashes already match.
Benchmark discipline. Do not use expected answer keys or fixture answers as evidence. Use retrieved packets, source, docs, and requested command output.
Tool routing
Route on the shape of the question, not on familiarity. Costs are measured medians on a 14.5k-node Rust workspace.
| Question shape | Tool | Cost |
|---|---|---|
| Arbitrary read-only user text or unknown intent | query |
Routes to the cheapest lossless typed operator; context fallback |
| Exact named symbol, one-hop callers/callees | query_relations / relations |
1–2 ms warm; tuple IR |
| One named symbol: blast radius, path, or "how does X work" | query_context / query |
0.4s fast path, 2.4s ranked |
| A predicate over many symbols: "which functions have no production caller", counts, existence | select |
~0.5s |
| Exact literal string, no relationship | rg / git grep |
— |
Exact one-hop MCP payloads:
{"target":"Type::method","direction":"callers"}
{"target":"Type::method","direction":"callers","sync":"git"}
Use the second shape when edits may make the saved graph stale or when an
absence/count must be licensed. CLI equivalent: graphgraph relations Type::method --direction callers --sync git. The default omits all Git and
manifest work and remains the latency floor.
Micro IR v2 decodes without outside prose: d is direction; tk names target
tuple t; k names neighbor tuples n; r contains matched/eligible/
returned/filtered/completeness/freshness/timing receipts. Optional a entries
are executable routing opcodes:
search_nodes/retry_exact_id_or_path_symbol: resolve a missing target;retry_candidate_id: disambiguate with an emitted candidate ID;raise_limit: rerun with a larger limit before reporting a complete list;sync_if_completeness_required: addsync: "git"/--sync git;verify_absence_or_count: topology extraction is partial or unknown, so verify an absence/count against source before asserting it.rebuild_graph: the saved graph uses an incompatible extractor identity;project_status: freshness remained stale after the requested sync.
answer_complete=true requires all three gates: no response truncation,
complete call-topology telemetry, and checked freshness.
query cannot answer set predicates at all — it anchors on named nodes. Do
not emulate one by looping query over a symbol list; that is the failure this
tool exists to prevent (a hand-rolled sweep published two contradictory counts
before select existed). Use label in [...] for batches.
graphgraph select "production_callers = 0 and crate contains locus-engine and include_tests = false" --mode count
graphgraph select "callers > 20" --limit 50 # hubs, not islands
graphgraph select "label in [parse, lower, emit]" --json
Grammar: clauses joined by and. production_callers/callers with
= != > >= < <=; kind=K; path|crate contains S; path|crate != S;
label contains S; label in [a, b, c]; include_tests=BOOL.
Modes: select (rows), count (integer), exists (boolean). count/exists
never materialize node payloads — prefer them when the answer is a number.
query --json and select --json emit the full envelope compactly. It is
already the token-efficient form; --pretty adds indentation for reading by
eye and costs ~26% more tokens. Do not pass --pretty for machine
consumption. MCP responses are always compact.
An unsupported clause raises rather than being silently dropped; a returned
answer is always the whole predicate.
Decision rules
- Unknown or unrestricted read-only question: call
query; follow its typed plan and receipt. Exact symbol plus one-hop caller/callee question: callquery_relations(or CLIrelations) first. Its micro tuple IR is the low-latency/token lane; tests are opt-in. Addsync: "git"/--sync gitwhen freshness is needed; otherwise it is explicitly unchecked. Follow any returnedaaction opcodes before claiming a complete list, count, or absence. For broader or natural-language structural questions, callquery_contextfirst. - Missing graph: audit exclusions,
build_graph/scan, validate, inspect the build receipt, then query. Do not letcontextauto-build before the audit. - Exact known string with no relationship question:
rg/git grepis valid. Prefer GraphGraph for callers, dependencies, paths, blast radius, and “how does this work?” orientation, andselectfor anything quantified over the whole repository. - Focus with CLI
--scope src/pathor MCPsearch_nodesthenfinal_packet. Explicit scope defaults to--scope-mode strict; chooseexpandonly when structurally connected dependency boundary crossings are useful. - Validate saved graphs with
validate_packetorgraphgraph validate-graph; validate rendered packets withgraphgraph validate. - Treat graph output as orientation evidence; verify final claims against source or tests before changing code.
- Accept a build only after checking: ignore files honored, rule/default paths
pruned, selected frontend, fallback/failure counts, validation, and file or
symbol/document truncation. CLI scans emit timed phases, document counts,
slowest documents, and source-concept timings to stderr. MCP
build_graphreturnsfrontend,exclusions, and a machine-readablephase_profile.describe_frontendsreports per-languageready_languagesandunavailable_languages; aggregate Tree-sitter availability does not imply every optional grammar is installed. - For documentation answers, require grounded section/paragraph facts. Treat
document_warning, zero grounded doc nodes, or unfulfilled requested phrases as a retrieval failure to narrow, refresh, or report—not a successful heading match. - Qualify same-named members as
Type::methodin queries. For affected tests, inspect each recommendation'scoversreceipt and use the emitted command; Rust commands are manifest-derived and distinguish an integration target from a module/filter. Execute the focused command before claiming it passes.
Main operations
| Need | MCP | CLI |
|---|---|---|
| Any read-only natural-language question, typed routing | query |
query "<text>" |
| Exact one-hop callers/callees, low-token IR | query_relations |
relations <symbol> --direction callers|callees [--sync git] |
| Natural-language packet, optionally fresh | query_context |
context "<query>" [--sync git] [--json] |
| Build after exclusion audit | build_graph |
scan --depth symbols --docs --exclude <dirs...> |
| Exact edited/deleted splice | query_context with changed/deleted paths |
update --files ... / remove --files ... |
| Low-level splice tools | update_graph_files / remove_graph_files — both require a paths array (repo-relative or absolute) |
update --files ... / remove --files ... |
| Resolve labels/paths | search_nodes |
query "<text>" --show-anchors |
| Packet from known IDs | final_packet |
final --query-class <class> --starts <ids...> |
| Bounded exact source | source_snippets |
snippets --starts <ids...> |
| Whole-repo predicate / counts / batch symbol lookup | select_symbols |
select "<predicate>" [--mode count|exists] [--json] |
| Project/install health, resolution + staleness receipts | project_status |
status --probe / doctor |
| Validate | validate_packet |
validate-graph / validate |
| Compile advanced graph passes | compile_context |
platform compile |
| Enforce multi-repo gates | - | platform benchmark --config <json> |
| Migrate platform state | - | platform migrate --directory .graphgraph |
| Issue/error repair context | repair_context |
platform repair |
| Structural snapshot diff | graph_change |
platform change |
| Scoped memory | memory_context |
platform memory |
| Historical graph view | graph_at_time |
platform as-of |
full_graph is an exceptional escape hatch and refuses large graphs by
default. describe_formats, describe_ontology, describe_frontends, and
describe_traversal expose the low-level contract. CLI --starts belongs only
to final and render, not context or query.
Query classes: direct_lookup, reverse_lookup, subsystem_summary,
blast_radius, multi_hop_path, affected_tests, doc_summary, negative_query, and
recent_changes (requires a scan with --history). Compact gg is the
normal token floor; choose larger formats only for columns they uniquely carry.
Live validation harness
Run python scripts/validate_live.py --repo <repo> from this skill directory
to scan and validate live packets against any repository. The harness derives
default queries from that repository and detects Cargo, Go, npm, pytest, or
unittest tests.
- Override detection with
--test-command "<command>". - Use
--skip-testswhen tests are intentionally out of scope. - Add repeatable
--query "<question>"values to replace derived defaults. - Enable
--saved-reportsonly for GraphGraph self-validation; foreign repositories do not fail because GraphGraph benchmark reports are absent.
Reading the output: what each receipt licenses
Every line below is emitted by the tool. Treat them as preconditions on what you may assert, not as decoration.
| Receipt | Meaning | What you may conclude |
|---|---|---|
-- CAVEAT: member-call resolution N% on select |
Unresolved member calls emit no calls edge |
production_callers = 0 is an upper bound on dead code, not a proof. Output is a candidate list requiring per-symbol verification. Never delete on this alone |
GraphGraph partial result: node budget omitted N known direct reverse neighbor(s) |
The list was truncated | The answer is partial. Re-run with a larger --max-nodes before reporting a count or an absence |
anchor=exact_fast_path vs anchor=ranked (query --show-stats) |
Which anchor route ran | ranked means the name was ambiguous or absent, and costs ~4x. If you expected one definition, ranked says there are zero or several |
! STALE GRAPH: N changed ... (status, query) |
Files moved since the scan | Refresh before trusting an absence: context --sync git |
! STALE: counts were measured by a full scan ... (status) |
Member-call telemetry was carried forward | The resolution numbers describe an older scan, not this graph |
Unresolved receivers by shape: ... (status) |
Why receivers went untyped | Diagnostic for resolver work. Bucket size is not addressability — most large buckets iterate generic/stdlib types that can never name a repo symbol |
a in relation micro IR |
Required next actions before a stronger claim | Execute the listed opcode: sync, raise the limit, disambiguate, search, or verify absence/count against source |
Run graphgraph status for the current repository-specific member-call
resolution rate and telemetry scope. A symbol reported with zero callers may
simply be called through an unresolved receiver. This is the single most
important limitation to carry into any dead-code, island, or blast-radius
conclusion.
Measurement discipline
scandefaults to incremental. A resolver- or extractor-level change affects every file, not only changed ones, so an incremental scan shows almost no delta and the change appears to have done nothing. Measure withgraphgraph scan --depth symbols --docs --no-incremental.statusreports member-call counts from the last full scan and prints aSTALEline when they were carried forward. Numbers without that line are current; numbers with it are not.query --show-statsprints the execution receipt to stderr (packet still on stdout). Use it to attribute latency:anchor=exact_fast_pathskips the lexical index build,anchor=rankedpays it.- Warm and cold query latencies differ ~6x. Compare like with like; a first-run number is not a steady-state number.
Measuring a change
graphgraph eval --graph <g> --tasks <tasks.json> scores retrieval against
hand-verified expectations. Task shape: {"query": ..., "expected": [labels or paths]}; expected_nodes/expected_edges are also accepted.
- A task with no parsable expectations reports
scored: falseand a null recall, never a passing score. Treatscored: falseas "this task measured nothing", not as success. eval/graphgraph-self.jsonis the committed regression suite for this repository, and deliberately ends with a red task naming symbols that do not exist. It must scorenode_recall: 0.0. If it ever scores above zero, the harness is broken, not the retrieval.- Rebuild with
--no-incrementalbefore comparing resolver numbers, or the carried-forward telemetry will show no delta.
Language coverage for calls edges
Receiver typing decides whether a member call becomes a calls edge, and it
is not uniform:
| Language | Receiver evidence used |
|---|---|
| Python | parameter/variable annotations, x = Type(), class fields, base classes |
| Rust | parameter and let types, container element types, callee return types, impl blocks |
| TypeScript/JavaScript | parameter/variable annotations, new T(), as T, this.field |
| C#/Java | typed parameters/locals, new T(), field/property declarations, and both this.member and bare field receivers (_repo.Method()) |
| C/C++ | C and C++ parameter/local declaration types and qualified receivers; C++ member-field receivers are not yet typed (class extraction pending) |
| Other supported languages | symbol/call extraction varies; inspect status before trusting caller completeness |
Inherited methods resolve through the base-class chain. A symbol reported with
zero callers may simply be called through a receiver that could not be typed;
status reports the current resolution rate and the shape breakdown of what
went untyped.
Noise and receipt rules
Defaults skip VCS, environments, dependencies, builds, caches, generated agent
artifacts, local agent/MCP configuration, graph outputs, vendors, and cloned
references. These defaults are a safety floor, not proof of a clean graph.
Prefer excluding reproducible derivatives while retaining source-of-truth and
relationship-bearing tests/docs. Ignore-matched directories must be reported
as pruned before descent; a scan that merely walks and discards every ignored
file is a performance bug.
doc_summary may carry bounded paragraph spans beneath selected headings; use
its grounding telemetry to distinguish an answer from a heading-only match.