Basic Memory
What's New

Upgrade to v0.23

Breaking changes, migrations, and required actions when upgrading Basic Memory from v0.22.1 to v0.23.

v0.23 applies database migrations automatically, but integrations and operator scripts need review. Complete the actions below before relying on existing automation.

Install v0.23.2 or later rather than stopping on v0.23.0. The two patch releases fix PostgreSQL search coverage and reindex safety, bm doctor and bm update reliability, and case-duplicate folder creation.

Rebuild vectors after changing indexes

If you change semantic_vector_index between pgvector and milvus, rebuild the configured vector store:

bm reindex --embeddings

The default incremental run is enough. Before it finishes, vector search returns no results and hybrid search quietly serves full-text results only. See Run Postgres with Milvus for the full switch and recovery procedure.

Milvus requires the basic-memory[milvus] extra. Standalone Redis read caching requires basic-memory[redis]. Reranking remains off by default and downloads its local model on the first reranked search.

Update CLI automation

  • bm status now reports project index status instead of the old sync-report tree. Scripts that parse its human output must switch to --json and the new shape.
  • bm reindex --embeddings exits nonzero when embedding fails. Treat that exit as a real failed rebuild instead of assuming success.
  • Interactive bm tool commands render Rich output on a TTY. Piped output remains machine-readable; pass --json when a script requires a stable contract.
  • The new top-level groups are bm config and bm hook. The top-level command set otherwise remains intact; bm sync was already absent in v0.22.1.

Update MCP clients

  • The canvas, cloud_info, and release_notes MCP tools are removed. Obsidian Canvas generation and the API resource-write endpoints behind it are no longer available.
  • The ChatGPT compatibility tools search and fetch now accept OpenAI MCP clients only. Other clients must use search_notes and read_note.
  • list_directory is bounded and paginated. Consumers must use page and page_size instead of assuming one exhaustive response. It also supports title/update sorting, and file_name_glob now filters returned files without pruning traversal through nonmatching folders.
  • replace_section is heading-level-aware. By default it replaces the selected section through the next heading of the same or higher level, including nested subsections. Set replace_subsections=false to preserve nested subsections.
  • Ambiguous identifiers now raise an error instead of returning a best guess, and write_note rejects filename-convention twins instead of creating a duplicate.
  • FastMCP moves to 4.0.0b1 with MCP SDK v2. Downstream embedders pinned to FastMCP 3.x must upgrade with Basic Memory.

Update HTTP clients

Resource write endpoints and pre-v0.18.0 legacy routes are removed. Old clients that still call those routes receive 404 responses. The current v2 API remains the supported surface.

Update configuration

Basic Memory automatically migrates these config-file keys and legacy environment variables:

OldNew
sync_delayindex_delay
sync_changesindex_changes

When old and new names are both set, the new name wins.

sync_thread_pool_size and sync_max_concurrent_files are removed without aliases and are silently ignored in config.json. Remove them. materialization_workers is the nearest replacement for bounding local write materialization, but it is not a one-for-one thread-pool setting.

Review behavior changes

bm cloud sync is a one-way mirror and now deletes a previously synced cloud file when that file becomes ignored by .bmignore. Preview the exact deletion set with bm cloud sync --name <project> --dry-run, or use the additive bm cloud push and bm cloud pull workflow.
  • Deleting the default project now chooses a new default instead of refusing the deletion.
  • created and modified frontmatter timestamps are authoritative. Historical values can change recency ordering after the first reindex.
  • Single transcript timestamps, spaced timestamp ranges, and Markdown checkbox markers are no longer indexed as observation categories.
  • Note-type filters are case-canonicalized, so values such as Person and person now match the same population.
  • Root-level wikilinks can resolve through a note's .md filename even when its title differs. A unique case-insensitive underscore/hyphen filename alias is tried only after exact identities; ambiguous aliases remain unresolved.

Review late-release fixes

Several v0.23 fixes require no migration or configuration change but affect operator expectations:

  • Incremental bm reindex --search reconciles stale full-text rows, including orphaned rows attached to unchanged files.
  • Re-selecting the current default project is idempotent and no longer leaves the workspace without a default.
  • Directory scans recheck a note's lineage and current path before applying a deletion, protecting notes accepted while a scan is running.
  • Standalone semantic note reads make fewer API requests, and the normal read-cache lifetime increases from 60 seconds to 5 minutes.
  • The Hermes integration preserves command and skill registration ownership. Follow the version-specific Hermes patch guidance; modern Hermes versions must not replace the lifecycle-aware collector with the legacy workaround.

