The e5 embedder puts every cosine in one narrow band (0.79-0.89), so the
absolute query_min_score gate cannot tell a real hit from a made-up
question: any value under the band answers everything, any value above it
answers nothing. False recall was 5/5.
New gate asks whether one note is clearly the best instead: top1 - top2 >
delta. New query_min_margin config knob, default 0.008, read off the sweep
in the recall harness. The absolute floor stays as a second check.
On the recall fixture with e5: answered 72% -> 68%, false recall 5/5 -> 1/5.
Vikunja #359
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
Task 7 inserted note embeddings into the memory Store but nothing read them
back, and facts weren't indexed at all. Complete the read side:
- Facts are now embedded and inserted into memStore on capture (best-effort,
never fails the fact write) — the notes table can't answer fact questions
("когда я пил воду?"), so memStore is their only recall path.
- Insert meta now carries text/ts/type so a Search hit is self-describing.
- IntentQuery consults memStore.Search after notes-RAG misses and before the
general-knowledge phraser fallback (bestRecall, unit-tested). Strictly
additive: it only runs once the notes path has already given up, so it can't
regress existing recall. Note hits here overlap notes-RAG by design; the
payoff is fact recall and a real read seam for a future persistent backend.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>