--- phase: 1 stream: rust-capabilities date: 2026-09-29 orbit-version: 0.133.0 (~/.local/bin/orbit, embedded skill "orbit-cli" v0.18.2) test-beds: spacecraft (Bevy game, 25-crate workspace, 903 .rs files), dirigent (Tokio/Dioxus server+web, 35-crate workspace, 989 .rs files) status: done --- # Orbit 0.133 on Rust: capabilities probe Legend: **[V]** = verified here with command output. **[I]** = inferred (reasoned from evidence, not tested directly). Scratch scripts used for the measurements are in `~/.cache/orbit-probe/` (`calls.py` = CALLS recall against grep, `cfgcheck.py` = cfg-gated item presence, `ext.py` = impl-for against EXTENDS, `defdiff.sql`, `impl-heuristic.sql`). Phase 2 can reuse them. ## TL;DR - **Orbit understands Rust well where code is visible.** Name resolution is type-aware, and it follows `use` paths across workspace crates. Recall for "who calls X" was **85–100 %** for free functions, `Type::new`-style associated functions and methods on concretely typed receivers. It even resolved calls through `match` bindings on enum payloads. [V] - **It does not see everything.** cfg is evaluated with **every Cargo feature off (default features included)**, `test` off and `debug_assertions` off. Feature-gated modules drop out entirely. In dirigent the whole `server` feature is invisible: `dirigent_core/src/runtime/mod.rs` (180 KB) and `config.rs` (105 KB) have **0 definitions**, and 176 of 989 Rust files have none. `#[cfg(test)] mod tests` never appears, so unit tests are invisible. Only `tests/` integration tests are indexed. [V] - **Trait graph is partial.** `impl Trait for Type` gives `EXTENDS Type→Trait` only when the impl block **is in the same file as the type** (60/62 hits same-file, 0/10 cross-file). Supertraits work. External traits (`Plugin`, `Display`, `Serialize`, …) give nothing, and the result is silently empty (216 `impl Plugin for` in spacecraft). [V] - **Weak spots for CALLS:** calls through `dyn Trait` (0/4 and 1/12 callers found), calls inside macro arguments (`assert!(f(..))`), Bevy systems passed to `add_systems` (they are not calls), and Dioxus components used in `rsx!` (4 of 260 have any incoming edge). [V] - **Review use is feasible.** Two commits (main plus a detached `git worktree`) can be indexed at once and diffed at definition level with one `--all` SQL query. Orbit has **no command to delete an index entry**, so the worktree's rows stay in the shared DB. [V] - **Hygiene:** - Index with `-t 4`. With the default thread count the per-file watchdog skipped 4–7 large files per run, a different set each time. - Check `processing.skipped_files` in the index JSON. - Leave `orbit sql` at its default scope. MCP `run_sql` is **always unscoped**. [V] ## Probe context and state notes - **Pre-existing entries.** These belong to the user, who had indexed the repos before this probe. They are not leftovers from the interrupted attempt, and I removed none of them. All had status `indexed`; there were no `indexing`/`error` rows. At probe start (17:12 local), each indexed commit compared with the repo's HEAD as follows [V]: | repo_path | indexed commit (last_indexed_at, UTC) | HEAD at probe start | stale? | |---|---|---|---| | ~/Projects/spacecraft | `3ff5f27` (14:37:01) | `8d9ec30a` (main) | **yes** | | ~/Projects/dirigent | `dd8dfbb2` (14:36:49), branch `feature/w1c-live-acp` | `5de6c726` (same branch) | **yes** | | ~/Projects/sandcage | `2e70b44` (14:37:22) | `2e70b44` (main, dirty worktree: 6 modified docs) | no; uncommitted edits are not visible to orbit [I] | **Staleness detection on the stale spacecraft entry (before any re-index)** [V]: - `orbit repo-map` → `Error: current commit 8d9ec30a… is not indexed … run: orbit index .` (hard fail). - `orbit sql "select count(*) from gl_definition"` → note on stderr: "commit 8d9ec30a … is not indexed; querying every indexed commit (as with --all)". It returned **34881**, the rows of all three repos. - `orbit grep new` → header `grep "new" @ 8d9ec30a` and results, with **no warning**. - `orbit context ` → returned a file map with no warning. I then re-indexed spacecraft and dirigent at their HEADs. Re-indexing a path replaces that path's entry. The entries are now `spacecraft@8d9ec30a` and `dirigent@8c64bfd2` (dirigent got a new commit from another session during the probe). The "writes into the repo" check below is therefore a **re-index** check on existing entries, plus a first-index check on a fresh worktree (§10). - **Concurrency.** Other sessions were active during the probe. spacecraft was re-indexed by someone else at 17:12 local, between two of my queries. dirigent received a commit (`8c64bfd2`) and a file edit. The `strategy-mvp` worktree in spacecraft moved. The DuckDB file `~/.gitlab/orbit/graph.duckdb` is **shared by every agent on the host**, and one row per `repo_path` is overwritten on re-index. [V] - **`orbit index` writes nothing into the repo.** Evidence: - spacecraft (re-index): `git status --porcelain --ignored` was identical before and after. - Scratch worktree (first index of a fresh checkout): identical, and no new files appeared. - dirigent: the only diffs came from the concurrent session (a test file edit, later committed). - All output goes to `~/.gitlab/orbit/graph.duckdb`, which grew from 46.9 MB to 70 MB. [V] ## 1. CLI surface: what is new since 0.124 `orbit --help` (0.133.0) lists these subcommands: `version index grep context sql schema list mcp repo-map skills setup uninstall query status ontology dsl tools graph-status config`. [V] | Area | 0.124 guide | 0.133 (verified from `--help` and use) | |---|---|---| | `index` | `orbit index .` | `-t/--threads` (0 = auto), `-s/--stats` (per-file and per-language timings, skipped files), `-v`, `--db PATH`. PATH may be a directory that holds several repos. Prints JSON stats, including `processing.skipped_files`. | | `grep` | several queries, ranked | **One** query; `'a\|b\|c'` for OR. `--repo`; repeatable `--path` with globs; `--kind`; omit the query with `--path` to get a file **outline**. Each result is labelled `exact-name` / `name/path` / `body-only ×N` and shows the first body line that matched. | | `context` | `--tests` flag | **`--tests` removed** (error: unexpected argument). Test/fixture/generated connections always print in their own section. Accepts `Definition:`, `File:`, exact FQNs, paths, `path:start-end`, directories. `--repo`. | | `sql` | default scope, `--all` | Adds `--repo PATH` (scope to another checkout) and `-` to read stdin. **New:** if the current commit is not indexed, it falls back to `--all` and prints a note **on stderr**. | | `schema` | whole schema | `orbit schema gl_definition gl_edge` (filter by table), `--raw` (JSON). | | `list` | table | `-F table\|json\|ndjson\|csv`. | | `repo-map` | overview/tree/api/class/extends/imports | Same subcommands. `--repo`, `--ext`. `class` also covers traits and modules. `overview` adds "Key abstractions" (most EXTENDS descendants), "Most-imported defined symbols" and "Most-called callables". Refuses to run on an unindexed commit. | | new | – | `skills [get orbit [path]]`: embedded vendor skill `orbit-cli` v0.18.2, with references `local/cli.md`, `local/sql.md`, `local/repo_map.md`. `setup`/`uninstall`: write instructions, hooks, skill and MCP config into claude/codex/duo/opencode/pi, with `--dry-run` and `--project`. `config` (`~/.gitlab/orbit/settings.json`, only `telemetry.enabled`). | | new, remote-only | – | `query`, `status`, `ontology`, `dsl`, `tools`, `graph-status` talk to the remote GitLab Orbit API. Not probed. | | Schema | – | `_orbit_manifest` now has `project_id` and `parent_repo_path` (links a worktree to its main repo). `gl_file.reason` (`skip_excluded_extension`, `skip_oversize`, `skip_not_utf8`, `skip_binary`). Byte and char offsets on definitions and imports. `gl_edge` has **no** `commit_sha`. | | Env | – | `ORBIT_DATA_DIR` appears in the binary's strings [I: untested]. `--db` exists on every local command. | **Changelog:** not found locally. There is no changelog subcommand or file, and nothing under `~/.local/share` or `~/.cache`. The embedded skill says its source is `gitlab-org/orbit/knowledge-graph`. [V: absence locally] ## 2. MCP `orbit mcp serve` is a stateless stdio server. I checked it with an `initialize` + `tools/list` + `tools/call` pipe (`timeout 20`). The process exited, and `pgrep` showed no orbit process left. [V] - Server `orbit-cli 0.133.0`, protocol `2024-11-05`. **Still only three tools:** - `get_graph_schema(db?)` - `index(path, threads?, stats?, db?)` - `run_sql(sql: string[], db?)`: now takes an **array** of statements and returns one result set per statement. "Orbit rejects large result sets." - **`run_sql` has no repo/commit scoping.** `select count(*) from gl_definition` gave **52114 rows across 4 commits** over MCP, against **17110** with CLI default scope in spacecraft. Every MCP query therefore behaves like `--all`: filter by `commit_sha`, or join `_orbit_manifest`. [V] - `grep`, `context` and `repo-map` are still CLI-only. The guide's advice holds: use the CLI through bash. ## 3. Rust schema **`definition_type` for `.rs` files** [V]: | type | spacecraft | dirigent | notes | |---|---|---|---| | Field | 6356 | 5359 | struct fields and enum-variant fields (`Variant::0`, `Variant::reason`) | | Function | 2961 | 3430 | free fns, including `#[test]` fns in `tests/` | | Struct | 2288 | 954 | | | Method | 1272 | 1960 | fns with `self`, and trait method declarations (fqn `Trait::m`) | | Variant | 1270 | 1931 | enum variants | | Constant | 794 | 367 | | | Module | 642 | 682 | inline `mod x {}` and `mod x;` | | AssociatedFunction | 517 | 529 | fns without `self` inside `impl` (`new`, `default`, …) | | Enum | 285 | 456 | | | TypeAlias | 29 | 62 | | | Trait | 18 | 30 | | | Macro | 10 | 7 | `macro_rules!` only | | Static | 6 | 35 | | - There is **no `Impl` node.** Methods from every impl block (in any file) hang off the type through fqn `crate::path::Type::method`. [V] - A trait impl's methods cannot be told apart from inherent methods: the fqn carries no trait. [V] - **fqn root is the cargo target name.** This covers lib crates, `[[bin]] name` and integration tests (file stem, e.g. `archetypes::…`). In spacecraft the root package lib `warpspace` and the bin `warpspace` (from `warpspace_app`) **share the root `warpspace`**. Group by `file_path`, not by fqn prefix. [V] - **`start_line` includes doc comments and attributes.** Example: `Gamemode` shows at `mod.rs:177`, but `pub trait` is at line 182. [V] **Edges** (`relationship_kind`, `source_kind` → `target_kind`) [V]: | edge | spacecraft | dirigent | |---|---|---| | DEFINES File→Definition | 17110 | 17152 | | DEFINES Definition→Definition (nesting) | 9253 | 8718 | | IMPORTS File→ImportedSymbol | 12912 | 11074 | | CALLS Definition→Definition | 5570 | 10028 | | CALLS Definition→ImportedSymbol (unresolved/external) | 877 | 2377 | | CALLS File→… (top-level) | 69 | 74 | | CONTAINS Directory→File/Directory | 4285 | 3220 | | EXTENDS Definition→Definition | 43 (31 Rust) | 34 | - CALLS targets include `Struct` (1157 in spacecraft) and `Variant` (406). These are **construction sites**: struct literals, tuple-struct and variant constructors. Type mentions in signatures, `Query<&T>` or generics produce **no** edge. [V] - **Proc-macro invocations resolve.** `hex_color!(…)` has 202 grep sites and 200 raw CALLS edges to the `#[proc_macro] fn hex_color`. `macro_rules!` invocations resolve at statement level (4/4, 7/7, 3/3). `seam!` invoked inside `vec![…]` has 0/63. [V] ## 4. Traits and impls - **In-repo trait, implementor in the same file:** `EXTENDS Struct→Trait`. 30/30 in spacecraft; 30/32 in dirigent (two misses in `dirigent_server/src/adapters/process.rs`, cause unknown). [V] - **Impl block in a different file from the type:** **no EXTENDS** (spacecraft 0/5, dirigent 0/5). Example: `JsonlBackend` implements 6 archivist traits in 6 files. Only `ArchiveBackend` is linked, because that impl is in `backend.rs` with the struct. The methods are still attached to `JsonlBackend` (`repo-map class JsonlBackend` lists them with their files). [V] - **Extension traits on external types** (`impl RegisterGameUpdate for App`, `impl IntoPlayerId for u32`): no edge, because the implementing type is not a node. [V] - **Blanket impls** (`impl AbilityClone for T`): no edge. [V] - **Supertraits work.** `trait Ability: Debug + Send + Sync + AbilityClone` gives `EXTENDS Ability→AbilityClone`, so `repo-map extends AbilityClone` shows `Ability` at depth 1. External supertraits (`Resource`, `Debug`) are dropped. [V] - **External traits:** `orbit repo-map extends Plugin` prints only the header, with no rows and no warning. spacecraft has 216 `impl Plugin for`. dirigent has 348 impls of external traits. [V] - **Enums implementing in-repo traits:** neither repo had a case, so this is untested. [I: likely the same same-file rule] - **Trait-object users are missing from EXTENDS by construction.** `repo-map extends ` is a **lower bound**. Cross-check with `git grep -n "impl .* for"`, or use the method-name overlap heuristic (cookbook C7). [V] ## 5. CALLS coverage Method used: for each identifier, compare the set of **enclosing definitions** of `git grep -w` hits (comments, `use` lines and the definition line excluded) with the set of CALLS sources pointing at the target. Script: `~/.cache/orbit-probe/calls.py`. | repo | identifier | kind | grep callers | orbit callers | recall | notes | |---|---|---|---|---|---|---| | spacecraft | `generate_ship` | free fn, cross-crate | 31 | 30 | 97 % | | | spacecraft | `themed_button` | free fn | 25 | 25 | 100 % | | | spacecraft | `micro_exec_tick` | free fn | 17 | 17 | 100 % | | | spacecraft | `border_radius_small` | method (`theme.x()`) | 40 | 34 | 85 % | misses inside nested `with_children(\|p\| …)` closures and on `Res` receivers | | spacecraft | `register_session_resource` | extension-trait method on `App` | 19 | 19 | 100 % | | | spacecraft | `validate` | fn plus same-named method | 46 | 35 | 76 % | misses: `assert!(validate(..))`, `matches!(validate(..))`, `field.field_type.validate(v)` | | spacecraft | `MonitorShared::new` | assoc fn | 1 | 1 | 100 % | | | spacecraft | `Gamemode::{check_victory, spawn_player_units, room_for_joiner, initial_camera_transform}` | via `Box` | 4 | 0 | **0 %** | | | spacecraft | `update_flex_centering` | Bevy system | 2 | 0 | 0 % | passed to `add_systems`, not called | | dirigent | `throttled` | free fn | 79 | 73 | 92 % | | | dirigent | `use_workspace` | Dioxus hook | 41 | 40 | 98 % | | | dirigent | `show_toast` | free fn | 29 | 29 | 100 % | | | dirigent | `SchemaValidator::new` | assoc fn | 47 | 46 | 98 % | | | dirigent | `handle_request` | method | 29 | 28 | 97 % | | | dirigent | `put_session` | via `Arc` field | 12 | 1 | **8 %** | `reg.backend.put_session(..).await` | | dirigent | `count_messages` | dyn / field chain | 7 | 1 | **14 %** | | | dirigent | `#[component]` fns in `rsx!` | Dioxus | 260 comps | 4 with any edge | ~0 % | | All rows above are [V]. - **Trait dispatch resolves when the concrete type is known.** `ReplayMover::update_movement` does `match self { ReplayMover::Pid(m) => m.update_movement(..) }`, and edges go to **each concrete impl method** (`PIDMovement::update_movement`, …). They do not go to the trait declaration. [V] - **Calls to the trait method declaration are rare**, so "who calls `Trait::m`" undercounts generic and `dyn` callers. [V] - **Cross-crate CALLS resolve.** Cross-crate edges: spacecraft 807 of 4038 crate→crate edges, dirigent 613 of 8810. [V] - **Default `orbit sql` scope deduplicates edges to one per (source, target).** `--all` shows **one edge per call site**: `border_radius_small` has 34 edges scoped against 45 with `--all`, and `hex_color` 4 against 200. `repo-map overview` "callers" counts raw call sites. [V] - **Share of callables with no incoming CALLS:** - spacecraft: Function 1840/2961, Method 1019/1272, AssociatedFunction 405/517. - dirigent: Function 1848/3430, Method 1145/1960, AssociatedFunction 222/529. - Most of these are Bevy systems, trait impl methods, handlers and `main`s. **"No callers" is not "dead code".** [V] ## 6. Imports (`gl_imported_symbol`) - **`import_type` values for Rust** [V]: - `Use`: `use a::b::C;` - `UseGroup`: `use a::{B, c::D}`; nested groups are flattened to full paths. - `GlobUse`: `use bevy::prelude::*`; `import_path` = module, `identifier_name` empty. - `AliasedUse`: `as`. - `PubUseGroup`, `ReExport`, `ReExportGlob`, `ReExportAliased`: the `pub use` forms. - `ModDeclaration`: `mod x;` also lands here. - `ExternCrate`. - **Paths are raw and not normalised.** `crate::theme::widgets::themed_button`, `super::theme::widgets::themed_button`, `super::widgets::themed_button` and edition-2018 relative `game_states::is_client_world` all appear as written. Cross-crate imports carry the crate name (`warpspace_engine::game_states::InSession`). To match a symbol, use `identifier_name`, or `import_path LIKE '%::name'`. [V] - **Imports are not resolved to definitions.** `context` labels them `external/unresolved`, even in-repo ones. There is no ImportedSymbol→Definition edge. Re-export chains (`pub use`) are not followed. [V] - **`repo-map imports PATTERN` returns aggregated counts per `(symbol, import_path)`, not the file list.** Use SQL for the files (cookbook C4). [V] - **Imports are kept in files whose definitions were dropped** (cfg-excluded modules, `#[cfg(test)]` files, `vendor/`). Example: `runtime/mod.rs` has 37 import rows and 0 definitions. An import matrix therefore **includes** feature-gated and test code, while definitions and CALLS **exclude** it. [V] - **Crate-to-crate import matrix works** (dirigent: `dirigent_web→dirigent_api` 217, `dirigent_core→dirigent_protocol` 201). [V] ## 7. Workspaces, crates, tests - **All workspace members are indexed.** spacecraft: 25 crates plus the root package. dirigent: 35 crates. The file set is **git-tracked files only** (903 of 2424 `.rs` files on disk in spacecraft; `target/` and untracked files are ignored). [V] - **Code outside the workspace crate trees gets no definitions,** only imports and `macro_rules`. spacecraft `vendor/` (88 tracked `.rs` files of vendored bevy_replicon/lightyear) has 5 definitions, all macros. `archive/*.rs` has 0. [V: counts; I: exact rule, probably module-tree reachability] - **Tests** [V]: - `#[cfg(test)] mod tests { … }` (144 inline in spacecraft, 389 in dirigent) and files pulled in by `#[cfg(test)] mod tests;` (e.g. all of `warpspace_testing/src/tests/*.rs`) have **0 definitions**. `fqn LIKE '%::tests::%'` returns 0. - `crates/*/tests/*.rs` integration tests **are** indexed. Their fns show up in `context` under "Test, fixture, or generated connections". Example: `generate_ship` has 30 test callers from `archetypes::…`, `determinism::…`. - Integration test files with `#![cfg(feature = "…")]` have 0 definitions. - `context --tests` no longer exists. - **cfg evaluation.** Features are **all off**, including `default = [...]`. `debug_assertions` off, `test` off, `target_os = linux`/`unix` on. - Evidence: - `cfg(not(feature="server"))` items are kept, `cfg(feature="server")` items dropped. - Of the four `platform_id` variants, only the `cfg(all(unix, not(macos)))` one is kept. - `cfg(debug_assertions)` consts and fns are dropped. - spacecraft `update_steam_status` (`feature="steam"`, a **default** feature) is dropped. - `#[cfg]` on **methods inside an impl block is not evaluated.** Example: `set_client_latency` under a non-default feature is kept. [V] - Scale: files with zero definitions are spacecraft **212/903** (2.9 of 11.6 MB) and dirigent **176/989** (3.0 of 13.6 MB). Test files are included in those counts. The largest non-test blind spots in dirigent are `dirigent_core/src/runtime/mod.rs`, `config.rs`, `connectors/gateway/mod.rs` and `connectors/opencode.rs`, `dirigent_api/src/acp_controller.rs`, and `dirigent_web/src/server.rs`. Most of dirigent's backend sits behind `server`. [V] - No flag, config key or env var to enable features was found in `--help`, `orbit config list` or `strings`. [I] ## 8. repo-map quality and timings | step | spacecraft | dirigent | |---|---|---| | `orbit index . -t 4` | **4.4–4.7 s**, 0 skipped | **5.3 s**, 0 skipped | | `orbit index .` (auto, 20 cores) | 7.7–8.4 s, **4–7 files skipped** (`timeout_sentinel`, "per-file watchdog killed analysis") | 5.7 s, 0 skipped | | `orbit index . -t 1` | 7.2–8.0 s, 0 skipped | – | | `repo-map` overview | 0.67 s, 132 lines | 0.62 s, 141 lines | | `repo-map tree ` | – | 0.54 s | All timings are [V]. The skipped files were 700–2000-line Bevy UI/lab files. A watchdog skip under default threads silently drops those files' definitions. The first index I saw (by another agent) had 16456 definitions, against 17110 with no skips. - **`overview`: useful.** - Per-crate files/types/callables table. - "Key abstractions": LabScene 15, MovementBehaviour 6, Gamemode 3, EditTarget 3. It **misses** `ArchiveBackend` because of the cross-file impl gap. - "Most-imported defined symbols" (`PlayerId` 83, `InSession` 74, `UiTheme` 57) is a good anchor list for review context. - "Most-called callables" counts call sites and mixes in Python scripts. - The Languages row "unknown 2853" is noise from md/toml/assets. - **`tree `: good.** Example: `Enum ArchiveCapability @L9 / TypeAlias CapabilitySet @L20 / Trait ArchiveBackend @L44 …`. - **`class `: good for structs and traits.** - It lists fields and methods across impl files, with locations. - **Signatures are one-line extracts.** Multi-line signatures truncate to `fn apply_command(` or `async fn append_messages(`, and some show only the name (`delete_session_if_unchanged`, `trash_session`). - **`extends `:** correct depth tree, subject to the same-file and in-repo limits (§4). - **`imports `:** counts per import path (§6). - **`api `:** same data as `tree` plus signatures, with the same truncation. [I: not sampled separately; the signature extractor is shared with `class`] - **`grep`: much better than 0.124.** Match classes make exact-name hits obvious (`put_session` → trait declaration plus `JsonlBackend::put_session`, then body-only hits with the matching line). `grep --path file.rs` with no query prints an outline. It still ranks, caps with "… N more", and does not search excluded code (cfg/test). [V] ## 9. The guide's §7 gotchas for Rust | Guide gotcha | Rust / 0.133 status | |---|---| | 7.1 worktree duplication | **Carries over** for `--all`. Worse for **MCP `run_sql`, which is always unscoped** [V]. Also new: `--all` edges are per call site, while default scope deduplicates them [V]. | | 7.2 dynamic calls invisible | **Much smaller in Rust.** Concrete receivers resolve at 85–100 %. It **still applies** to `dyn Trait` / `Arc` field receivers (0–14 %), macro arguments, Bevy systems registered by fn reference, and Dioxus `rsx!` components [V]. | | 7.3 imports only via statements | **Carries over.** Paths are also raw (`crate::`/`super::`/relative), not resolved, and re-exports are not followed [V]. | | 7.4 third-party bases not nodes | **Carries over** for external traits (silently empty) [V]. New: cross-file impls give no EXTENDS even for in-repo traits [V]. | | 7.5 grep ranked, not exact | **Still true for counting.** Match classes make exact-name lookups reliable. Excluded code (cfg-off, `#[cfg(test)]`) is never searched [V]. | | 7.6 no type/decorator semantics | **Mostly carries over.** `repo-map class` shows (truncated) signatures. Type mentions (fn params, `Query<&T>`, generics) are not edges. `#[derive]` and attributes are not modelled [V]. | | 7.7 stale index, silent | **Changed.** `repo-map` refuses on an unindexed commit. `sql` falls back to **all commits** with a stderr note, which is easy to miss in scripts and mixes repos [V]. `grep` printed `@ ` against an unindexed commit without a warning [V; what it searched is I]. Re-indexing the same path **replaces** the old commit's entry [V]. | | 7.8 ticket numbers | Not orbit-specific. Skipped. | ### New Rust-specific gotchas 1. **cfg blindness.** All features off, `test` off, `debug_assertions` off. Feature-gated modules, whole crates' backends, and all `#[cfg(test)]` unit tests have no definitions and no CALLS. A review of feature-gated code must use `git grep`/read the files. Check `gl_file` rows with zero definitions before trusting a "no callers" or "not found" result (cookbook C6). [V] 2. **Watchdog skips under default threads.** Index with `-t 4`. Assert `processing.skipped_files == 0` in the JSON. [V] 3. **EXTENDS only for same-file impls, in-repo traits and in-repo types.** "Implementors of X" is a lower bound. [V] 4. **`dyn Trait` dispatch has no callers.** For trait methods, search call sites with `orbit grep ''` (body-only hits) or `git grep '\.method('`. [V] 5. **Macros.** Calls inside macro arguments are missed (`assert!`, `matches!`, `vec![seam!(..)]`, `rsx!`). Statement-level `macro_rules!` and proc-macro invocations do produce CALLS. [V] 6. **ECS / framework wiring is not calls.** Bevy `add_systems(Update, f)`, observers and Dioxus components give no incoming edges. [V] 7. **CALLS→Struct means construction, not usage.** "Who uses component `Health`": 4 of 52 enclosing definitions found. Use `repo-map imports` or SQL on imports, or grep. [V] 8. **fqn root is the cargo target name, and collides** (lib vs bin `warpspace`). Integration tests are rooted at the file stem. Group by `file_path`. [V] 9. **`start_line` includes doc comments and attributes.** This helps when mapping diff hunks to definitions, but a hunk touching only a doc comment counts as touching the item. [V] 10. **Default scope deduplicates edges; `--all` does not.** Do not compare counts across the two. [V] 11. **SQL papercuts:** - `target` is a reserved word in DuckDB (`AS target` fails); use `tgt`. - An empty result prints **nothing** (no header), exit 0. - zsh loops: don't name a variable `path`. That is a shell issue, not orbit. [V] 12. **Shared DB and concurrent agents.** One `graph.duckdb` per user. Other sessions re-index the same repos, and `repo_path` rows are replaced. **`--db ` isolates cleanly.** `orbit index . -t 4 --db ~/.cache/orbit-probe/scratch.duckdb` built a 22.8 MB DB with 17110 definitions. `list`, `sql` and `repo-map extends` with the same `--db` worked, `rm` of the file was the whole cleanup, and the shared DB was untouched (still 4 entries). [V] `ORBIT_DATA_DIR` is untested. [I] ## 10. Multi-commit / worktree comparison Steps [V]: 1. `git -C ~/Projects/spacecraft worktree add --detach ~/.cache/orbit-probe/wt-spacecraft bfbbcd96`. This commit is 4 commits behind `main`, with 6 `.rs` files changed (+363). 2. `orbit index . -t 4` inside the worktree: 3.8 s, 17074 definitions. `git status --porcelain --ignored` was unchanged. 3. `_orbit_manifest` gained a row with `repo_path=/home/g4b/.cache/orbit-probe/wt-spacecraft`, `parent_repo_path=/home/g4b/Projects/spacecraft`, its own `project_id`, and `branch='HEAD detached at bfbbcd96'`. 4. Definition IDs are unique per commit: 0 shared IDs between the two commits. Join across commits on `(fqn, definition_type, file_path)`. 5. `orbit sql --repo …` and `orbit repo-map --repo …` scope to that commit. 6. `orbit sql --all -f defdiff.sql` (cookbook C8) listed exactly the expected changes: - added: `warpspace_ui::replay_browser::*` (plugin, structs, fns), `MenuAction::OpenReplay`, `MenuState::ReplayBrowser`, `warpspace::start_replay_session`, `auto_start_replay`, `Args::replay` - resized: `handle_menu_actions` 70→82, `main` 248→264, `spawn_main_menu_root` 181→188 - Python script adds and removes in the same range 7. Re-creating the worktree at another commit and re-indexing the same path **replaced** that path's entry. 8. Cleanup: `git worktree remove` + `git worktree prune`. `git worktree list` is back to `main` + `.worktrees/strategy-mvp`. What is left over: - **Orbit keeps the index entry.** `orbit list` still shows `/home/g4b/.cache/orbit-probe/wt-spacecraft @ bfbbcd96`, with 17074 definitions in the DB. - There is **no CLI command to remove an entry**: no prune/forget/remove in `--help`. `orbit index ` fails with "failed to find git top-level". - Removing the entry would need direct DuckDB surgery or a separate `--db`. I did neither, because the DB is shared. - Scratch files in `~/.cache/orbit-probe/` (about 250 KB of scripts and outputs) remain on purpose for phase 2. No scratch DB. Implication for the skill: branch-vs-main review works well at definition level. Only length changes are detected, so pair it with `git diff -U0` hunks for body edits. Worktree entries accumulate in the shared DB, so a dedicated `--db` per review is the clean pattern. `--db` isolation and deleting the file afterwards were verified (gotcha 12). Indexing two commits into one scratch DB was not tried, but it should behave like the shared DB [I]. ## Tested cookbook All of these ran on the repos above. Default scope unless marked. ```bash # Index (always check skipped_files) orbit index . -t 4 | grep -E '"definitions"|"skipped_files"' orbit list # Orientation orbit repo-map # overview orbit repo-map tree crates/dirigent_archivist/src/backend orbit repo-map class JsonlBackend # fields + methods across impl files orbit repo-map extends MovementBehaviour # same-file in-repo implementors (lower bound) orbit repo-map imports themed_button # counts per import path orbit grep put_session --kind Method --path crates/dirigent_archivist orbit grep --path crates/dirigent_archivist/src/backend/health.rs # outline orbit context Definition: # source + callers/callees + test callers ``` **C1. Definition types (Rust only)** ```sql SELECT d.definition_type, count(*) n FROM gl_definition d JOIN gl_file f ON f.path = d.file_path WHERE f.language = 'rust' GROUP BY 1 ORDER BY 2 DESC; ``` **C2. Callers of one definition** (distinct callers under default scope; add `--all` plus a commit filter to count call sites) ```sql SELECT s.fqn AS caller, s.file_path, s.start_line FROM gl_edge e JOIN gl_definition s ON s.id = e.source_id JOIN gl_definition t ON t.id = e.target_id WHERE e.relationship_kind = 'CALLS' AND t.fqn = 'dirigent_web::components::toast::show_toast' ORDER BY s.file_path; ``` **C3. Cross-crate call matrix** ```sql SELECT split_part(s.file_path,'/',2) src_crate, split_part(t.file_path,'/',2) dst_crate, count(*) n FROM gl_edge e JOIN gl_definition s ON s.id=e.source_id JOIN gl_definition t ON t.id=e.target_id WHERE e.relationship_kind='CALLS' AND s.file_path LIKE 'crates/%' AND t.file_path LIKE 'crates/%' AND split_part(s.file_path,'/',2) <> split_part(t.file_path,'/',2) GROUP BY ALL ORDER BY n DESC; ``` **C4. Files importing a symbol; crate import matrix** (includes cfg-off and test files) ```sql SELECT file_path, import_type, import_path FROM gl_imported_symbol WHERE identifier_name = 'themed_button' ORDER BY 1; SELECT split_part(i.file_path,'/',2) from_crate, split_part(i.import_path,'::',1) to_crate, count(*) n FROM gl_imported_symbol i WHERE i.file_path LIKE 'crates/%' AND i.import_path LIKE 'dirigent_%' AND split_part(i.file_path,'/',2) <> split_part(i.import_path,'::',1) GROUP BY ALL ORDER BY n DESC; ``` **C5. Trait implementors via EXTENDS** (lower bound) ```sql SELECT s.fqn implementor, s.definition_type, t.name AS trait_name, s.file_path FROM gl_edge e JOIN gl_definition s ON s.id=e.source_id JOIN gl_definition t ON t.id=e.target_id WHERE e.relationship_kind='EXTENDS' AND t.definition_type='Trait' ORDER BY trait_name; ``` **C6. Blind-spot check: Rust files with no definitions** (cfg-off, test-only, vendor) ```sql SELECT f.path, f.size_bytes FROM gl_file f WHERE f.language='rust' AND NOT EXISTS (SELECT 1 FROM gl_definition d WHERE d.file_path=f.path) ORDER BY f.size_bytes DESC LIMIT 20; ``` **C7. Candidate implementors by method-name overlap** (covers cross-file impls, but noisy: it also matched the `Archivist` coordinator with 7/21) ```sql WITH tm AS ( SELECT m.name FROM gl_definition t JOIN gl_definition m ON m.fqn LIKE t.fqn || '::%' AND m.definition_type='Method' WHERE t.definition_type='Trait' AND t.name='ArchiveBackend') SELECT regexp_replace(d.fqn,'::[^:]+$','') implementor, count(DISTINCT d.name) matched, (SELECT count(*) FROM tm) trait_methods FROM gl_definition d WHERE d.definition_type='Method' AND d.name IN (SELECT name FROM tm) GROUP BY 1 HAVING count(DISTINCT d.name) >= 3 ORDER BY matched DESC; ``` **C8. Definition diff between two indexed commits** (`orbit sql --all -f defdiff.sql`; SHAs from `orbit list`) ```sql WITH a AS (SELECT fqn, definition_type, file_path, end_line-start_line+1 len FROM gl_definition WHERE commit_sha=''), b AS (SELECT fqn, definition_type, file_path, end_line-start_line+1 len FROM gl_definition WHERE commit_sha='') SELECT CASE WHEN a.fqn IS NULL THEN 'added' WHEN b.fqn IS NULL THEN 'removed' ELSE 'resized' END change, coalesce(b.fqn,a.fqn) fqn, coalesce(b.definition_type,a.definition_type) kind, coalesce(b.file_path,a.file_path) file, a.len old_len, b.len new_len FROM a FULL OUTER JOIN b USING (fqn, definition_type, file_path) WHERE a.fqn IS NULL OR b.fqn IS NULL OR a.len <> b.len ORDER BY file, fqn; ``` **C9. Callables with no incoming CALLS** (triage only; Bevy systems, trait impls and handlers dominate) ```sql SELECT d.definition_type, count(*) total, count(*) FILTER (WHERE NOT EXISTS (SELECT 1 FROM gl_edge e WHERE e.relationship_kind='CALLS' AND e.target_id=d.id)) no_incoming FROM gl_definition d WHERE d.file_path LIKE '%.rs' AND d.definition_type IN ('Function','Method','AssociatedFunction') GROUP BY 1; ``` ## Open points for phase 2 - Blast radius across two commits: join CALLS on fqn between the worktree and main entries. Measure how much the cfg and `dyn` gaps hurt on a real diff. - Run a full per-review lifecycle in one scratch `--db`: index main and the branch worktree, diff, delete the file. Single-commit `--db` isolation is already verified. - Decide how the skill should state cfg blindness. Options: always run C6 first, or grep feature-gated files touched by the diff. ## Refinement 2026-09-29 (from phase 2) Phase 2 (`phase-2-review-workflows.md`) found that orbit indexes the **working tree**, including uncommitted and untracked files, but labels the index with HEAD's commit. Any claim above that an index reflects a commit exactly should be read with this in mind.