fix(sf): invalidate stale embedding when memory content is updated

updateMemoryContent rewrote the row but left the existing memory_embeddings
vector in place — that vector was computed against the old content, so the
next cosine query would score the memory by what it used to say, not what
it says now.

Now drop the embedding row on update; the next runEmbeddingBackfill
(agent_end hook) re-embeds. Best-effort: a missing embedding is the
silent-fallback case the ranker already handles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mikael Hugo 2026-05-02 22:38:24 +02:00
parent a3c000de26
commit 7bec2dc2d0

View file

@ -6,6 +6,7 @@
import {
_getAdapter,
decayMemoriesBefore,
deleteMemoryEmbedding,
incrementMemoryHitCount,
insertMemoryRow,
isDbAvailable,
@ -270,6 +271,12 @@ export function createMemory(fields: {
/**
* Update a memory's content and optionally its confidence.
*
* Invalidates the existing embedding row (if any): the stored vector was
* computed against the old content and would otherwise rank incorrectly
* on the next cosine query. The next runEmbeddingBackfill sweep will
* re-embed the new content. This is best-effort a missing embedding
* row is the silent-fallback case the ranker already handles.
*/
export function updateMemoryContent(
id: string,
@ -280,6 +287,12 @@ export function updateMemoryContent(
try {
updateMemoryContentRow(id, content, confidence, new Date().toISOString());
try {
deleteMemoryEmbedding(id);
} catch {
// Stale-vector window is brief (until next backfill); never fail
// the content update because the embedding cleanup raised.
}
return true;
} catch {
return false;