Standalone plugin with one skill: when to use orbit and when grep, a mandatory preflight for stale indexes and blind regions (cfg(test), features, macros, dyn dispatch), a scripted review pass over isolated worktree DBs, and a JSONL feedback log for mining real usage later. Registered in the marketplace as a prototype.
8.8 KiB
name, description
| name | description |
|---|---|
| orbit | PROTOTYPE. Use the orbit code graph (GitLab `orbit` CLI) for STRUCTURAL questions about a Rust codebase: reviewing a Rust change, branch, PR, merge or diff ("what did this diff touch structurally", "review this branch against main"); "who depends on / calls / imports / uses X"; "who implements trait X", "did every impl get the new method"; blast radius of a change; "did this cross a crate or module boundary"; "what does this crate/module/type look like". Also for briefing a subagent (e.g. Spire's `search`) that should run these queries. NOT a grep replacement: orbit is blind to #[cfg(test)], cargo-feature-gated code, macro args, rsx!, dyn dispatch and Bevy add_systems, so every orbit answer is a lower bound that grep must close. For literal text, counting occurrences, or "where is string X", use grep. Shell only, not MCP. |
Orbit (prototype)
Orbit indexes a Rust workspace into a DuckDB graph: definitions, CALLS, IMPORTS, EXTENDS. This skill decides when orbit is worth asking, what must be checked before trusting it, and how to report what it says. It was built from a probe of orbit 0.133 on spacecraft (Bevy) and dirigent (Tokio/Dioxus). The numbers below come from that probe.
Paths: S=${CLAUDE_PLUGIN_ROOT}/skills/orbit/scripts. If CLAUDE_PLUGIN_ROOT is unset (subagent shells), use the plugin's absolute path. Details are in references/review-recipes.md and references/cookbook.md. Read them only when you run a recipe or write ad-hoc SQL.
Orbit or grep
| Question | Use |
|---|---|
| Which definitions did this diff add, remove, or change in signature? | orbit (defdiff.py, exact within visible code) |
| Who calls or imports X outside the diff? | orbit first, then grep. Orbit finds method callers of a type that grep can't. Grep finds the ~50 % orbit misses at definition level. |
| Who implements in-repo trait T? | orbit plus grep. EXTENDS only links same-file impls (traitcov.py does both). |
| New crate→crate edges, documented layering rules | orbit (boundary.py), plus its Cargo.toml and inline-path fallbacks |
| Outline of a file, module, or type | orbit grep --path <file> (no query) / orbit repo-map class <Type> / repo-map tree <dir> |
| Literal strings, counts, "every place that mentions X", test code, feature-gated code | grep. Orbit does not see gated code and its grep is ranked and capped. |
| Runtime behaviour, ordering, Bevy schedules, async | Neither. Read the code or run it. Orbit knows structure only. |
Mandatory preflight (every time, before trusting a single row)
- Freshness. Ad-hoc:
$S/freshness.sh <repo>(read-only). For a review,preflight.pychecks this itself.STALEmeans the index sha is not HEAD.orbit sqlthen silently queries every repo and commit (the note goes to stderr only), andgrep/contextgive no warning. Re-index the main checkout (orbit index <repo> -t 4, which replaces that repo's entry in the shared DB), or stop.DIRTY/ working-tree caveat: orbit indexes the working tree, including uncommitted and untracked files, but labels the entry with HEAD's sha. A matching sha proves nothing if the tree was dirty at index time or was edited since.
- Always
-t 4. With default threads, a per-file watchdog silently skips 4–7 large files per run. Checkprocessing.skipped_files == 0anderrored_files == 0in the index JSON. If either is not 0, re-index; if it persists, list those files as blind. - Blind regions. State them before any conclusion:
#[cfg(test)]modules, and files gated by a#[cfg(test)] mod x;: no definitions, no CALLS, and no import rows foruseinside test mods.- Cargo features are evaluated ALL OFF, defaults included. dirigent's
serverfeature hides most ofdirigent_core; spacecraft's defaultsteamfeature is dropped. There is no flag to change this. - CALLS gaps:
dyn Traitreceivers (0–14 % recall), macro arguments (assert!,matches!,vec!),rsx!bodies (Dioxus components ~0 %), fns passed by reference (Bevyadd_systems, observers, callbacks), enum variant patterns,Type::default(). - Traits: external traits (
Plugin,Serialize) are not nodes and return empty without a warning. Cross-fileimplblocks, blanket impls and impls on external types have no EXTENDS. - Imports: inline paths (
other_crate::x::Ywithoutuse) are not import rows.pub usere-exports are not followed.
Review pass (Rust branch, PR, or uncommitted diff)
Run it in an isolated --db, never the shared DB. Worktree entries in the shared DB can't be removed, because orbit has no delete command.
# base..tip: branch -> base=$(git merge-base main B) tip=B; merged PR -> <merge>^1..<merge>
# uncommitted -> tip=$(git stash create) (a commit object of tracked changes; touches nothing; untracked files NOT included)
export ORBIT_REVIEW_HOME=~/.cache/orbit-review # default; worktrees + DBs + outputs live here, never /tmp
$S/review-pass.sh <repo> <base> <tip> <name> [$S/rules/<repo>.rules] > review.txt # ~30 s, ~8–16 KB
$S/review-teardown.sh <repo> <name> # always, even after failure
review-setup.sh adds two detached git worktrees to the repo (these are removed in teardown) and indexes both into <name>.duckdb. Afterwards, git worktree list and pgrep -a orbit should show nothing of yours.
Read the output in this order:
- Preflight. Blind share of changed lines: <25 % → orbit-led review, grep the listed gaps. 25–60 % → orbit for the visible part only; read the blind files. >60 % → stop using orbit except defdiff and boundary, and say so. Also stop if the index isn't fresh or clean, or if the plan's key items all sit in blind files.
- defdiff. Compare with the plan: is every planned item present, and is anything unplanned present? Read every
S(signature) row, every-row, and every type with member changes. - blast. For each OUTSIDE-diff dependent: did it need a change it didn't get? Then close the gaps with grep: an added enum variant → grep every
matchon the enum; a changed Bevy system signature → is it still registered? a removed item → is the old path re-exported, or does it dangle? - traits. Read every
[G]-only or!!implementor.[H]-only may be a delegating facade; confirm it by reading. - boundary. Check new crate edges and Cargo.toml deps against the documented graph (AGENTS.md, crate CLAUDE.md). Doc drift is a real finding.
- context. A compact API brief of the touched area to hand to the reviewer, together with the plan.
For ad-hoc questions, use the SQL in references/cookbook.md (callers, importers, implementors, blind files).
Reporting
- Fact: orbit states it and grep or a file read confirmed it. Cite
file:line. - Hypothesis: any orbit count or absence ("no other callers", "no implementors", "no boundary crossing") that grep has not closed. Write it as "≥ N (orbit lower bound; gaps: cfg-test, dyn, …)". Never write "unused" or "dead": most callables with no incoming CALLS are systems, handlers or trait impls.
- Blind: state the preflight blind files and blind share once, at the top.
Feedback (cheap, required while this is a prototype)
After each orbit query or recipe whose result informed a decision (or should have, and was wrong), append one line:
ORBIT_AGENT=<search|reviewer|…> $S/fb.sh <recipe> <verdict> "<purpose>" "<cmd>" <rows> ["<note>"] [gap]
# recipe: defdiff blast traitcov boundary preflight context adhoc-sql grep repo-map context-cmd
# verdict: useful | wrong | incomplete | grep-needed | noise | unused
# gap: cfg-test cfg-feature gated-file dyn fn-ref macro rsx cross-file-impl external-trait inline-path pattern-match stale-index sig-truncated other
Written to ~/.local/share/orbit-skill/feedback.jsonl (override with ORBIT_FEEDBACK). One call per decision, not per query. Skip it for idle exploration.
Briefing a subagent
Spire's search agent (Sonnet, Bash, read-only) is the right carrier. Put this in the brief:
Use the orbit skill (
orbit:orbit), from the shell only (the orbit MCP server has only unscoped SQL). Scripts:<abs path>/skills/orbit/scripts. First runfreshness.sh(ad-hoc) orreview-pass.sh(range<base>..<tip>, name<name>, isolated DB under ~/.cache/orbit-review), and always runreview-teardown.shat the end. Never runorbit indexon a worktree without--db. Never use/tmp. Do not compile. Return: (1) blind share and blind files, (2) facts with file:line, (3) hypotheses labelled "orbit lower bound" with gap classes, (4) the grep commands you ran to close gaps. Log onefb.shline per result that informed a finding (ORBIT_AGENT=search).
The receiving reviewer (Opus) decides severity. The search agent relays facts and hypotheses, not verdicts.