#961·qmd

BUG: qmd search and qmd get serve deleted files (and stale content) silently

Author: yev9enCreated Sep 17, 2026Updated Sep 17, 2026

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)

bash
$ 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)

bash
$ 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() in dist/cli/qmd.js never calls checkIndexHealth — only vectorSearch() 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 get likewise 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-deleted flag;
  • 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.