BUG: qmd search and qmd get serve deleted files (and stale content) silently
BUG: qmd search and qmd get serve deleted files (and stale content) silently
qmd 2.8.3 (facd35e) · Node v22.14.0 · Debian 13 (trixie) · linux-arm64 · CPU-only
Summary
After a file is deleted, qmd search still returns it with a score, and
qmd get still prints its full body — both exit 0, with no warning and no
staleness hint, until a manual qmd update runs. The same silent-staleness
applies to edited files: new content is unfindable, old content keeps
ranking, and every failure mode exits 0, so callers cannot tell "not in the
notes" from "index is out of date."
Repro — deleted-file ghost (verbatim, exit codes shown)
$ echo "Ghost verification token: ghosttoken777yevgen" > notes/ghost2.md
$ qmd update # indexes it (1 new)
$ qmd search ghosttoken777yevgen
qmd://audit/notes/ghost2.md:1 #1242e1
Score: 65%
...
$ rm notes/ghost2.md
$ qmd search ghosttoken777yevgen # NO update run
qmd://audit/notes/ghost2.md:1 #1242e1 <-- ghost, exit 0
Score: 65%
$ qmd get qmd://audit/notes/ghost2.md
qmd://audit/notes/ghost2.md #1242e1 <-- full cached body, exit 0
---
1: Ghost verification token: ghosttoken777yevgen
$ qmd update # NOW it reports "1 removed"
$ qmd search ghosttoken777yevgen
No results found.Repro — stale content after edit (verbatim)
$ printf 'Fresh verification token: yevgenverify9931\n' >> notes/audit-note.md
$ qmd search yevgenverify9931
No results found. # exit 0, no stderr, no staleness hint
$ qmd status # says "Updated: 3m ago" — no dirty-file line
$ qmd ls audit # shows OLD size/mtime (index stats, not disk)
$ qmd update # "1 updated"
$ qmd search yevgenverify9931
qmd://audit/notes/audit-note.md:10 #987c39
Score: 56%Root cause (from reading dist/, 2.8.3)
search()indist/cli/qmd.jsnever callscheckIndexHealth— onlyvectorSearch()does. And even that only warns when the index is 14+ days old, not when a file changed 10 seconds ago.- The keyword path serves the sqlite copy unconditionally; nothing stats the underlying file before answering.
qmd getlikewise serves the cached body with no existence check.
Suggested fix
Before answering, stat each candidate file (or check mtime/hash) and:
- missing file → warn on stderr and skip, or require an explicit
--include-deletedflag; - file newer than its index row → warn "index stale, run qmd update" (or auto-refresh that collection);
- distinct exit code (or stderr marker) when a "no results" answer came from a possibly-stale index, so scripts can tell the two apart.
Why it matters
QMD is otherwise fast and correct in keyword mode (unique-token searches answer in ~0.1s, ranking is right). But "answers about files that no longer exist, without saying so" is the failure mode an agent-built index must never have: a bot asking "where did I write X?" gets a confident, wrong answer with no signal anything is amiss.
Happy to provide more detail — full audit notes with all findings (staleness, BM25 score collapse, cross-collection duplicates, exit-code contract) are available on request.
Source: tobi/qmd