Indexer ingests .stversions backups, sync-conflict copies and .obsidian junk as notes

#1 · closed · 1 comments

View on GitHub ↗

tonydzi

## What happens `index_notes.py` collects notes with `sorted(notes_dir.rglob('*.md'))` (line 51). `rglob` descends into dot-directories, so on a real Obsidian vault — the exact audience of this repo — the index fills with files that are not notes, and **stale copies of a note compete with the live one at retrieval time**. ## Repro ```python from pathlib import Path import tempfile d = Path(tempfile.mkdtemp()) (d / "real.md").write_text("real note") (d / ".stversions").mkdir(); (d / ".stversions" / "real~20260701.md").write_text("OLD VERSION") (d / "real.sync-conflict-20260801-ABC.md").write_text("CONFLICT COPY") (d / ".obsidian").mkdir(); (d / ".obsidian" / "cache.md").write_text("plugin junk") (d / ".git").mkdir(); (d / ".git" / "COMMIT_EDITMSG.md").write_text("git junk") print(sorted(p.relative_to(d).as_posix() for p in d.rglob("*.md"))) ``` ``` ['.git/COMMIT_EDITMSG.md', '.obsidian/cache.md', '.stversions/real~20260701.md', 'real.md', 'real.sync-conflict-20260801-ABC.md'] ``` 5 files indexed, 1 of them an actual note. Anyone syncing a vault with Syncthing has both `.stversions/` and `*.sync-conflict-*` files; anyone using Obsidian has `.obsidian/`. Why it bites: an old version of a note is *semantically almost identical* to the live one, so it lands next to it in the top-K and quietly eats a retrieval slot with outdated content. The answer looks right and is stale. ## Second, smaller bug in the same line Line 53 reads with `errors='ignore'`, which also swallows the UTF-8 BOM: ```python Path("bom.md").write_bytes(b"\xef\xbb\xbf---\ntitle: x\n---\nbody") Path("bom.md").read_text(encoding="utf-8", errors="ignore")[:12] # '---\ntitle: ' ``` The leading `` means the first line is not `---`, so any frontmatter parser downstream sees the note as having no frontmatter. `encoding="utf-8-sig"` handles this correctly. ## Shape of the fix - Skip dot-directories while walking, and skip `*.sync-conflict-*` by name. - Read with `encoding="utf-8-sig"`. - Make the exclusion list overridable (env var, consistent with the repo's env-only configuration) — someone will legitimately want to index a hidden folder. ## Definition of done - The repro above indexes exactly `real.md`. - A test with a synthetic tree covering: dot-dir, sync-conflict name, BOM file, normal note. - `python index_notes.py <dir>` still prints the same summary line for a clean directory. Comment "claiming this" to take it — yours for 7 days.

Comments

tonydzi

Fixed in 698a6c7. Both bugs in those two lines are gone: - **File selection.** Dot-directories and `*sync-conflict*` names are skipped, so `.stversions/` revisions, `.obsidian/` cache and `.git/` internals no longer enter the index. The repro from the description now yields `['real.md']` and nothing else. `BRAIN_INDEX_HIDDEN=1` opts hidden folders back in — someone will legitimately keep notes in one. - **BOM.** Reading moved to `utf-8-sig`, so a BOM'd note starts with `---` again and its frontmatter (date, title) is parsed instead of silently lost. `errors='replace'` rather than `ignore`, so a damaged file degrades visibly instead of quietly dropping characters. The selection and reading now live in `iter_notes()` / `read_note()` — a seam that needs no model and no network, which is also what #2 asked for to make tests possible at all. **Tests: 7 cases, and they were shown red before the fix.** With the seam extracted and the old behaviour deliberately kept: `5 failed, 2 passed`. With the fix: `7 passed in 5.80s`. A test that has never been red proves nothing, so the red run came first on purpose. Worth saying plainly: this was reported by us and fixed by us, which is not the same as a contributor finding it. The queue is still open — #2 (a smoke test that runs without downloading a model) is the natural next one and is a genuine design conversation, not a chore.