From b7d3c8d49b4d30d466e5d5175319ce640c8902c3 Mon Sep 17 00:00:00 2001 From: "vitalii.semianchuk" Date: Fri, 3 Jul 2026 14:48:03 +0100 Subject: [PATCH] fix: evicted observations leave ghost entries in search indexes when evict deleted observations from KV, it never removed them from the BM25 or vector indexes. this meant search would return IDs for evicted entries, but the second-pass KV lookup would get null, silently dropping results and under-filling search pages. added index removal calls to match how forget and auto-forget already handle it. --- src/functions/evict.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/functions/evict.ts b/src/functions/evict.ts index 626f4ff1d..dc4258c4e 100644 --- a/src/functions/evict.ts +++ b/src/functions/evict.ts @@ -10,6 +10,7 @@ import { KV } from "../state/schema.js"; import { StateKV } from "../state/kv.js"; import { recordAudit } from "./audit.js"; import { deleteAccessLog } from "./access-tracker.js"; +import { getSearchIndex, vectorIndexRemove, flushIndexSave } from "./search.js"; import { logger } from "../logger.js"; interface EvictionConfig { @@ -212,6 +213,8 @@ export function registerEvictFunction(sdk: ISdk, kv: StateKV): void { }); continue; } + getSearchIndex().remove(o.id); + vectorIndexRemove(o.id); if (o.imageData) await decrementImageRef(kv, sdk, o.imageData); if (o.imageRef && o.imageRef !== o.imageData) await decrementImageRef(kv, sdk, o.imageRef); await recordAudit(kv, "delete", "mem::evict", [o.id], { @@ -255,6 +258,8 @@ export function registerEvictFunction(sdk: ISdk, kv: StateKV): void { }); continue; } + getSearchIndex().remove(o.id); + vectorIndexRemove(o.id); if (o.imageData) await decrementImageRef(kv, sdk, o.imageData); if (o.imageRef && o.imageRef !== o.imageData) await decrementImageRef(kv, sdk, o.imageRef); await recordAudit(kv, "delete", "mem::evict", [o.id], { @@ -291,6 +296,8 @@ export function registerEvictFunction(sdk: ISdk, kv: StateKV): void { }); continue; } + getSearchIndex().remove(mem.id); + vectorIndexRemove(mem.id); if (mem.imageRef) { await decrementImageRef(kv, sdk, mem.imageRef); } @@ -326,6 +333,8 @@ export function registerEvictFunction(sdk: ISdk, kv: StateKV): void { }); continue; } + getSearchIndex().remove(mem.id); + vectorIndexRemove(mem.id); if (mem.imageRef) { await decrementImageRef(kv, sdk, mem.imageRef); } @@ -340,6 +349,10 @@ export function registerEvictFunction(sdk: ISdk, kv: StateKV): void { } } + if (!dryRun) { + await flushIndexSave(); + } + logger.info("Eviction complete", { stats }); return stats; },