Re-embed stored notes and facts after an embedder swap (#378)

The embedder swap left every stored vector in the old model's space, so cosine against a new query vector is noise. Add the one-shot backfill: store.ReembedAll re-embeds every note and fact text with the currently configured embedder (the passage side, which is the side stored text was written with) and rewrites both places a vector lives — the notes table embedding column and the memory_vectors rows.

All of it plus the embedder marker happens in one transaction, so a failure partway changes nothing and writes no marker: re-run it. A run against a DB whose marker already names the current embedder does nothing.

Triggered explicitly with `mavend -reembed`, not automatically on mismatch: ONNX on the laptop CPU makes this minutes of work, and a silent multi-minute stall on boot would look like a hang. The mismatch warning now tells the user to run it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
This commit is contained in:
kami
2026-07-31 13:50:43 +04:00
parent 4282f6b9a9
commit 92ecb691de
5 changed files with 376 additions and 6 deletions
+3 -3
View File
@@ -60,9 +60,9 @@ const EmbedderUnknown = "unknown (written before this marker existed)"
// marker was added to catch.
// - marker absent and no vectors ⇒ fresh DB, claim it, nothing to fix.
//
// TODO(#378): on a mismatch, run the one-shot backfill here — re-embed every
// stored note and fact text with the current embedder (EmbedPassage side),
// write the vectors back, then SetMeta the current id.
// On a mismatch the fix is ReembedAll (backfill.go), run explicitly with
// `mavend -reembed`. Nothing is re-embedded here: that work is minutes of CPU
// on the laptop and must not stall a normal start.
func (s *Store) CheckEmbedder(ctx context.Context, currentID string) (stored string, mismatch bool, err error) {
stored, err = s.Meta(ctx, metaKeyEmbedderID)
if err != nil {