Let migrations finish

Six revisions run automatically when upgrading from v0.22.1 to the latest v0.23 patch:

  1. A durable note-file vacate record distinguishes moves from byte-identical copies.
  2. The semantic manifest gains vector-index identity and readiness state.
  3. Relation-derived search refreshes become durable, retryable work items.
  4. Relations and their search refreshes gain generation ownership.
  5. A one-time repair removes duplicate observations and orphaned observation rows from full-text search.
  6. On PostgreSQL, a child full-text index stores bounded, overlapping chunks of complete note bodies and backfills existing notes. SQLite needs no equivalent schema change because it already indexes full content.

The observation repair is not reversible through a downgrade. Markdown files remain the source of truth and the index is rebuildable, but back up memory.db before the upgrade if you want a pre-migration database snapshot. Let the PostgreSQL migration and index build finish before testing deep full-text matches.

Configure custom FastEmbed model prompts

Basic Memory does not infer model-specific prompts for custom FastEmbed models. Configure the selected model's dimensions and any required document/query prefixes explicitly. For example, multilingual E5 uses 1024-dimensional vectors, passage: for indexed content, and query: for search queries:

bm config set semantic_embedding_provider fastembed
bm config set semantic_embedding_model intfloat/multilingual-e5-large
bm config set semantic_embedding_dimensions 1024
bm config set semantic_embedding_query_prefix "query: "
bm config set semantic_embedding_document_prefix "passage: "
bm reindex --embeddings

The trailing spaces are meaningful and preserved. Rebuild embeddings after changing the model, dimensions, or either prefix so stored document vectors and new query vectors use the same contract. The symmetric default model (bge-small-en-v1.5) needs no prefixes.

Review the v0.23.1 and v0.23.2 fixes

The patch releases do not add breaking API changes, but they correct several behaviors worth knowing about:

  • PostgreSQL searches complete note bodies. v0.23.1 backfills the new full-text chunks automatically. Boolean queries and punctuated terms work across the complete text, not only titles and capped content stems.
  • Reindexing is project-scoped. Rebuilding search for one project no longer drops search rows belonging to sibling projects in the same database. The migration also repairs a missing shared search table if the old destructive path removed it.
  • Use #bm:links_to to disambiguate prose. End a list item with the directive when a single word before a wikilink is prose rather than a relation type: - Mother [[Alice]] #bm:links_to. The directive remains readable in Markdown but is removed from indexed content.
  • bm doctor and bm update are safer. Doctor waits for deferred materialization and can create its disposable project under BASIC_MEMORY_PROJECT_ROOT; update status output survives an in-place package replacement.
  • Folder casing resolves consistently. In v0.23.2, write_note(directory="schemas") and note moves land in an existing unique Schemas/ folder instead of creating a case-duplicate sibling. Resolution happens segment by segment for nested folders and behaves the same locally and in Cloud.

Known issues in v0.23

macOS file-identity edge cases

Two open issues can produce duplicate entities for a single file on macOS:

  • Unicode normalization (NFC/NFD): APFS preserves whichever byte form a filename was written with, and some cross-platform sync tools (notably Syncthing) rewrite names to the other form. File-path lookups are byte-wise, so the same file can be indexed twice, accumulating -1-suffixed permalinks and breaking edit_note on the affected notes (#1275).
  • Case-only renames: on a case-insensitive APFS volume, renaming config.md to Config.md via move_note is refused as "destination already exists"; case-only folder renames can silently not change the folder's case on disk and leave duplicate index entries behind (#1281).

Workarounds: avoid case-only and normalization-only renames from MCP tools (rename through an intermediate name instead), and run bm reindex --full -p <project> to collapse duplicates if they appear.

The v0.23.2 folder-casing fix applies to the destination folders used by normal note writes and moves. It does not rename existing folders, merge existing case-variant siblings, or fix case-only and Unicode-normalization-only renames.

Continue setup