First activation slice of semantic search (docs/discovery/05+06):
- EmbeddingBackfillWorker: fills Email.Embedding in small batches (32, 2s pause,
newest first; subject+snippet, truncated) so interactive load is never starved.
Exits immediately when the embedding provider is unavailable — AI-off deployments
are untouched, Ollama hiccups back off instead of crashing the host.
- compose: optional 'ollama' service under the 'ai' profile (persistent model
volume, commented GPU passthrough for the RTX 3080); api gets
Ai__OllamaBaseUrl pointing at it.
- LiveDb test proves the worker persists 768-dim vectors against real pgvector
(deterministic fake provider); LiveDb classes serialised into one xUnit
collection (concurrent MigrateAsync on a fresh DB races — found while testing).
Verified against REAL Ollama locally: pulled nomic-embed-text in a container and
confirmed the /api/embeddings contract the provider uses returns 768-dim vectors.
Full suite 55 green (4 LiveDb vs real pgvector); format clean.
Next slice: hybrid RRF ranking in SearchService once embeddings exist.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a periodic inbox-summary email per opted-in user. SMTP is
configured via appsettings (Smtp section); the digest is built from
existing analytics (health score, top senders, recommendations) and
sent by a new hourly DigestWorker, mirroring the GmailSyncWorker
pattern. Frequency and send hour are configurable (Digest section).
Backend: IEmailSender (SmtpEmailSender, MailKit), IDigestService,
DigestWorker, new User.DigestEnabled/LastDigestSentUtc fields +
migration, SettingsController for the per-user toggle and a
send-now test endpoint, all scoped to the authenticated UserId.
Frontend: SettingsApi + a bell/no-bell toggle button in the topbar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>