This document explains how we handle embedding migrations for existing users when embedding logic changes.
- Embedding Model: Gemini
gemini-embedding-001(3072 dimensions by default) - Task Types:
- Documents:
RETRIEVAL_DOCUMENT(for screenshot embeddings) - Queries:
RETRIEVAL_QUERY(for search queries)
- Documents:
- Granularity: One embedding per screenshot (concatenated OCR text with app context)
When: Feb 11, 2026 Why: Ensure all users get fresh, correct Gemini embeddings after test phase
What it does:
- Clears ALL existing screenshot embeddings
- Resets backfill status to
completed = 0, processedCount = 0 - Triggers full backfill on next app launch
Affected users:
- Users who installed during test phase (had 1000-item test limit)
- Users with incomplete backfills due to API errors
- Users with embeddings created with wrong parameters
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:
- On next app launch,
OCREmbeddingService.backfillIfNeeded()runs - Checks
migration_statustable forscreenshot_embedding_backfillstatus - If
completed = 0, starts processing missing embeddings - Processes in batches of 100 screenshots
- Updates progress every 1000 items
- Marks complete when all screenshots embedded
New 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.pyExpected: 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.):
- Add a new migration in
RewindDatabase.swift - Clear old embeddings:
UPDATE screenshots SET embedding = NULL - Reset backfill: Update
migration_statusforscreenshot_embedding_backfill - Update model/logic in
OCREmbeddingService.swiftandEmbeddingService.swift - Test locally with the steps above
- Document the change in this file
- Backfill rate: ~100 screenshots every 200ms = ~500 screenshots/minute
- For 143,884 screenshots: ~288 minutes (~4.8 hours)
- Rate limiting: Required by Gemini API to avoid quota errors
- Memory usage: Disk-based search, no in-memory index needed
- User impact: Backfill runs in background, doesn't block app usage
Desktop/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