desktop/docs/EMBEDDING_MIGRATION.md
This document explains how we handle embedding migrations for existing users when embedding logic changes.
gemini-embedding-001 (3072 dimensions by default)RETRIEVAL_DOCUMENT (for screenshot embeddings)RETRIEVAL_QUERY (for search queries)fullEmbeddingBackfillV2When: Feb 11, 2026 Why: Ensure all users get fresh, correct Gemini embeddings after test phase
What it does:
completed = 0, processedCount = 0Affected users:
Database changes:
UPDATE screenshots SET embedding = NULL WHERE embedding IS NOT NULL;
UPDATE migration_status
SET completed = 0, processedCount = 0, startedAt = datetime('now'), completedAt = NULL
WHERE name = 'screenshot_embedding_backfill';
Migrations are registered in RewindDatabase.swift using GRDB's migrator:
migrator.registerMigration("fullEmbeddingBackfillV2") { db in
// Clear embeddings and reset backfill
}
Each migration runs once per database and is tracked in the migration_status table.
After migration resets the backfill:
OCREmbeddingService.backfillIfNeeded() runsmigration_status table for screenshot_embedding_backfill statuscompleted = 0, starts processing missing embeddingsNew screenshots are embedded immediately in RewindIndexer.swift:
await OCREmbeddingService.shared.embedScreenshot(
id: screenshotId,
ocrText: ocrText,
appName: appName,
windowTitle: windowTitle
)
This ensures newly captured screenshots always have embeddings, independent of backfill status.
Simulate old user with embeddings:
sqlite3 ~/Library/Application\ Support/Omi/omi.db "
UPDATE migration_status
SET completed = 1
WHERE name = 'screenshot_embedding_backfill'
"
Check pre-migration state:
sqlite3 ~/Library/Application\ Support/Omi/omi.db "
SELECT COUNT(*) FROM screenshots WHERE embedding IS NOT NULL
"
Run the app (migration executes automatically)
Verify migration ran:
sqlite3 ~/Library/Application\ Support/Omi/omi.db "
SELECT completed, processedCount FROM migration_status
WHERE name = 'screenshot_embedding_backfill'
"
Should show: 0|0 (reset for backfill)
Watch backfill logs:
tail -f /private/tmp/omi.log | grep OCREmbedding
Use the test script after backfill completes:
python3 scripts/test_semantic_search.py
Expected: Top results should be semantically relevant to each query.
sqlite3 ~/Library/Application\ Support/Omi/omi.db "
SELECT
completed,
processedCount,
startedAt,
completedAt
FROM migration_status
WHERE name = 'screenshot_embedding_backfill'
"
sqlite3 ~/Library/Application\ Support/Omi/omi.db "
SELECT
COUNT(*) as total_screenshots,
SUM(CASE WHEN embedding IS NOT NULL THEN 1 ELSE 0 END) as with_embeddings,
SUM(CASE WHEN embedding IS NULL AND ocrText IS NOT NULL THEN 1 ELSE 0 END) as missing_embeddings
FROM screenshots
"
If we need to change embeddings again (new model, different granularity, etc.):
RewindDatabase.swiftUPDATE screenshots SET embedding = NULLmigration_status for screenshot_embedding_backfillOCREmbeddingService.swift and EmbeddingService.swiftDesktop/Sources/Rewind/Core/RewindDatabase.swift - Migration definitionsDesktop/Sources/Rewind/Services/OCREmbeddingService.swift - Embedding & backfill logicDesktop/Sources/ProactiveAssistants/Services/EmbeddingService.swift - Gemini API callsscripts/test_semantic_search.py - Validation test suitescripts/verify_embeddings.py - Quick embedding check