- Rust 99.8%
- Shell 0.2%
|
All checks were successful
CI / cargo fmt (push) Successful in 16s
CI / OSS corpus tier-3 scale (weekly) (push) Has been skipped
CI / cargo clippy (push) Successful in 1m11s
CI / cargo check (MSRV 1.88) (push) Successful in 1m56s
CI / cargo check (windows-gnu) (push) Successful in 2m30s
CI / cargo deny (push) Successful in 4m0s
CI (Windows) / fmt + clippy + build + test (windows) (push) Successful in 6m37s
CI / cargo test (push) Successful in 3m23s
CI / OSS corpus (tier 1) (push) Successful in 9m5s
Release Build / Generate Version (push) Successful in 26s
Release Build / Native Windows CI green (push) Successful in 31s
Release Build / Build linux-x86_64 (push) Successful in 8m44s
Release Build / Build linux-x86_64-musl (push) Successful in 9m14s
Release Build / Build linux-aarch64 (push) Successful in 9m31s
Release Build / Build windows-x86_64 (push) Successful in 9m39s
Release Build / Create Forgejo Release (push) Successful in 2m6s
|
||
|---|---|---|
| .forgejo/workflows | ||
| _prdoc | ||
| crates | ||
| tests | ||
| .gitignore | ||
| .mcp.json.example | ||
| ARCHITECTURE.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CONTRIBUTING.md | ||
| deny.toml | ||
| README.md | ||
code-index
Fast structural code index for AI coding agents, exposed over MCP.
Six languages indexed today: Rust, Python, TypeScript / JavaScript, C#, PHP, Ruby. Live updates via filesystem watcher. Symbols, references, imports, full-text search. Local SQLite, no network, no service.
Quick start
Grab a release archive from
git.h-dv.de/h-dv/code-index/releases
(linux-x86_64, linux-aarch64, windows-x86_64; bundles the three
binaries plus a per-platform README and .code-index.toml.example),
or build from source:
# build
cargo build --release
# initialise a project (one-off)
./target/release/code-index init
./target/release/code-index index
# health check
./target/release/code-index doctor
Wire into Claude Code (or any MCP client):
// claude_desktop_config.json or equivalent
{
"mcpServers": {
"code-index": {
"command": "/path/to/code-index-mcp",
"args": ["--root", "/path/to/your/project"]
}
}
}
That's it. The MCP server auto-spawns the daemon on first connect and
initialize returns in milliseconds (see I015) regardless of project
size; the daemon runs its initial reconciliation in the background and
lives until 30 minutes of idle (configurable) or your editor session
ends. All twelve MCP tools, four resources, and two prompts are available
immediately — calls made during the cold-start window either succeed,
return state: "reconciling", or return a structured warming_up
error the agent retries automatically.
If startup reconciliation fails, the RPC daemon stays alive and serves
the last durable snapshot instead of entering an index/fail/respawn loop.
project_overview reports state: "degraded" with state_detail, and
the daemon retries in place with an exponential cooldown that is never
shorter than the failed attempt itself. The state clears after recovery.
Upgrading to 0.13
- Gitignored files are no longer indexed by the live watcher. It now applies the same ignore-FILE stack the cold walk does, so the two agree. If you were relying on a gitignored path being searchable, it was only ever searchable until the next re-walk deleted it.
- On Windows,
index.dband its sidecars are now owner-only. They previously inherited the parent directory's ACLs, whiledaemon.toml— which holds only the token guarding access to that index — did not. See the note below on what the index actually holds. WatchOptsgainsignore_change_rewalk;WatcherStatswas removed in 0.12.
Upgrading to 0.12
Four changes you will notice:
- A nested git checkout is no longer indexed as part of the project.
A subdirectory carrying its own
.gitor.jj— a submodule, a linked worktree, a nested clone — belongs to another project at another commit, and indexing it here duplicated every symbol into these tables. If you relied on that, index it as its own project withcode-index link add <name> <path>, which also gives it correct resolution. The walker logs each one it prunes. - Hidden paths are purged from existing indexes at the next reconcile.
The live watcher used to index what the cold walk excluded, so
.env,.claude/,.vscode/and.gitinternals could end up in the index — contents included, retrievable throughsearch_text. Those rows go away on upgrade. Nothing you index today is lost: the cold walk never yielded them. project_overviewcan reportstate: "resyncing". A full re-walk is in progress. Results stay readable and every tool still answers; freshness is unverified until it clears, which it does on its own.WatcherStatsis removed fromcode-index-indexer's public API. Nothing ever populated it.
A branch switch now heals in seconds instead of waiting out the periodic reconcile, and on Windows a large event burst — where the OS gives no overflow signal at all — triggers recovery once the filesystem goes quiet.
The live watcher now applies the ignore-FILE stack too (.gitignore,
.ignore, .git/info/exclude, .code-index-ignore, and your global
core.excludesFile), so it agrees with a cold index rather than
indexing gitignored files and deleting them again on the next re-walk.
Editing a .gitignore while the daemon runs is honoured immediately.
The index holds what your working tree holds
.code-index/index.db is a structural copy of every file the walker
indexes, plus their text. Treat it with the same care as the source
tree itself — it is not a filtered or sanitised view, and no exclusion
rule makes it one. What the exclusion rules give you is AGREEMENT
between a cold index and a live one, not a security boundary.
So the index is protected at the level source code is: the state
directory is created 0700, index.db is pre-created 0600 before
SQLite opens it (a chmod afterwards would leave a window), the WAL and
shm sidecars inherit that, and on Windows all three get a protected
owner-only DACL — the same hardening daemon.toml gets, because
protecting the capability token but not the index it guards is not a
threat model. .code-index/ is added to .git/info/exclude so the
index never follows your source into a commit.
Project-scoped .mcp.json: gotcha
If you drop a per-project .mcp.json (instead of the user-scope
claude_desktop_config.json shown above), Claude Code will not
register the server retroactively in an already-running session. Symptom:
code-index tools don't appear in the agent's tool list, and the agent
silently does without them. Two paths out:
- Restart Claude Code in the project dir and approve the prompt. When a
project-scoped
.mcp.jsonis detected for the first time, Claude Code asks the user to approve each declared server. Decline once and it stays declined. - Or pin code-index at user scope so it's available everywhere
without per-project approval:
claude mcp add code-index code-index-mcp claude mcp list # verify
code-index doctor includes an mcp registration check that surfaces
the most common cause ("project hasn't been opened in Claude Code yet")
with a pointer to claude mcp list.
What the agent gets
- Handles, not blobs. Symbol responses carry IDs, locations, and
signatures — never embedded source. The agent calls
read_codeonly when it actually needs bytes. - Paginated, token-bounded responses. Lists return
{results, total, next_cursor};read_codeis hard-capped at 4000 tokens with atruncated: trueflag on overflow. - Structured errors.
{error, query?, did_you_mean[], hint?}— not free-text strings. - Live freshness. Edits, creates, renames, and deletes in your editor flow into the DB within a few hundred milliseconds. Atomic saves are coalesced, and references re-resolve per change batch — cheaply, thanks to an outline-hash firewall that skips the global pass when only function bodies changed.
- Precise-enough references. Resolution is three-tiered: symbol
visibility (extracted for all six languages) gates candidates,
cross-file bindings additionally require reachability (same dir,
or an import naming the candidate's file, identifier, or container
type — so a lone exported
getno longer swallows every stdlib.get()call), same-file definitions win over same-name twins elsewhere, and the ref's own imports break remaining ties; unqualified heuristic shortcuts never apply to qualified calls, soVec::new()-style externals stay honestly unresolved. On this repository the resolver reaches ~21% resolved refs — the exact ratio is visible asrefs_resolvedinproject_overview. response_format: "concise"on the six list tools cuts rows to handles + signatures (roughly half the tokens) when scanning many results.find_referencestakesexclude_tests: trueto hide test-file references, with an honestexcluded_test_refscount.
Orientation and diff awareness
Two tools answer the questions agents ask at the start and end of a task:
// "Where do I even look?" — token-budgeted architectural map, ranked
// by personalized PageRank over the reference graph. Pass `focus`
// to pull a task-relevant neighborhood to the top.
repo_map { "token_budget": 1500, "focus": ["resolve_ref_targets"] }
// "What did I just touch, and what depends on it?" — symbols
// intersecting your git diff, with per-symbol direct_callers.
// Replaces the git-diff + N-greps ritual before a commit.
changed_symbols { "base": "HEAD", "staged": false }
In watcher-backed sessions, changed_symbols and review_diff wait
briefly for changed source files whose index row is behind disk. If
reconciliation does not finish inside the bounded wait they return
index_updating; they never return shifted symbol ids/spans as usable
output. Snapshot mode (--no-daemon) cannot heal and instead preserves
the explicit per-file index_stale: true diagnostic. A changed file the
index does not cover at all — a skip directory (dist/, target/,
node_modules/, …), any per-directory ignore rule the walker honours
(.code-index-ignore, a nested .gitignore, .git/info/exclude), a
hidden dot-file, or one over the file-size cap — never heals, so it is
disclosed per file as not_indexed: true instead of blocking the call.
The project root may sit anywhere inside the work tree (a monorepo
member, a workspace crate): git's output is rebased onto it, so paths
stay project-relative and a sibling project's changes never appear. A
file git reports as deleted that is still on disk was untracked
(git rm --cached), not removed: it is reported as
change: "untracked" with no symbol rows, never as an API break.
Other corrective misses (for example an unknown focus) carry
did_you_mean suggestions.
The six languages
| Language | Extensions | Plugin |
|---|---|---|
| Rust | .rs |
tree-sitter-rust |
| Python | .py, .pyi, .pyw |
tree-sitter-python |
| TypeScript / JavaScript | .ts .tsx .js .jsx .mjs .cjs |
tree-sitter-typescript / -javascript |
| C# | .cs (skips *.Designer.cs, *.g.cs) |
tree-sitter-c-sharp |
| PHP | .php .php3 .php4 .php5 .phtml |
tree-sitter-php |
| Ruby | .rb .rake .gemspec, Rakefile, Gemfile |
tree-sitter-ruby |
Ruby extraction is Rails-aware: association macros (has_many,
belongs_to, has_one, has_and_belongs_to_many) emit a type reference
to the associated model class (honoring class_name: and singularizing
plural names), and mixins (include/extend/prepend) reference the
mixed-in module.
Default-skipped directories: node_modules, vendor, __pycache__,
.venv, .tox, dist, build, .next, .code-index, plus
target/ (Rust roots only) and bin//obj/ (.NET roots only).
Override via .code-index-ignore (same syntax as .gitignore).
Binaries
| Binary | Purpose |
|---|---|
code-index |
CLI for one-off indexing, watching, doctor |
code-index-daemon |
Long-lived watcher + RPC server |
code-index-mcp |
MCP stdio bridge for AI agents |
You normally only invoke code-index-mcp; it spawns the daemon for you.
CLI reference
code-index init # write a default .code-index.toml
code-index index # one-shot index of the current project
code-index watch # initial index + live updates (foreground)
code-index doctor # diagnose: DB, schema, daemon, inotify, plugins
code-index link add <name> <path> # add a [[links]] entry (workspace links, I011)
code-index link list [--json] # list configured links + live daemon status
code-index link remove <name> # remove a [[links]] entry
Add --root <path> to operate on a project other than the current dir.
The link subcommands manage the [[links]] array in .code-index.toml
without you having to hand-edit TOML. Comments and whitespace in the file
are preserved; writes are atomic; validation matches the MCP runtime
exactly (reserved name, duplicates, self-cycle, link-link path collisions
all caught at write time).
Auto-detected project root
If you don't pass --root, code-index walks up from your CWD looking for:
.code-index.toml— innermost wins. Drop one in any directory to pin that as the root regardless of any marker above it.code-index initcreates one for you..git/— innermost wins. Stops the walk: a workspace atrepo/with sub-crates atrepo/crates/foo/is correctly detected asrepo/even when you runcode-indexfrom inside a sub-crate. A stray~/.git(dotfiles repo) won't trip detection because a closer.git/always wins.- Outermost
Cargo.toml/package.json/pyproject.toml/composer.json/*.csproj/*.sln— only when no.git/is found anywhere up the chain. Catches non-git workspaces.
The walk is bounded at $HOME so a marker far above your home directory
can't poison detection.
Workspace links — querying multiple projects from one MCP entry
A customer install that extends a base product, a fork that needs to compare against upstream, an app that sits on a vendored library — common patterns where one Claude / Cursor / Cline session needs to query two indices at once.
Drop a [workspace] + [[links]] block into the primary project's
.code-index.toml:
[workspace]
name = "h-dv"
[[links]]
name = "mainproject" # routing key for tool calls
path = "../../code/mainproject/24.0/source" # absolute, `~/…`, or relative to this file
relationship = "base" # base | dependency | fork_source | sibling
description = "mainproject 24.0 — base product"
That's it. Your existing MCP client config keeps one entry pointing at the primary root; the server attaches a daemon for each link at startup. Tools route across projects three different ways:
// (a) Name/phrase searches FAN OUT by default — primary + every
// available link. Each hit carries a `project` tag.
search_symbols { "query": "mainprojectService" }
// → [{ name: "mainprojectService", project: "primary", … },
// { name: "mainprojectService", project: "mainproject", … }]
// Pin a single project by passing its name:
search_symbols { "query": "mainprojectService", "project": "mainproject" }
// (b) Path-shaped tools (file_outline, read_code, get_dependencies)
// AUTO-ROUTE: an absolute path picks the owning project by
// longest root-prefix; relative paths default to primary.
file_outline { "path": "/abs/path/under/mainproject/foo.cs" } // → mainproject
file_outline { "path": "src/foo.rs" } // → primary
// (c) Symbol-id tools (get_symbol, find_references, find_callers,
// find_callees) are project-local: pass the `project` from
// the row that produced the id.
find_references { "symbol_id": 42, "project": "mainproject" }
project_overview on primary lists every linked project with its
index health, and the MCP info.instructions block names each
project + canonical root on initialize — so a fresh agent
discovers the topology without any extra calls.
What it does: fan-out + path auto-routing + isolation,
structured errors when a project name is unknown (project_not_found
with did_you_mean), configured-but-unavailable
(project_not_available — daemon failed to spawn or path was
unreachable), or a path lives outside every indexed root
(path_outside_known_roots with the known roots in did_you_mean).
What it doesn't do (yet): cross-project ref resolution
(find_callers on a mainproject symbol won't surface h-dv callers).
Phase 2 adds find_base_definition / find_overrides /
find_call_into_base via qualified_name joins over ATTACH DATABASE.
See _prdoc/missions/I011-workspace-links.md for the full design.
Architecture
See ARCHITECTURE.md for the design rationale and the
diagrams. See CONTRIBUTING.md to build, test, and
add a language plugin. Specs and adaptation reports live under
_prdoc/.
Status
Current release: v0.9.0 (changelog).
| Milestone | Scope | Shipped in |
|---|---|---|
| M1 | Workspace, schema, Rust plugin, CLI | pre-0.1.0 |
| M2 | Parse pool, writer thread, layered change detection | pre-0.1.0 |
| I003 | C#, Python, TS/JS, PHP plugins | pre-0.1.0 |
| M4 | MCP server: 10 tools, 4 resources, 2 prompts | 0.1.0 |
| I004 | notify watcher | 0.1.0 |
| I005 | Daemon, RPC, lockfile, auto-spawn | 0.1.0 |
| M5 | doctor, docs, metrics | 0.1.0 |
| I011 | Workspace links, multi-project routing | 0.2.0 |
| I013 | CLI link management (link add|list|remove) |
0.2.0 |
| I015 | Non-blocking startup, async daemon attach, warming_up |
0.3.0 |
| I014 | Periodic safety-net reconcile (heals missed watcher events) | 0.3.1 |
| I024 | Ruby plugin (Rails-aware) + parser recursion-depth guards | 0.4.0 |
| I024b | Recursion guards extended to py/cs/ts type-expression helpers | 0.4.1 |
| I017 | CI green + release pipeline fixed; multi-platform binaries (linux x86_64/musl/aarch64, windows) | 0.4.2 |
| I018 | Self-healing daemon reconnect + root-identity handshake + reconnect tracing (issue #7) | 0.5.0 |
| I019 | Precision tier: visibility-gated 3-tier ref resolution, outline-hash firewall, roles v1 (exclude_tests), response_format, repo_map + changed_symbols (12 tools), schema v7 |
0.5.6 |
| dogfood | Qualified-import reverse-deps, module-aware exclude_tests, bounded changed_symbols, test-role backfill (schema v11) |
0.5.7 |
| review | Materialized ref_count / symbol_edges (search ranking + repo_map), FTS5 query simplification, crate-scoped reverse-deps, import-prefix index, Windows token-DACL test + PR cross-compile gate (schema v14) |
0.5.9 |
| I020 | Production hardening: resolver perf on generated code (candidate-file resolution, 18+ min → ~21 s cold index), CLI write-firewall while daemon is reachable, Ctrl-C SQLite interrupt with rollback, RPC on blocking pool, routine quick_check |
0.5.11 |
| I021 | MCP dogfood hardening: ref_count in get_symbol/symbol_at, SQLite errors no longer masked as not-found, paginated resource envelopes (no mid-JSON clipping), Windows installed-daemon discovery, truthful MSRV 1.88 + CI gate, sustained-load/staged-install/watcher-convergence e2es |
0.5.12 |
| I022 | Windows dogfood fixes: changed_symbols no longer hangs (git subprocess moved off tokio's Windows child-reaper onto spawn_blocking), case-insensitive path routing (E:\Temp vs E:\TEMP), resolver tier-count assert corrected (no debug-build writer panic), e2e harness hardened (CARGO_BIN_EXE_*, persistent stdio reader, live-git regression suite) |
0.5.13 |
| I021b | Resolution precision + role freshness: tier-1b reachability gate (stdlib-shaped names no longer bind cross-crate; ~350 false refs → 0), role-recompute migration (exclude_tests exact on stale DBs), operator-query line attribution in search_text |
0.5.14 |
| I022b | Diff truth + upgrade equivalence: changed_symbols gains a deleted category with exact base-blob classification, file_outline/repo_map honesty fixes, m0016 fresh-index-equivalence migration (schema v16: resolutions, aggregates, and stale symbol metadata all heal on upgrade), migration busy-wait for long heals |
0.5.14 |
| I023 | Precision residuals + tool polish: method-call same-dir gate (phantom .get() edges 176 → 4), origin-aware name/container imports (use anyhow::Result no longer reaches core's Result), impl symbols out of candidate pools (321 poisoned refs now resolve), m0017 re-heal (schema v17); changed_symbols concise, find_callers exclude_tests, fingerprint-bound cursors, top_level_only, count-only limit=0, honest spans/errors |
0.5.15 |
| I025 | Qualifier capture (schema v18): all six plugins emit the qualifier text; anchored tier-1Q resolution — Writer::new-class refs resolve by container while std::fs::write/Vec::new/tempfile::tempdir-class externals (700+ measured phantoms) go honestly NULL; PHP qualified calls resolvable for the first time; qualifier on ref rows |
0.5.16 |
| I026 | Pool hygiene (schema v19): impl-child associated types/consts out of bare-name candidate pools (30 phantom Err(...) bindings → 0); Self::/static::/parent:: resolve same-file only — package fallback reserved for crate/super |
0.5.17 |
| I027 | Daemon respawn on reconnect: the idle-shutdown exit (30 min, by design) no longer bricks a session — the next tool call respawns the daemon and self-heals, instead of failing until the MCP server restarts | 0.5.18 |
| I028 | Brainstorm-found fixes: watcher relative_path drift (single-file-root watch matching), rust plugin type-ref recursion DoS guard (deep-nesting overflow test now paritized across all 6 languages), dead-code removal (Writer single-message conveniences, span_of) |
0.5.19 |
| I029 | Graph Intelligence v1 (#33 #26 #22 #36): CI dot-dir indexing, explain_dependency resolved dependency paths, change_impact transitive blast radius + test-role dependency proxies with confidence block, resolution_gaps reason-coded blind-spot telemetry, per-language resolution in overview; 15 tools; 7-language graph e2e |
0.5.20 |
| I029b | Graph-tool honesty hardening: test-role results renamed to static dependency proxies (test_role_dependents/test_role_files/no_test_role_dependents), qualified_unresolved_refs, graph_semantics disclosure, confidence over the full reference universe, 1M-live-edge guard; two-way wire compat across daemon-version skew (normalize + one-release dual-emit); daemon-RPC-path e2e |
0.5.21 |
| I030 | Agent Change Safety v1 (#29 #25 #34, schema v20): receiver/binding resolution (tier 1R, all 6 languages, rust method_call 6.6%→7.4% with zero phantoms), safe_delete + check_rename evidence preflight (word-boundary string pass), review_diff post-edit reviewer; 18 tools; 43 review findings fixed across two rounds; dogfood bench harness |
0.6.0 |
| I030b | Agent-change-safety honesty round: string→text occurrence terminology (two-way wire shim incl. reason-code values), review_diff cross-batch seed-exclusion + measured impact_batches + coverage_capped finding, change_impact seed-role handling | 0.6.1 |
| I031 | Stable symbol handles (#37) + minimal context_pack (#35): cih1_ versioned durable handles derived from stored columns (no schema change) with exact/moved_or_changed/ambiguous/missing resolution, resolve_handle, handles on symbol responses; context_pack (single-project, budget-classed, evidence-linked) from symbol/handle/path/diff/task-term seeds; 20 tools; 3 deep-review rounds |
0.7.0 |
| I031.1 | stable_handle broadened to changed_symbols (added/modified rows) + review_diff (symbol-anchored findings) — exact-resolving (full qualified_name + untruncated signature), MCP-side, no wire skew; closes #37's review_diff round-trip clause literally; 3-agent adversarial review (0 defects, 3 LOW fixed) |
0.7.1 |
| I032 | Handle v2 (cih2_, rename-tolerant shape_fingerprint; rename → ambiguous candidate only, never a minted id; v1 still decodable) + review_diff per-file untested aggregation with navigable members + same-package counterevidence downgrade + context_pack rewrite (rank-before-cap path pools, dependencies class, strict complete-response budget). Ultradeep 13-agent review: 6 confirmed / 0 refuted, all fixed + independently re-verified (handle wire-skew shim, honest dependency/seed disclosures, discriminating package_component) |
0.8.0 |
| I033 | Multilingual precision + zero-phantom regression gate (#38): machine-readable per-language edge oracles (positives + decoys) + precision_gate.rs asserting phantom==0 / recall / per-(lang×kind) parity / determinism across all 6 languages — automates the manual-dogfood guard, proven to catch the I023 same-dir method-call phantom class (a single-candidate receiver-blind decoy). Plus #30.6 per-file index_health in project_overview (file_health + honest no_candidate-vs-internal_missed split + internal_resolution_pct). 3-agent adversarial review: 3 findings fixed (gate-vacuity decoy, wire-skew degrade, external→no_candidate honesty rename) |
0.8.1 |
| I034 | Production trust + freshness: watcher-backed diff tools (changed_symbols/review_diff) hold a bounded freshness barrier and return structured index_updating rather than shifted ids/spans; a changed file the index provably cannot cover (skip dir / ignore rule / size cap) is disclosed per-file as not_indexed instead of blocking the call; staged mode short-circuits; retries poll a cheap probe so the full collect runs at most twice. Uniform non-authoritative confidence block on find_references/find_callers/find_callees (+ graph_semantics/verification on safe_delete/check_rename). 21-finding adversarial review — headline: the barrier as written permanently bricked both diff tools in any repo with a tracked file under dist//bin//obj/ |
0.8.2 |
| I035 | Test-hardening + live-bug sweep: a coverage audit found the suite ran almost entirely --no-daemon while daemon mode is production — five RPC arms could be deleted with green CI. Fixes 10 live bugs at HEAD (the I034 brick still live via nested .gitignore/.git/info/exclude/hidden files; subdirectory project roots reporting a silent false-clean; spaced paths silently dropped; git rm --cached fabricating an api_break_deleted; symbol_names bricking instead of degrading; four filters silently ignored by an older daemon; array-reply graph shims; zero-commit repos; server runtime files reported as user work) — each with a mutation-proven regression test. Adds the reusable harness that closes the class: a daemon⇄snapshot parity runner + RPC_METHODS completeness gate, a both-direction wire-skew simulator (first test of the handle-v2 shape_fingerprint shim), a disclosure-field value-pinning table, precision-gate decoys for C#/PHP/Ruby + the missing JavaScript oracle + a structural anti-vacuity guard, and daemon-leak/CARGO_TARGET_DIR test-infra fixes. (v0.8.4: fix a CI-only flake — the new disclosure fixtures git commit without a user.email/user.name, which a fresh CI runner lacks; the shared test git helper now injects a throwaway identity) |
0.8.4 |
| I036 | MCP client compatibility: Google Antigravity could not connect at all — it opens with a non-standard server/discover probe before initialize, and rmcp 1.5's handshake aborts the process on anything but a ping, so the client's own initialize hit a closed pipe. Replaces rmcp's stock stdio transport with a tolerant one that owns the framing: a pre-initialize probe is answered -32601, a known-but-early method -32600 (never "method not found" for a method we serve), a malformed initialize -32602, and any undecodable line -32700 — instead of ending the session. Ids are echoed verbatim from the raw JSON, so ids rmcp cannot model get an answer rather than hanging the client. Round-2 review caught a critical regression in the fix itself: receive() was not cancellation-safe (rmcp polls it in a select!), silently losing ~3% of a pipelined burst. Also: [[links]] path now expands ~, and refuses rather than root-joining a ~ it cannot expand. 46 review agents over two rounds, 27 confirmed findings, 11 mutations all caught. |
0.8.5 |
| I037 | Dogfood fixes: using v0.8.5 on its own repo surfaced a phantom — find_callers reported self.replies.send(..) (an mpsc sender) as a resolved call to the file's own send. A method call whose receiver EXISTS but cannot be typed is now excluded from the receiver-blind locality tiers, across all six languages; the evidence-gated import tiers are untouched. Macro bodies now carry their receiver too (they did not, which would have cost 37 correct resolutions). Also: enum-variant paths count as uses of the enum — Severity read ref_count 1 against 47 occurrences because unit variants and every match pattern were dark (now 45). Measured on this repo: 0 refs changed target in either change. A fourth finding (inline pkg::Type::f() callers carry no import row, so change_impact can omit them) is DOCUMENTED and pinned by a test rather than closed with a heuristic — the one-pass repair was implemented, measured to change nothing, and reverted. |
0.8.6 |
| I038 | Inline callers become reachable: cfg.resolve_links(..) resolved in a crate that wrote use pkg::Config and NOT in one calling pkg::Config::load(..) inline, so change_impact omitted real consumers. The obvious repair — treat a resolved type ref as file reachability — was implemented and measured to admit 176 phantoms for 1 correct answer (every r.get(0) on a rusqlite Row bound to ReadPool::get), then reverted. What shipped instead needs TWO independent facts: the initializer's callee must be proven by its indexed signature to yield the type (-> Self, -> Result<Self>), and the calling file must NAME that type in a qualified path with matching package origin. Result: +2 resolutions, 0 refs changed target, 0 get-shaped phantoms. |
0.8.7 |
| I039 | An OSS corpus, and the four defects it found. Seven real repositories pinned by sha, graded by oracle-free invariants, self-oracling mutations, a live-watcher history replay and an exact-count ratchet. A spike falsified four predictions first (parse, determinism, cold==incremental and deletion-mutation were already clean), redirecting the work at the watcher and at scale. Found: sticky anchored resolution (#52), superlinear resolve cost (#53/#54, 5.79s->1.35s on the dominant statement), CommonJS invisibility (#56), and relative specifiers matched by bare stem (#57, ./utils from lib/ reaching test/utils.js). |
0.9.0 |