Repository navigation
Conversation
…eration (DeusData#2585) check_index_coverage judged freshness by mtime and size alone, although file_hashes records the SHA-256 of the indexed bytes. A same-size edit with the original mtime passed as metadata_match. Identical bytes rewritten with a new mtime stayed metadata_changed after re-indexing, because the no-op incremental route never rewrites the record. get_code_snippet sliced current disk bytes with indexed line numbers, so it could return another symbol's code under the requested name. No response carried an identity that changes on incremental re-index. - Freshness now compares the file's SHA-256 with the recorded hash. metadata_match additionally means the bytes match. content_changed (new) means same metadata but different bytes; content_match (new) means stale metadata but identical bytes. metadata_changed keeps its meaning. A per-path content_verified flag says whether the hash check ran. A path counts as current only when it did: without a usable hash or a readable file, the freshness label keeps its metadata value, but the path recommends read_source_and_reindex and is not exact-path verified. - get_code_snippet reads the file once, checks that buffer's hash, and slices the indexed range from the same buffer, so a concurrent write cannot produce a content_verified snippet of bytes that were never indexed. When the bytes are not the indexed bytes, it withholds the source and reports source_drift, freshness, and recommended_action "reindex_then_retry". Unlike a metadata gate, this also catches the same-size, preserved-mtime case. Unverifiable files keep the existing fallback, marked content_verified: false. - check_index_coverage metadata and get_code_snippet now carry store_generation ("u<db_uid>g<mutation_gen>", the identity query_graph cursors already use), which advances on every index run. It names the publication current when the response is built; it does not make the response one atomic read. metadata.generation is unchanged: it names the coverage-recording run. Tests (tests/test_mcp.c) reach each state through recorded hash rows instead of file times, so they behave the same on every platform. Without the fix they fail on the defects themselves: "metadata_match" != "content_changed" snippet source contains another function's code store_generation absent unverified metadata match recommended as current a write between check and slice is served as verified (test seam) The DeusData#1613 truncated-catalog test now records the file's real hash, as the indexer does, instead of an empty one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Scott Beck <296871+bline@users.noreply.github.com>
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Addresses part of #2585:
check_index_coverageandget_code_snippetnowjudge a file by its content. When content verification succeeds, snippets
are sliced from the exact buffer checked; detected content drift withholds
source. Unverifiable files retain the existing fallback, marked
content_verified: false. Both responses also carry a publicationidentifier. What this PR
does not cover is listed under Not in this PR.
Freshness by content.
check_index_coveragecompared only mtime andsize, although
file_hashes.sha256already holds the SHA-256 of the indexedbytes. It now compares that hash:
metadata_matchcontent_changed(new)content_match(new)metadata_changedA per-path
content_verifiedflag says whether the hash check ran. A pathcounts as current only when it did. If the recorded hash is unusable or the
file can't be read, the freshness label keeps its metadata value, for
compatibility. But a metadata match alone no longer earns
use_graph_with_best_effort_caveator exact-path verification. Therecommended action is
read_source_and_reindex. The indexer always recordsa hash, so this only affects damaged rows and unreadable files.
Case 1d is fixed on the read side. Re-indexing identical bytes takes the
no-op route, which deliberately does not reopen the sealed generation for
writing, so the stored mtime stays as it was. Freshness now reports such
files as
content_matchinstead of asking for a re-read that cannot help.Snippets.
get_code_snippetreads the file once, hashes those bytes, andslices the indexed line range from the same buffer. A write that lands
between the check and the slice can't produce a
content_verifiedsnippetwith code that was never indexed. A test seam writes the file at exactly that
point to prove it. When the bytes are not the indexed bytes, the source is
withheld, and the response says
source_drift: true, gives thefreshnessvalue, and sets
recommended_action: "reindex_then_retry". This is the #1750symptom. #1788 and #2216 gate snippets on metadata, which case 1c gets past.
The content check covers that case too. Happy to reconcile field names with
either PR.
Publication identifier.
check_index_coveragemetadata andget_code_snippetnow carrystore_generation(u<db_uid>g<mutation_gen>,the identity
query_graphcursors already use), which advances on everyindex run. It is read when the response is built. It names the publication
current at that moment, but it doesn't make the response one atomic read
of that publication.
metadata.generationkeeps its meaning (thecoverage-recording run that
generation_matcheschecks), so it is notrepurposed.
Not in this PR
search_codesource attribution.attach_result_sourcestill sliceslive file lines by indexed coordinates without a content check, so
search_codecan still show the Bug: Stale AST line coordinates in get_code_snippet / search_code after file modification #1750 symptom. I'd rather fix itseparately: grep matches on drifted files also need different
attribution, not just withheld source.
store_generation, and it is an identifier, not a guarantee that everyvalue in a response came from one publication. A per-response read
transaction, and the identifier on the other tools Tool results cannot be tied to one coherent, content-verified index snapshot #2585 names, would be
follow-ups.
Evidence
Reproduce-first tests in
tests/test_mcp.creach each state throughrecorded hash rows rather than file times, so they behave the same on
Windows. Without the source change they fail on the defects themselves:
The two guarantees added in review were checked the same way, by reverting
each one in the source:
tool_check_index_coverage_accepts_truncated_ignored_catalog_for_fresh_path_issue1613recorded an empty hash with matching metadata and expected exact-path
verification. Its fixture now records the file's real hash, as the indexer
does. Its own assertions are unchanged.
With the change,
scripts/test.sh --suites mcpreports 371 passed, 4 skipped(Linux x86-64, gcc, ASan+UBSan).
End to end, on a release build from this branch, using the scripts attached
to #2585 (all cases on throwaway repositories):
Cost.
check_index_coveragenow hashes every path it checks (at most128 per call). Measured on release builds through the CLI, with 128 tracked
files per call, best of 7 runs, all 128 paths verified:
mainBoth builds have a fixed per-call CLI floor of about 980 ms. The hashing
cost appears only at the largest size: about 250 ms for 256 MiB, or about
1 GiB/s.
get_code_snippetnow reads the whole file it serves from, wherebefore it stopped at the symbol's last line.
Fork CI (
dry-run.ymlon bline/codebase-memory-mcp, run 38180923472):lint, memory lint, security gates, and the test matrix pass on Windows
(x86-64 and ARM64), macOS 14 and 15, Ubuntu 22.04 x86-64 and 24.04 (x86-64
and ARM64), plus TSan, MSan, and LSan. The
ubuntu-22.04-armgcc shards failto compile
src/foundation/sha256.c, which this PR does not touch(
inlining failed in call to 'always_inline' 'vsha256hq_u32': target specific option mismatch, gcc 11). That leg runs only in the dry-run matrix, not inPR CI, so it looks like an existing break from #2441.
Checklist
git commit -s) — required, CI rejectsunsigned commits (DCO, see CONTRIBUTING.md)
scripts/test.sh(the canonical leg), 9142 passed, 0 failed, 10 skipped,177 suites, Linux x86-64, gcc
scripts/lint.sh --ciintest-infrastructurelintcontainer(cppcheck + clang-format-20 + memory-core), "All linters passed"
Written with an AI agent (Claude); I reviewed the change and certify it via
the DCO sign-off.
🤖 Generated with Claude Code