fix(incremental): re-insert cached embeddings on incremental path

Bugbot re-review caught: deleteNodesForFile cascades to the
CodeEmbedding table (DELETE WHERE e.nodeId STARTS WITH ...), so
changed-file embedding rows are wiped along with their nodes. The
previous fix gated re-insert on `!isIncremental`, which silently
dropped those embeddings — a regression versus the full-rebuild path's
"preserve embeddings by default" guarantee.

Remove the `!isIncremental` gate. The per-batch try/catch already
handles the unchanged-file PK-conflict case ("some may fail if node
was removed, that's fine") with the same semantics, so re-inserting
the full cached set on incremental works:

  - changed-file rows: deleted, then re-inserted from cache (preserved)
  - unchanged-file rows: still in DB, re-insert PK-conflicts and is
    silently ignored (existing rows are correct)

Cost: re-inserting ~24K embeddings on incremental when only a few
files changed — most are no-op conflicts. Bounded by batch size of
200; ~3-5s overhead. Worth it for correctness.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
abhigyanpatwari 2026-05-10 18:28:40 +05:30
parent 5eb05977e1
commit 60c10f1827

View file

@ -477,12 +477,20 @@ export async function runFullAnalysis(
progress('fts', 90, 'Search indexes ready');
// ── Phase 3.5: Re-insert cached embeddings ────────────────────────
// Skipped on the incremental path because that path keeps the
// existing DB rows in place (re-inserting cached vectors over
// surviving rows would PK-conflict). On the full-rebuild path,
// the DB was wiped, so re-inserting the cache is the mechanism
// that preserves embeddings across the rebuild.
if (cachedEmbeddings.length > 0 && !isIncremental) {
// Runs on BOTH the full-rebuild path and the incremental path:
// - Full rebuild: DB was wiped, every cached row needs to come back.
// - Incremental: changed-file rows were just deleted by
// deleteNodesForFile (which cascades to their
// embedding rows) — so their cached vectors need
// to come back too. Unchanged-file rows still
// exist; re-inserting their cached vectors would
// PK-conflict, but the per-batch try/catch below
// silently ignores those (matches the existing
// "some may fail if node was removed, that's
// fine" semantics). Bugbot review on PR #1479
// flagged that gating this on `!isIncremental`
// silently lost changed-file embeddings.
if (cachedEmbeddings.length > 0) {
const cachedDims = cachedEmbeddings[0].embedding.length;
const { EMBEDDING_DIMS } = await import('./lbug/schema.js');
if (cachedDims !== EMBEDDING_DIMS) {