Graph-Accelerated Discovery Guide¶
The shared contract for seeding a skill's codebase-research phase from the
ll-code query surface (EPIC-2575) instead of discovering everything with
open-ended Grep sweeps.
This file is the canonical statement of the ll-code seeding contract, the
safety rules, and staleness handling. Consumers keep only their own
phase-specific procedure and link here — one place to fix when the contract
moves.
Consumers¶
| Skill / command | Phase | Consumer doc |
|---|---|---|
/ll:wire-issue |
Phase 3.6 | skills/wire-issue/graph-discovery-layer.md |
/ll:refine-issue |
Step 3.05 | commands/refine-issue.md § 3.05 |
/ll:verify-issues |
§2B.0 | commands/verify-issues.md § B.0 |
/ll:verify-issues follows the same procedure and safety rules as the other two
consumers, but under a stricter local rule: it mutates issue state (verdicts,
status), so a graph result there may only corroborate or correct a verdict, never
originate one. callers-of/references exiting 1 ("no callers") is never
sufficient by itself to reach RESOLVED or INVALID — see § B.0 in the consumer
doc for the full statement. It also queries defines/callers-of/references
only; impact-of is out of scope there because regression detection already has a
deterministic git-history signal.
Why the orchestrator queries, not the agents¶
ll-code is a CLI, so reaching it requires Bash. Agent frontmatter (agents/*.md)
takes bare tool names — it cannot express the Bash(ll-code:*) scoping that a
command's allowed-tools can. Wiring ll-code into ll:codebase-locator /
ll:codebase-analyzer / ll:codebase-pattern-finder would therefore mean granting
three read-only, document-don't-modify agents unrestricted shell.
It would also be redundant: those agents are dispatched as a parallel wave, so each
would re-probe status, re-derive the same symbol set, and return three
independently-deduped candidate lists.
So the orchestrator (the command/skill body, which already has scoped Bash)
runs the queries once and passes confirmed candidates into the agent prompts as
already-known facts. The agents then confirm and extend rather than rediscover.
Procedure¶
# 1. Probe the provider (silent fallback on unavailable / error).
STATUS=$(ll-code --json status 2>/dev/null)
# available:false in STATUS, or a non-zero (exit 2) provider error → skip the
# graph phase entirely and run the consumer's normal exploratory flow.
# 2. For each change target (symbol or file) the issue names, gather candidates:
ll-code --json callers-of <symbol> # who calls it
ll-code --json importers-of <path> # who imports the module
ll-code --json impact-of <path> # transitive impact set (tests/config/docs)
# 3. Confirm each positive hit with ONE targeted Grep at its path:line before it
# is treated as a fact.
# 4. Hand confirmed candidates to the consumer's agent wave as seeds.
Query only what the provider advertises: status.capabilities is a list drawn from
callers_of, callees_of, defines, impact_of, importers_of, references
(underscored, unlike the hyphenated subcommand names). A subcommand outside that
list exits 2; treat it exactly like an unavailable provider for that one query
rather than aborting the phase.
Contract¶
See docs/reference/CLI.md § ll-code — authoritative.
ll-code --json status→{provider, available, freshness, indexed_at, detail, capabilities}.- Query subcommands (
callers-of/callees-of/importers-of/defines/references/impact-of) →{provider, freshness, query, results:[{path,line,symbol,kind,confidence,provider}]}whereconfidenceis"exact"|"heuristic". - Exit codes:
0= hits,1= no hits,2= provider error / unsupported query. - Every query response also echoes top-level
freshness, so a phase can gate on staleness without a separatestatuscall.
The three safety rules (encode verbatim)¶
- Silent fallback. If
ll-code --json statusreportsavailable: falseor a query exits2(provider error / unsupported), skip the graph phase and run the consumer's normal exploratory flow. Zero regression when no provider exists. - Confirm-before-use. Every positive hit is a lead, not a verdict. Confirm it
with one Grep at its
path:linebefore it is written down or handed to an agent as an established fact. - Never trust negatives. Exit
1("no callers") is the negative result that is NEVER trusted alone — run the normal exploratory pass for that target.
Staleness handling¶
If freshness == "stale" (an index-backed provider whose index lags the working
tree — the codegraph provider; the FallbackProvider reads the working tree
directly and is always fresh), treat all candidates as leads only and widen
confirmation to the normal exploratory flow for anything correctness-critical.
With code_query.codegraph.auto_sync enabled (default, ENH-2863), stale should be
transient rather than steady-state — the provider self-heals via codegraph sync
--quiet on the read that observes staleness, so a run that hits stale is usually
the one-time straggler before the next status() reports fresh again. It is still
observable in practice, so the widening above is a live path, not a formality.
Setup¶
ll-init detects the @colbymchenry/codegraph
tool and offers to build the index (wizard prompt; ll-init --yes indexes when the binary is
already installed, ll-init --code-graph install also runs the npm install). By hand:
npm install -g @colbymchenry/codegraph && codegraph init ., then ll-code status.
ll-doctor's Code Graph (ll-code) section reports fresh / stale / missing with the
commands to fix it.
Provider note¶
Both providers are real today: FallbackProvider (grep/AST, no index, always
available) and codegraph (ENH-2613, a read-only reader over .codegraph/codegraph.db,
exact confidence, all six capabilities including impact_of per ENH-3092).
--provider auto prefers codegraph when its index is available.
The distinction matters when reading measurements: on FallbackProvider a graph
phase measures structured-query-then-confirm vs. open-ended agent tracing; on
codegraph it additionally measures index acceleration. A benchmark that does not
record which provider served it cannot tell those apart.