Skip to content

fix(mcp): fold parent snapshot id into rescan purge cursor hash - #248

Merged
radimsem merged 1 commit into
devfrom
fix/rescan-purge-cursor-hash
Jun 6, 2026
Merged

fix(mcp): fold parent snapshot id into rescan purge cursor hash#248
radimsem merged 1 commit into
devfrom
fix/rescan-purge-cursor-hash

Conversation

@radimsem

@radimsem radimsem commented Jun 6, 2026

Copy link
Copy Markdown
Owner

Closes #247.

Problem

The background rescan purge path (pkg/mcp/rescan/rescan.go reconcileDeleted) emitted its deleted-file snapshot with a content-only diff.CursorHashFlat(synthetic). snapshots.cursor_hash is CHAR(16) NOT NULL UNIQUE, so a sequence that purges the same file set twice with an identical recreate in between (delete → purge → recreate+compile → delete same files → purge) reproduces the earlier purge hash and collides. The failure is swallowed (r.logger.Error + return false), so the purge snapshot is silently dropped rather than surfaced.

This is the last emitter.Emit* call site that was still on a parent-unaware digest — follow-up to #238 (write/summarize) and #244/#246 (compile/forget).

Fix

Route the purge through parent-folded hashing, the model #238 adopted:

  • Read prevHeadID via GetHeadSnapshotID — already inside the OpMu critical section, so the head-id read and the emitter.Emit (which advances HEAD) stay atomic.
  • Swap diff.CursorHashFlat(synthetic)diff.CursorHashForChange(prevHeadID, deltas).
  • Drop the now-unused synthetic []*parser.ContextNode scaffold (it existed only to feed CursorHashFlat) and the orphaned parser import.

Tests

  • pkg/mcp/rescan: TestRescanLoop_RepeatedDeleteOfSameFileSet — delete the same file set twice with an identical recreate in between; asserts two distinct purge snapshots. Confirmed to fail pre-fix (purge snapshots = 1, want 2 — second purge swallowed by the UNIQUE collision) and pass after.
  • Full suite green: build, go vet, 1186 tests, gofmt, golangci-lint (0 issues).

Audit (closes the #247 follow-up note)

All five emitter.Emit* snapshot sites now fold the parent id: compilerCursorHashForCompile, rollbackCursorHashForRollback, write/summarize+forget+rescanCursorHashForChange. CursorHashFlat/CursorHash remain only for non-snapshot, diff-internal uses. No further emit path on a bare content/delta hash.

Reviewers

go-style and Codex reviews both clean.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@radimsem
radimsem merged commit d3c5a18 into dev Jun 6, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Rescan purge can still hit UNIQUE cursor_hash on repeated delete of the same file set

1 participant