Skip to content

feat(hsearch)!: literal queries, surface DB failures, always back up before migration - #80

Merged
sysid merged 4 commits into
mainfrom
fix/hsearch-backup-error-swallowing
Oct 4, 2026
Merged

sysid merged 4 commits into
mainfrom
fix/hsearch-backup-error-swallowing

Conversation

@sysid

@sysid sysid commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Summary

hsearch and the pre-migration backup turned database failures into "nothing
found" / "nothing to back up". This PR makes those failures visible, always backs
up a real database before migrating, and makes hsearch treat its query as plain
text so technical terms stop breaking it.

Supersedes the valid parts of #79 with a minimal implementation.

Changes

Commit Problem Fix
fix(hsearch): propagate vector store failures has_embeddings().unwrap_or(false) — a locked or corrupt DB silently degraded hybrid search to FTS-only Propagate the error. vec_bookmarks always exists after startup, so an error is a real fault
fix(db): always back up databases before migrating Backup was skipped when the bookmark COUNT(*) returned 0 or failed — a broken DB was migrated without a backup Remove the row-count check; every DB ≥ 16 KB is backed up
fix(hsearch): print [] for --json when nothing matches Zero hits printed a colored message to stderr and nothing to stdout — invalid JSON for consumers Emit [], consistent with search --json
feat(hsearch)!: match query terms literally Raw query went to FTS5: c++, node.js, foo-bar, title:x, AND aborted the whole search with syntax errors Quote each term for the FTS branch; semantic branch still embeds the original text

⚠️ Breaking change

hsearch no longer interprets FTS5 syntax — AND/OR/NOT, "phrases",
column filters and prefix* are matched as literal text. Use search for those.

Rationale: in hybrid mode the same text is also embedded, so operators work against
the semantic branch (auth NOT oauth pulls semantic results towards oauth).

Before / after:

$ bkmr hsearch c++
main:  Error: ... fts5: syntax error near "+"
PR:    results

$ bkmr hsearch --json --mode exact nomatch
main:  (stdout empty)
PR:    []

Requires a minor version bump.

Testing

All new tests were written first and verified failing on main:

  • Hybrid search with a failing vector repository returns an error (was Ok([]))
  • Migration of a ≥ 16 KB DB whose bookmark count fails creates a backup (was none); DB < 16 KB still gets none
  • hsearch --json with zero hits prints [] (CLI test)
  • fts_query() quoting incl. embedded "; service-level search for c++, node.js, foo-bar, AND, tips: finds the bookmark

make test green.

Docs

  • hsearch --help: query is matched literally
  • skill/bkmr-memory/SKILL.md: no quoting needed for hsearch
  • Wiki Search-and-Discovery.md updated separately (to be published with the release)

Follow-up

Error-prefix duplication and blank-query handling: #81

sysid added 4 commits October 4, 2026 14:52
hybrid_search treated an error from has_embeddings() as "no
embeddings" and silently fell back to FTS-only results. The
vec_bookmarks table is always created at startup, so an error here is
a real database fault (lock, corruption) and must reach the user.

The info command keeps its fallback on purpose: it is display-only
and should not hide the remaining statistics.
The pre-migration backup was skipped when the bookmark count query
reported zero rows, and also when that query failed. A locked or
corrupted database was therefore treated as empty, and the migration
ran without a backup.

Drop the row count check and keep only the 16 KB size gate. Backing up
a large database that has no bookmarks costs nothing; skipping the
backup of a real one can lose data.
On zero hits hsearch --json wrote a colored message to stderr and
nothing to stdout, so JSON consumers received invalid input. Fall
through to the JSON branch instead, matching search --json.
hsearch passed the raw query to FTS5, so ordinary technical terms such
as c++, node.js, foo-bar or title:x failed with syntax errors. The
whole command aborted even though the semantic branch could have
answered.

Quote every whitespace-separated term for the FTS branch. The semantic
branch still embeds the original text. FTS5 power syntax stays
available in search, where it belongs: in hybrid mode the same text
is also embedded, so operators such as "NOT oauth" would pull
semantic results towards oauth.

BREAKING CHANGE: hsearch no longer interprets FTS5 syntax (AND/OR/NOT,
phrases, column filters, prefix*). Use search for these queries.
@sysid sysid changed the title Fix/hsearch backup error swallowing feat(hsearch)!: literal queries, surface DB failures, always back up before migration Oct 4, 2026
@sysid
sysid merged commit d173819 into main Oct 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant