Two calls seconds apart served DIFFERENT index generations while answer_provenance was byte-identical: nothing says which generation answered #245
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#245
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?
Found live by the #137/#160/#238 lane while working, not by a test. This is the #181/#182 axis one level down — those asked which binary and which tree answered; this is which generation, and nothing answers it.
Measured
Two
search_symbols/search_textcalls seconds apart, same session, same server:The first reply's line numbers were 418 lines stale for
crates/mcp-server/src/server.rs.And in both replies,
answer_provenancewas byte-identical:Same binary. Same tree. Same HEAD. Same
uncommitted_changes. Different generation, different symbol ids, different line numbers, and the reply says nothing.Why this matters more than a stale read
lineis the field callers feed straight intoread_code. A 418-line stale span sends the reader to the wrong place — and this project has closed that exact family twice already (I052 forread_code, #202 forsearch_text'sline/snippetdivergence). Both of those were about a lying span within one reply. This is a lying span across replies, with the provenance block asserting they came from the same state.answer_provenance's whole job is to say what produced the answer, and it currently answers two of the three questions:buildindexed_trees[].headA generation is the unit the index actually reads rows from. Two generations of one tree are as different as two trees, and the machinery to say so exists —
plugin_activation.active_generationis already inproject_overview; it just is not in the provenance block that every reply carries.Why this is not "the watcher is behind", which is a known and disclosed state
index_stale/pending/ the reconcile disclosures all describe the index being behind the disk. This is different: both calls were served, both returned rows, and the rows came from different generations. The caller has no way to know a comparison between two of its own replies is invalid.What must NOT be done
project_overview~3;code_index_test_support::headroomnow grades both and a re-record must attribute. This has to be small — an integer in a block that already ships.uncommitted_changes. That describes the tree, not the index.Shape of a fix
answer_provenance.indexed_trees[<project>]gains the generation the rows came from — the same integerplugin_activation.active_generationalready reports. Absent means the daemon did not report it (three-state, as everything else there); a value means this reply's rows came from that generation.Then two replies are comparable iff their generations match, and a caller can tell.
Worth investigating alongside, but separable: why two calls seconds apart crossed a promotion at all, and whether the first reply's rows were from a generation already superseded when the call arrived. That is a correctness question; the disclosure is a honesty question, and the honesty half is cheap and independently right.
What a fix must prove
0; and a reply from a project with no generations still renders honestly rather than defaulting.answer_provenancemoves — #181 and #182's fields are load-bearing and separately graded.Related
#181 (which binary answered), #182 (which tree answered), #202 and I052 (the lying-span family this extends across replies), #241 (the same "the reply cannot situate its own evidence" shape).
Found 2026-09-09/10 against
master8a8ea5c, live oncode-index-mcp 0.27.1 (1d3228e).answer_provenancenames a commit the INDEX has not reached, so a watcher-lag miss and a measured absence are indistinguishable #260code-index-plugin-hostcannot say which build it is — three of four shipped binaries answer--version, the fourth exits 21 with usage #262Released in v0.28.1, build
340a75a, via #264 and #265. Release CI passed, including the Windows archive round-trip smoke test. The response now identifies the SQLite content actually read using its database incarnation and committed revision; it does not substitute the working-tree HEAD or an activation number for content identity. The public Linux archive was installed with the published installer, then exercised through direct and daemon MCP: stable replies agreed, a live edit changed the served snapshot, and the edited symbol became searchable. Missing-identity and inconsistent compound-read cases are covered by the regression suite. Closing the shipped disclosure fix.buildagent referenced this issue2026-09-11 20:25:15 +02:00