fix(search): the documented way to enable local search built a worse index than none - #2187
Conversation
…index than none (#2185) `data/sag.db` is gitignored, so every fresh clone builds it — from `data/okf/lessons.jsonl`, a **tracked** file with **no writer in CI**. Measured 2026-09-25: data/okf/lessons.jsonl last written 2026-07-07 (194 rows) a fresh export_okf.py 411 rows the committed export names 177 of 453 lesson files (61% missing) and `misakanet/server/handlers/search.py` prefers SAG over the complete BM25 path (`if HAS_SAG and not explain:`), while the hint it returns when no index exists says only: "Run: python3 scripts/build_sag_index.py to enable BM25/SAG search" `build_sag_index.py` only complains about a missing export (`Run export_okf.py first` is in the *file-not-found* branch), and the tracked file exists — so following the documented remedy produced an index covering 39% of the corpus, preferred over the one that covers all of it. Demonstrated with a probe query taken from a lesson the stale export cannot contain: stale (194 lessons) -> 0 hits fresh (411 lessons) -> 1 hit Two changes, both halves of the trap: * **the hint names both steps, in order** (`export_okf.py && build_sag_index.py`), because the second command reads what the first one writes; * **`build_sag_index.py` measures the export against the corpus** and prints a warning naming the cause and the command when it covers less than `COVERAGE_FLOOR` of it. Threshold measured, not chosen: a fresh export covers 91%, the committed one covered 39%. It warns rather than refuses — an old checkout or a filtered export is legitimate, and a build script that will not run is a worse trap than the one it closes. Evidence (2026-09-25): `python3 -m pytest tests/` → 2241 passed, 15 skipped. Against the committed export the builder now prints the warning; against a fresh one it stays silent. `tests/test_sag_export_freshness.py` (12 tests) pins the measurement, the boundary, the silent case outside a checkout, and both wirings; 4 mutations go red: the call removed from `build_index` · the threshold dropped below the stale population · the denominator counting READMEs · the hint losing the export step. The first of those four was green on the first attempt and is the reason the file now has `test_building_from_a_stale_export_warns_at_the_point_of_use`: every other test called the warning function directly, so deleting its call from `build_index` left the suite passing. The function was tested; the wiring was not — the same shape of hole as the `--kv-only` early return in #1822 and the `exit` output in #2183. What this does NOT fix, and #2185 stays open for it: nothing regenerates the tracked export in CI, so it will drift again. That wants a writer in the daily job (the same treatment `data/lessons.json` gets) or a decision to stop tracking it — an owner call about where the export step belongs, not a code change I should make on my own. Signed-off-by: misakanet-bot <bot@misakanet.dev>
PR Genius Analysis
Checklist
Anti-Patterns Detected
Suggestions
|
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
misakanet-web | 15da730 | Commit Preview URL Branch Preview URL |
Sep 25 2026, 03:26 AM |
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
🎉 Merged — Thank you!Your contribution has been merged into main. PR: #2187 — fix(search): the documented way to enable local search built a worse index than none What's next:
Welcome to the MisakaNet contributor community! 🧠 |
|
✅ Merged! Thanks again, @Ikalus1988. fix(search): the documented way to enable local search built a worse index than none (+226 lines, 3 files) Quick question — did any MisakaNet lesson help you this time? No need to reply if nothing comes to mind. ⚡ |
PR Code Suggestions ✨No code suggestions found for the PR. |
🧾 Audit Report — PR #2187 (15da730)📊 Quality Score🔏 DCO Audit✅ All commits signed-off. 📏 PR Size
🔐 Secret Scan✅ No hardcoded secrets detected. 📦 Dependency Audit⏭️ Skipped; no Python/JS dependency files changed. 🧪 Test Suite✅ PASS — 56% coverage 📋 Lesson Schema✅ All lessons valid. ⚖️ Verdict✅ All gates passed. Ready for merge. Scope: |
User description
Fixes the half of #2185 that can be fixed without deciding where the export step belongs.
The trap, measured:
data/sag.dbis gitignored, so a fresh clone builds it from the trackeddata/okf/lessons.jsonl— last written 2026-07-07, naming 177 of 453 lesson files. The searchhandler prefers SAG over the complete BM25 path, and its hint said only
Run: python3 scripts/build_sag_index.py. So following the documentation produced an index covering 39%of the corpus, preferred over the one covering all of it:
Two changes: the hint now names
export_okf.py && build_sag_index.pyin that order (the second readswhat the first writes), and
build_sag_index.pymeasures the export against the corpus and warns when itcovers less than
COVERAGE_FLOOR— 0.8, sitting in the measured gap between a fresh export (91%) and thecommitted one (39%). It warns rather than refuses: an old checkout is legitimate, and a build script that
will not run is a worse trap than the one it closes.
Evidence:
python3 -m pytest tests/ -q→ 2241 passed, 15 skipped. Against the committed exportthe builder prints the warning; against a fresh export it is silent.
tests/test_sag_export_freshness.py(12 tests) pins the measurement, the boundary, the silent caseoutside a checkout, and both wirings. 4 mutations red: the call removed from
build_index· thresholdbelow the stale population · denominator counting READMEs · the hint losing the export step.
One of those four was green at first — every test called the warning function directly, so deleting its
call from
build_indexpassed — which is why there is now a test that goes throughbuild_indexitself.Same hole as the
--kv-onlyearly return (#1822) and the unreadexitoutput (#2183): the function wastested, the wiring was not.
Still open in #2185: nothing regenerates that tracked file in CI, so it will drift again. That needs a
decision (give it a writer in the daily job like
data/lessons.json, or stop tracking it) rather than achange I make unilaterally.
PR Type
Bug fix, Tests
Description
Hint names
export_okf.py && build_sag_index.pyin orderbuild_sag_index.pywarns when export covers <COVERAGE_FLOOR12 tests pin measurement, boundary, wiring, and hint order
Diagram Walkthrough
File Walkthrough
search.py
hint names both export and build steps in ordermisakanet/server/handlers/search.py
actionfield of the "Search engine unavailable" responseexport_okf.pyandbuild_sag_index.pyin dependency orderbuild_sag_index.py
warn when OKF export coverage is below 0.8 floorscripts/build_sag_index.py
LESSONS_DIRandCOVERAGE_FLOOR = 0.8constantscorpus_lesson_files()excluding READMEsexport_coverage()returning (covered, total)warn_if_export_is_stale()printing cause + remediationbuild_index()after record loadtest_sag_export_freshness.py
pin freshness measurement, boundary, and hint wiringtests/test_sag_export_freshness.py
fixture
COVERAGE_FLOORbuild_indexandhandle_search_no_index_hintbehavioral driver