Feature Waves
Wave 5 — Recall & Search
One more retrieval feeding the classify → route → inject → answer pipeline, unifying past conversations and vault notes under one BM25 ranking.
Situation
Mallory could hold context and dig deep asynchronously, but had no fast, in-conversation way to search his own memory — past conversations or vault notes. This closes the two limits the persona apologises for: searching the wider note archive and pulling up separate past conversations.
The guiding move is that search rides the pipeline Mallory already runs — recall is one more retrieval feeding the context block, not a new subsystem — and it stays edge-local: the Worker reads D1, the Hub pushes, the Worker never calls the Hub.
Key decisions
Two corpora, one ranked set
Past conversations (already in D1) and vault notes (pushed by the Hub) are queried through one SQLite FTS5/BM25 index with a recency boost, not two separately-ranked lists.
FTS over Vectorize for v1
Chosen because both corpora share one BM25 scale (no cross-engine score reconciliation) and need no new infra; semantic search via Vectorize is the deferred relevance upgrade, kept swap-in compatible.
Hub-pushed index over live Worker→Hub call
A live call would break the edge-local invariant and make the hot path depend on the VM being up; rejected. Trade-off accepted: vault results are up to ~15 min stale.
Capture provenance, don't classify yet
vault_notes.folder/tags and a nullable messages.context column are captured raw now; work/personal classification is deferred to a vault-refactor task that also feeds Wave 6 modes — no backfill needed later.
Results presentation: quiet footer (option A)
Inline citations in the prose plus a compact "Sources" footer, reusing the Wave 2a link vocabulary, over richer source cards (deferred).
Conversation deep-link is new, in-scope work
No message-level deep-link exists today (only ?dig=); a ?msg=<id> route pages history until the message loads, then scrolls to it — with a non-navigating snippet as fallback if pagination proves fiddly.
Architecture
New FTS5 tables for both corpora plus a pure searchCorpus/rankHits core injected into the context block the same way signals already are.
notes (that table already means Mallory's own captured notes).rankHits to merge BM25 + recency across sources; filter is a no-op in the MVP.context.ts uses for signals; persona cites only injected hits.Risks & guardrails
Fabricated citations
Guarded by persona instruction: Mallory may cite only hits actually injected into context; if there are no hits he says so and offers the async dig.
FTS syntax injection
Raw user text would throw on FTS5 syntax if passed directly; a dedicated sanitiser converts it into a safe match expression first.
Vault staleness
Vault results run up to ~15 minutes behind the Hub's push cadence — accepted as fine for MVP with a clear upgrade path.
Large corpus growth
FTS5 index size on D1 needs monitoring; an exclude-list is deferred to the same refactor as context classification.
Delivery
Task 1
D1 migration — FTS tables, triggers, context column.
Task 2
Pure FTS query sanitiser (src/lib/search.ts).
Task 3
Pure ranking + row mapping (src/lib/search.ts).
Task 4
D1 search queries + searchCorpus (src/db.ts, src/searchCorpus.ts).
Task 5
Recall block + sources builders (src/lib/search.ts).
Task 6
Wire recall search into the chat route (src/routes/chat.ts).
Task 7
/vault/sync ingest endpoint.
Task 8
Persona nudge (src/persona.ts).
Task 9
Web — capture sources + render the quiet "Sources" footer.
Task 10
Web — ?msg= conversation deep-link.
Out of scope
docs/superpowers/specs/2026-06-13-wave-5-recall-search-design.mdplan ·
docs/superpowers/plans/2026-06-13-wave-5-recall-search.md