search_text whole_word=true treats boolean/prefix FTS expressions as literal text and silently drops matches #224
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#224
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?
With
whole_word=true, supported FTS query expressions silently become literal strings in the post-filter. A boolean query returns zero despite matching source, and the response describes that total as exact.Reproduction
Against this repository at
a32a7519be, callsearch_text:Actual:
total: 0, one candidate examined,saturated: false, and semantics saying the total is exact.Independent control:
This finds 20 matching lines in that file.
A minimal indexed fixture with
// alphaina.rsand// betainb.rsalso returns zero for(alpha OR beta)and foralph*whenwhole_word=true. Both queries have matching words.Cause
whole_word_needleunquotes a single phrase but otherwise returns the raw query. Both search routes pass that string tocontains_as_word, so parentheses,OR, and*are searched as source characters. FTS candidate selection and the post-filter answer different questions.Suggested fix
Prefer evaluating whole-word matching over the parsed query semantics. Reuse the daemon's actual query interpretation so the two layers cannot disagree about whether input is syntax or an auto-quoted literal; avoid a second approximate FTS grammar.
If full expression support is deferred, explicitly reject unsupported combinations with a structured error and actionable hint. Returning an exact-looking zero is not an acceptable fallback. Continue supporting plain literals and single quoted phrases, including escaped quotes.
Acceptance tests
TODOversusToDouble.Validation
Confirmed on a freshly built 0.27.0 (
a32a7519be), using MCP over stdio in disposable projects. Also dogfooded the connected 0.26.1 server against this repository. The reviewed Rust implementation is unchanged between those commits. Existing checks pass: 241 MCP binary unit tests and 8 daemon RPC integration tests; these cases are not covered by those passing tests.Priority assessment: P2 / medium correctness. Found during code-index-vs-ripgrep self-review on 2026-09-08. Full review and reproduction output will be attached.
Shared evidence for #224, #225 and #226:
Extract the ZIP, build the reviewed checkout with
cargo build --offline -p code-index-mcp -p code-index-cli, then runpython3 /path/to/repro.pyfrom that checkout. The script uses disposable fixture projects and an isolated plugin store; the generation change is fault injection only in a disposable database.Fixed in
492c0d3— "search_text(whole_word=true)grades the query the daemon actually ran" — onmaster, shipping in v0.27.0.Boolean and prefix FTS expressions were matched as literal source characters, and the resulting
total: 0was then described as exact. One compiledWordMatchernow serves admission, census and line numbers, and an expression the post-filter cannot honour is refused with a reason instead of answered with a confident zero.Closing on merge.