Skip to content

Memory search misses other word forms (Russian and other inflected languages): no stemming or prefix match #5357

Description

@skillet07

Summary

bb memory search only matches the exact word form typed in the query. In Russian (and other inflected languages) a word has many forms, so a search for сценарий ("script") misses a memory that says сценарии ("scripts"), and the two queries return different result sets. With dozens of memories this makes the search unreliable for non-English users. English is affected too, only less often: migrate does not find migrated.

Versions and environment

  • bb 0.46.0, desktop app, macOS 26 (Darwin 25.5.0, arm64)
  • Memory plugin 0.2.0 (builtin:memory)
  • Provider does not matter: the search runs in the plugin's server code

Steps to reproduce

bb memory add --scope global --name ru-forms-a --summary "Сценарии роликов писать адаптацией" --details "Сценарии роликов писать адаптацией." --reason repro --json
bb memory search "сценарий" --scope global --json
bb memory search "сценарии" --scope global --json
bb memory search "сценар*" --scope global --json

Expected vs actual

Expected: all three queries return ru-forms-a.

Actual: only the second query (the exact form) returns it. The first returns nothing, and the third also returns nothing because * is stripped by the tokenizer. On my store of 71 global memories, сценарий returned 4 memories and сценарии returned 2 others.

Cause

searchExpression quotes every query token as an exact FTS5 phrase:

bb/plugins/memory/server.ts

Lines 300 to 308 in 3912f4a

function searchExpression(query: string): string {
const tokens = query.toLowerCase().match(/[\p{L}\p{N}_-]+/gu) ?? [];
if (tokens.length === 0)
throw new CliError("search query has no searchable terms");
return [...new Set(tokens)]
.slice(0, 12)
.map((token) => `"${token.replaceAll('"', '""')}"`)
.join(" OR ");
}

The memories_fts table uses the default unicode61 tokenizer, which does no stemming:

CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(

Proposed fix (prototype works locally)

The fix is on the query side only, with no schema change and no reindex. Each token becomes "token" OR "stem"*:

  • Cyrillic tokens are stemmed with the Snowball Russian algorithm. It is a small dependency-free TypeScript module of about 150 lines, and it matches the reference Snowball output on all 49,785 words of snowball-data/russian/voc.txt.
  • Other tokens keep their exact form plus an FTS5 prefix, so test also finds tests.
  • Stems shorter than 4 characters match exactly, so a word like при does not become a prefix that matches half the store.
  • ё in the query is folded to е.

Result on the store above: сценарий, сценарии and сценариев now return the same 6 memories. The plugin's existing 13 tests still pass, and I added 9 tests (stemmer forms and CLI search across word forms). I am running it as a fork of the forkable built-in copy.

Known limit: text stored with ё is still not found by a query spelled with е, because unicode61 does not fold ё. Fixing that would need a tokenizer or trigger change, so I left it out of scope.

What I ruled out

Suggested priority and effort

Low effort: about 20 changed lines in server.ts plus one module and tests. This affects every non-English user of the memory plugin. There is no workaround short of guessing word forms in the query, and no data is lost.

I would like to open a PR with the patch if this direction is acceptable.

AGENT GENERATED

Activity

  1. added
    pluginsPlugin SDK, runtime, marketplace
    memoryBuilt-in plugin: memory
    on Oct 11, 2026
  2. bb-slop-cop commented on Oct 11, 2026

    @bb-slop-cop
    Contributor

    🚨 SLOP COP 🚨 · new-issue-autopilot

    Applied Type: Feature, Priority: Medium, Effort: Medium, and labels plugins, memory. All classifications were missing; no existing values were replaced. Verified the final Type after writing.

    On trusted origin/main (3912f4a), the Memory README describes FTS5 keyword retrieval, and searchExpression constructs literal token queries without wildcard syntax. Adding stemming or prefix matching extends that existing search behavior rather than repairing an implemented morphology feature. Medium priority reflects the retrieval friction for inflected languages; medium effort reflects language handling, precision tradeoffs, and regression coverage.

    No reproduction report or pull request: the rule skips those steps for non-Bug issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    memoryBuilt-in plugin: memorypluginsPlugin SDK, runtime, marketplace

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions