Sprint 21 closed: keyword-first search, meaning when the string misses

Until this sprint, memory_search was a contiguous SQL LIKE. A question that shared no literal substring with the stored entity — a Russian paraphrase of an English ADR title, a reordered identifier — returned nothing. Assistants then opened files they did not need.

Sprint 21 keeps the keyword path as the fast default and adds a bundled, offline embedding fallback when LIKE is empty (ADR-0020).

Keyword first, meaning when it misses

Exact id / name / summary hits are unchanged. Only an empty keyword result runs cosine over that project's vectors. There is no Hugging Face fetch at runtime: intfloat/multilingual-e5-small (int8 ONNX, 384-d) is vendored next to the process, one model per process, one memory.db per project. Unchanged entities are not re-embedded.

The number this sprint claimed to move, on a fixed bilingual suite with zero substring overlap:

Path Recall
Keyword LIKE 0
Bundled e5 fallback 1.0

Ranking that matches meaning, not heat

On a live ~170-entity graph, e5 compresses almost every row into cosine 0.76–0.82. Adding ADR-0009 vitality (up to +9) on top of that range returned the hottest components — the project node at strength 423 — instead of Sprint 21. Semantic fallback now ranks cosine first, keeps neighbours within 0.02 of the best hit, and uses vitality only as a tie-break.

Routine chore / ci / lockfile commits stay on the keyword path. feat / fix / ADR-touching commits remain eligible, with a small cosine penalty so a sprint or ADR of similar meaning wins the near-tie. ADR entities now carry a Context (else Decision) paragraph, so the embed-string is no longer title-only.

Dogfood query «найти знания когда точное совпадение строки ничего не даёт»: Sprint 21 first, sprint tasks next, important fix(scanner) commits still present, version bumps gone.

Licence and packaging

Meronq is Apache-2.0. The bundled model is MIT (intfloat); transformers.js is Apache-2.0. THIRD_PARTY_NOTICES.md and the model LICENSE travel with the weights. Desktop build:runtime copies packages/embeddings/models/ into installer resources. @huggingface/transformers stays external to sidecar.mjs — a packaged build that cannot resolve the WASM module remains keyword-only; MCP and pnpm dev use the local vendored weights.

End users do not download e5 from Hugging Face. Developers clone once run pnpm --filter @meronq/embeddings vendor-model. If weights are missing, search degrades to keywords without failing the session.

Review notes

Code review on the ranking diff (architecture focus): no medium+ blockers. Markdown stripping for ADR summaries was tightened so inline code (properties_json) survives into the embed-string. Weak-score hybrid ranking and cross-project search stay out of scope. Packaged-sidecar transformers resolution is the explicit follow-up so the installer matches MCP dogfood.

Principle

Keep the exact match when it exists. When it does not, look up meaning locally — and do not let heat or a version bump outrank the decision.

← Back to blog