ADR-005: Search Cutover — engram-compat → native reverie #

Status: Proposed Date: 2026-04-09 Linear: TOD-634 (epic), TOD-633 (time-decay gap)

Context #

The /search HTTP endpoint and mem_search MCP tool use EngramCompatStore::search(), which was built for byte-parity with Go engram. It provides:

It does not provide:

Now that reveried is the production daemon (engram Go binary retired), byte-parity with Go is no longer a constraint. The native reverie backends (ChunkStore, reverie-chunk) are tested and ready.

Decision #

Cut over in four phases, each independently shippable.

Phase 1: Time-decay in engram-compat (TOD-633) #

Add the same decay formula from sqlite_vec.rs to engram_compat::search():

let decay = 1.0 + RECENCY_BOOST * (-age_days / TAU).exp();
let final_rank = bm25_rank * decay;

Constants: RECENCY_BOOST = 0.3, TAU = 30.0 (monthly half-life).

This is a one-function change with no wire format impact. The rank field already exists in SearchResult. Immediate benefit for all MCP consumers.

Risk: Low. BM25 scores are negative (SQLite convention), so the multiply preserves ordering direction. Tests exist in sqlite_vec to validate the formula.

Phase 2: /search/v2 hybrid endpoint #

New route backed by ChunkStore::hybrid_search():

GET /search/v2?q=...&project=...&limit=...&include_chunks=true

Response shape:

{
  "results": [
    {
      "observation_id": 42,
      "chunk_id": "abc-123",
      "title": "...",
      "content": "...",        // chunk content, not full observation
      "score": 0.87,
      "tags": [{"facet": "Domain", "value": "infra"}]
    }
  ]
}

Old /search preserved unchanged. Clients opt in to v2.

Prerequisite: Production chunker pipeline must be running (observations → chunks on write).

Phase 3: MCP migration #

Update dispatch_tool("mem_search") in mcp.rs to:

  1. Try ChunkStore hybrid search first
  2. Fall back to engram-compat if ChunkStore is empty/unavailable
  3. Format chunk results into the existing MCP response shape

The MCP response format (content[0].text with markdown) stays the same — only the backend changes.

Consequences #

Positive:

Negative:

Neutral:

Alternatives considered #

  1. Port everything into engram-compat — Keeps one backend but makes engram_compat.rs increasingly non-engram. Rejected: defeats the purpose of having native backends.

  2. Hard cutover in one release — Simpler but risky. MCP consumers may depend on exact response shapes. Rejected: phased approach is safer.

  3. Proxy to external search service — Overkill for single-node daemon. Rejected.