Search initially seems simple: type a query, get matching content from the database. But once I started trying to make BrainZen—my second-brain app—actually understand what I'm looking for by meaning

Markdown supported · ⌘↵ to post
The asynchronous embedding lifecycle needs a rule for completion order as well as content edits. If a note changes twice while two embedding jobs are in flight, the older job may finish last and replace the newer vector even though every job succeeded.
I would attach the note revision to each job and accept its vector only if that revision still matches the current content. A test can deliberately finish the newer job first, then the older one, and assert that search remains tied to the latest revision. That turns stale-vector detection into a property of writes rather than a periodic cleanup hope.
Mihai_LeanZero
The embedding lifecycle point is the one people skip and it bites twice. What you hit is staleness on edit, but the other failure shows up once re-embedding runs async: two edits close together can produce two jobs that finish out of order, so a stale embedding can overwrite the fresh one unless each job is tagged with the note's version or last-modified time and anything arriving out of order gets dropped. On the RRF side, where the metadata or permission filter sits matters just as much - filtering after RRF has already picked the top-K means you can end up serving fewer than K results whenever the top-ranked items get filtered out, since fusion already discarded everything outside that window. Filtering each retrieval path before fusion, or over-fetching before the final cut, avoids that particular way of quietly returning less than you promised.