no_outline and path_not_indexed frame a PERMANENT exclusion as transient, and send the caller to a tool that cannot answer either #243
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#243
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?
Flagged by the #238 lane as a follow-up candidate; verified on
master1d3228ebefore filing. Small, but it is the one shape this tracker keeps closing, in the two errors an agent meets first.The fact
Two hints, one per tool, both worded the same way:
For a path the walker permanently refuses — a dot-directory, a gitignored file, a nested checkout, a file over the size cap — every clause of that is wrong in the same direction:
search_texton its name confirms whether it's indexed" — it does not.search_textreturnstotal: 0for an excluded path exactly as it does for a not-yet-indexed one. The remedy hands the caller the same ambiguity they came with, one tool over.So the error is corrective in shape and misleading in content: it produces a next step that cannot terminate.
The tool that does answer it
index_coverage(path)exists, is cheap, and returns a strict three-state verdict with the rule that fired:nevervspendingis precisely the distinction these two hints collapse — andindex_coverageis already the thing theevidence_gapssemantics string points readers at. The vocabulary exists; these two errors predate it and were never rejoined to it.Why it is worth a number
This is where an agent lands first.
file_outlineon a path the index cannot see is the opening move of half the sessions in this repo, and #201 is on record for what happens next: a lane readinternal_error, concluded the tool was broken, and fell back togrepfor the rest of the session — in a repo whose CLAUDE.md makes preferring the index a hard rule. A hint that sends the reader to a tool that cannot answer is the same dead end with better manners.What must NOT be done
index_coveragebefore erroring. That pays a probe on every miss, including the common typo. Name it in the hint; let the caller spend the round trip only when they need the answer.pending, and it is a real state. The defect is that transient is offered as the explanation rather than one of two.Shape of a fix
Both hints name
index_coverage(path)as the way to tell a permanent exclusion from a lag, and stop asserting the transient reading as the likely one. One shared string, since the two sites already say the same thing.What a fix must prove
index_coverage, and following it returnsverdict: "never"with the rule — end to end, not just the string.Related
#238 (where this was spotted; its part 1 fixed the same class for
empty_population), #201 (the corrective-error work these two hints predate), #220 (a blind spot that reports as a measurement is worse than one that reports as a limit).Filed 2026-09-09 against
master1d3228e.Fixed on
masterate97972c(merged98d3e3f, inorigin/master). 15 mutations, 15 RED, 0 survivors.Both hints now derive from one shared clause,
PATH_NOT_INDEXED_NEXT_STEP(crates/mcp-server/src/server.rs:5360), which namesindex_coverage(path)and keeps the transient reading as one of two rather than the only story.I re-ran the deciding mutation myself — putting the
search_textregression back inside the shared const:The test walks both call sites in one body, and a separate test (
the_two_hints_are_one_clause) asserts they carry the same string — which correctly stays GREEN under that mutation, because both moved together. Content graded by one test, sharing by another. Mutating one site's wording alone (M3) reds the second: "one clause, two call sites — a change to how this project explains an unindexed path must be a change to ONE string."One correction to this issue. "Two hints, same wording" is not quite true on
master— they were two different strings saying the same wrong thing (no_outlinealso carried "…is indexed at all", andpath_not_indexedhas a directory sibling). The fix still lands as one clause; worth noting because "one wording" would have let a reader assume a sharedconstalready existed.Cost, measured: the
no_outlinereply grows 770 → 1,036 B andpath_not_indexed299 → 572 B — error paths only. A successful call pays nothing, and the startup payload is byte-for-byte identical (66,048 chars before and after).Anti-vacuity arms both run:
a_plain_typo_still_gets_the_path_shape_advice_first(M4 reds if the diagnosis is put before the correction — "paying for the diagnosis before the correction is how a corrective error becomes a lecture"), anda_new_unwalked_file_still_reads_as_transient_and_resolves_to_pending(M5 reds if the transient sentence is deleted).Verification:
fmt0 ·clippy -D warnings0 · rustdoc-D warnings0 ·cargo test --workspace331 result lines · both the snapshot andCOSI_E2E_LEG=daemonlegs 0 ·corpus_ratchetwithbaseline.jsonunmoved ·precision_gate7/7 phantoms=0.Closing — and noting it sat fixed-and-open for a couple of hours, which is the defect I closed seven other issues for today.
evidence_gapswarns on every reply that the answer may be short, and gives no way to find out which file —index_coverage(path)needs the path you are trying to learn #241read_codesays an empty body "PROVES the range does not exist" — but it proves it about the SERVER's tree, which is not the caller's when a worktree is in play #258