Files
reliquary/plugins/orbit/skills/orbit/SKILL.md
T
g4borg f1de89e869 🤖 Add orbit prototype plugin for Rust code review
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.
2026-09-29 19:52:59 +02:00

8.8 KiB
Raw Blame History

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)

  1. Freshness. Ad-hoc: $S/freshness.sh <repo> (read-only). For a review, preflight.py checks this itself.
    • STALE means the index sha is not HEAD. orbit sql then silently queries every repo and commit (the note goes to stderr only), and grep/context give 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.
  2. Always -t 4. With default threads, a per-file watchdog silently skips 4–7 large files per run. Check processing.skipped_files == 0 and errored_files == 0 in the index JSON. If either is not 0, re-index; if it persists, list those files as blind.
  3. 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 for use inside test mods.
    • Cargo features are evaluated ALL OFF, defaults included. dirigent's server feature hides most of dirigent_core; spacecraft's default steam feature is dropped. There is no flag to change this.
    • CALLS gaps: dyn Trait receivers (0–14 % recall), macro arguments (assert!, matches!, vec!), rsx! bodies (Dioxus components ~0 %), fns passed by reference (Bevy add_systems, observers, callbacks), enum variant patterns, Type::default().
    • Traits: external traits (Plugin, Serialize) are not nodes and return empty without a warning. Cross-file impl blocks, blanket impls and impls on external types have no EXTENDS.
    • Imports: inline paths (other_crate::x::Y without use) are not import rows. pub use re-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:

  1. 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.
  2. 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.
  3. 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 match on the enum; a changed Bevy system signature → is it still registered? a removed item → is the old path re-exported, or does it dangle?
  4. traits. Read every [G]-only or !! implementor. [H]-only may be a delegating facade; confirm it by reading.
  5. boundary. Check new crate edges and Cargo.toml deps against the documented graph (AGENTS.md, crate CLAUDE.md). Doc drift is a real finding.
  6. 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 run freshness.sh (ad-hoc) or review-pass.sh (range <base>..<tip>, name <name>, isolated DB under ~/.cache/orbit-review), and always run review-teardown.sh at the end. Never run orbit index on 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 one fb.sh line per result that informed a finding (ORBIT_AGENT=search).

The receiving reviewer (Opus) decides severity. The search agent relays facts and hypotheses, not verdicts.