The index is reachable only over MCP, so no agent runtime can gate an edit on it — change_impact cannot be surfaced at edit time by any hook #282
Labels
No labels
code-review
correctness
dos
performance
security
severity/high
severity/low
severity/medium
tech-debt
Kind/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
h-dv/code-index#282
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The finding
code-index --helplists every subcommand the binary has:There is no query surface.
search_symbols,find_callers,change_impact,review_diff,index_coverage— all 21 tools — exist only as MCP tools.main.rs's own module doc still says "Future milestones adddaemon,stats,query";querynever arrived.Why it matters, and why it is not a cosmetic gap
It makes a whole class of integration structurally impossible.
An agent runtime that wants to check something before an edit — Claude Code's
PreToolUsehook, a gitpre-commit, a CI step, an editor's on-save action — runs a shell command. None of them speak MCP. So none of them can ask "what is the blast radius of this edit" without spawning an MCP server and hand-rolling JSON-RPC over stdio, which is not something a hook script does.This came out of a concrete attempt. In one session
change_impact,find_references,get_dependencies,find_callees,review_diffandrepo_maphad zero uses until the agent was challenged on it; the nine call sites of a changedpub enumwere found from compiler errors instead, whenchange_impactreturns all nine at depth 1 in one call. The obvious fix is to surface the tool at the moment of the edit. That fix cannot be built — not for Claude Code, and not for anything else.What this blocks, specifically
code-index rules --checkhas a sibling that cannot exist: a pre-commit hook that fails when a public signature moved and nothing looked at its callers.code-index callers fooin a terminal has no route to it.The asymmetry worth naming
crates/cli/src/rules.rsdistributes INSTRUCTIONS to eight agent conventions —AGENTS.md,CLAUDE.md,.cursorrules,.cursor/rules/code-index.mdc,.gemini/rules/code-index.md,.windsurfrules,.github/copilot-instructions.md,.clinerules. Instructions are portable; every runtime reads a file.ENFORCEMENT is not portable, and the reason is this issue. Telling an agent to run
change_impactreaches all eight. Making it impossible to skip reaches none of them, because the check would have to run in the runtime and the runtime has no way in.So the honest current answer to "how do we make the tool-choice gate automatically available to every agent" is: instructions, yes, already, via
code-index rules; enforcement, not until the index has a shell-reachable surface.Proposal
A read-only
code-index query <tool> [args]subcommand over the same daemon the MCP server talks to, emitting the same JSON the MCP tool emits — including the whole disclosure envelope, unchanged, since the value of these replies is that they carry their own reading instructions.Constraints that follow from the rest of the system:
IndexAccessthe MCP server uses. A second query path is a second set of answers, and this project has already paid for that shape (doctoris exposed through the lib for exactly this reason — "there is exactly onedoctorin the workspace and the binary runs the same code the test grades").guard_live_daemonexists becausecode-index watchonce quietly became one.evidence_gapsbecause it is verbose would be a surface where an absent row silently stops meaning "not measured" — see #281 for why the envelope is priced the way it is.code-index rules --checkis the precedent: 0 clean, 1 with findings, and the findings on stderr.Scope note: this is plumbing for a surface that already exists, not a new capability. The hard part is deciding which of the 21 tools get a CLI spelling and what their argument shape is — 21 hand-written arg structs is the wrong answer, and the
#[tool]schemas are already machine-readable.Related: #70 (tool adoption under real discovery friction) — an instruction an agent can skip and a check it cannot are different instruments, and only one of them is buildable today.
Closed by
beabeb8— CI 11/11 including Windows.Arguments come from an argument, or from stdin with
-so a hook can pass a value it just computed without quoting it through a shell.It speaks the protocol rather than calling the code
The three requirements above were met in the strongest form available, and the weaker forms were not available:
crates/mcp-serveris a bin-only crate. Nolib.rs, so its tool handlers are reachable from no other crate — which is the structural fact that produced this issue in the first place.crates/clifixed the same shape fordoctorin #78 step 11 by growing a library face, but that exposed ONE module; here it would mean publishing a 28k-line server as a library and linking all of it intocode-index. Re-deriving the answers in the CLI is what requirement 1 forbids.So the CLI spawns the server and talks to it. The reply is not merely the same CODE an agent gets — it is the same PROCESS through the same door, so it cannot differ. Requirement 3 ("the envelope ships intact") is therefore structural:
annotate_evidence_gapssits above the tool router and runs oncall_tool, so there is no path by which a shell caller could get a differently-annotated reply.Requirement 2 holds because
--no-daemonis deliberately NOT passed: the server attaches to the already-running daemon rather than becoming a second writer.The sibling binary is resolved beside this one before
PATH— the release installs all four binaries into one directory, and a stale copy onPATHwould answer with a different build's schema.Requirement 4: the exit code
Three outcomes, not two:
1 and 2 are separated deliberately. A pre-commit hook that blocked a commit because a binary was missing, the same way it blocks one because the index found a problem, would be worse than no hook: the first is the hook broken, the second is the hook working.
The first implementation got this wrong against this issue's own text — an unknown tool came back as a protocol error and mapped onto exit 1. Fixed with a third outcome (
NotServed).The JSON body prints on stdout even for a refusal, because the reason, the argument at fault and the fix are all in it; sending that to stderr would make the useful half of an error invisible to
jq.Scope note answered
The issue worried that "21 hand-written arg structs is the wrong answer". None were written. Arguments are passed through as a JSON object and validated by the server's own
argcheckagainst the published schema — so a new tool is reachable from the shell the moment it exists, with no CLI change at all.Tests
Four mutations RUN; one survived and is recorded because it is the more useful verdict: removing the local non-object-argument check still exits 2, because the server rejects a non-object as a PROTOCOL error which maps to 2 as well. The exit code could never have graded that check. What it buys is the message, and not spawning a server for a call that cannot work — so the message is what is asserted now.
ensure_server_beside_clibuilds the sibling once when absent and fails loudly if it still is, rather than skipping:cargo test --workspacecompiles every member before any test runs, butcargo test -p code-index-clialone does not.What this unblocks
The pre-edit blast-radius check is now buildable in any runtime — a
PreToolUsehook, apre-commit, a CI step, an editor action. It is not built here; this was its prerequisite. The asymmetry this issue named — instructions portable viarules.rsto eight agent conventions, enforcement portable nowhere — is no longer structural.code-index querycosts ~10 minutes per invocation on Windows, and it has been silently consuming the CI budget #283