Skip to content

feat(core): remove semantic_search_enabled; semantic search is always on - #1696

Merged
phernandez merged 11 commits into
mainfrom
remove-semantic-flag
Oct 9, 2026
Merged

phernandez merged 11 commits into
mainfrom
remove-semantic-flag

Conversation

@phernandez

Copy link
Copy Markdown
Member

Why

Follow-up to #1691. Semantic search already defaulted on whenever fastembed and sqlite-vec were importable, and both are core dependencies, so semantic_search_enabled only mattered for installs that turned it off by hand. Keeping it meant a second code path at every write, search and indexing site. Removing it removes those branches.

What changed

  • semantic_search_enabled is removed from BasicMemoryConfig, along with its dependency-sniffing default and the reranker's "requires semantic" validation.
  • Every check of the flag is gone:
    • repository construction (both backends always build the embedding provider and vector index)
    • create_search_reader (always composes the semantic stack)
    • vector-sync scheduling on note writes and index-file
    • MCP server startup logging and embedding-status logging
    • bm reindex --embeddings (no longer refuses)
    • default retrieval mode in search_notes (hybrid) and grep (FTS by default, hybrid when semantic=True)
    • the local watcher and project indexer (always pass embeddings)
    • project readiness and get_embedding_status
  • Six knowledge routes drop the app_config dependency they only had for the flag. EmbeddingStatus drops its now always-true semantic_search_enabled field.
  • Docs, README, justfile, benchmark scripts and integration READMEs no longer mention the flag.

Implementation details

  • The [BUG] Fatal crash on startup when python.org Python 3.12 is present alongside Homebrew on macOS #711 fallback stays. When sqlite-vec cannot load (for example python.org Python on macOS), init_search_index() turns semantic retrieval off for that repository and search continues keyword-only, so Claude Desktop's handshake still succeeds. The error for a vector/hybrid query in that state now reads "semantic search is unavailable" and points at the startup log, and the MCP help text explains the fix instead of telling users to set a flag.
  • Old configs keep loading. BasicMemoryConfig uses extra="ignore", so config.json files and BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED are ignored rather than rejected. New test: test_legacy_semantic_search_enabled_setting_is_ignored.
  • Tests. The suites used the flag to skip the ONNX embedding stack. They now use a deterministic test embedder (tests/fake_embeddings.py, wired as an autouse fixture in both conftests): it keeps the real FastEmbedEmbeddingProvider class, and therefore the configured embedding identity, and replaces only embed_query/embed_documents with a unit-length hash vector. Tests marked semantic or the new real_embedder marker keep the real model.
    • Tests that only covered flag-off behavior are removed (about 20).
    • Inspection and status tests that relied on semantic being off now set up real vector storage.
    • The subprocess CLI tests in test_project_add_indexing.py run a real install, so they embed with the real model; they share the FastEmbed cache instead of downloading into each pristine HOME.

Testing

  • pytest tests test-int -n 8 -m "not semantic and not benchmark and not live" (SQLite): 8,593 passed, 66 skipped, 4m40s. The unit suite alone went from about 3m to 3.6m with the test embedder.
  • tests/cli/test_project_add_indexing.py with the real model: 2 passed in 35s.
  • just fast-check: clean.
  • Postgres: left to CI.

Risks / Follow-ups

  • Users who had set semantic_search_enabled: false (to avoid the model download or CPU use) now get semantic search; there is no off switch.
  • Cloud reads config.semantic_search_enabled in apps/cloud/src/basic_memory_cloud/main.py:555 and pgq/jobs/deps.py:591 (feeding entrypoints.py:1723). Those reads must be removed in the Cloud pin bump that picks this up, along with the cloud tests that pass semantic_search_enabled=False.

🤖 Generated with Claude Code

https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF

Semantic search already defaulted on whenever fastembed and sqlite-vec were
importable, and both are core dependencies, so the flag only mattered for
installs that turned it off by hand. Every check of it is gone: repository
construction, the search reader factory, embedding scheduling on writes and
index-file, MCP startup, bm reindex, the search and grep tool defaults, the
watcher, readiness, and embedding status. Six knowledge routes lose the
app_config dependency they only had for the flag, and EmbeddingStatus loses its
always-true field.

The #711 runtime fallback stays: when sqlite-vec cannot load, that repository
drops to keyword-only search, and the error now points at the startup log
instead of a config flag. Old configs and BASIC_MEMORY_SEMANTIC_SEARCH_ENABLED
still load because the config model ignores unknown keys.

Tests used the flag to skip the ONNX model. They now use a deterministic test
embedder (tests/fake_embeddings.py) that keeps the real FastEmbed provider class
and identity and swaps only its embed methods; tests marked semantic or
real_embedder keep the real model. Tests that only covered flag-off behavior are
removed; the rest set up real vector storage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF
Signed-off-by: phernandez <paul@basicmachines.co>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-09T03:37:08.391662Z f4d95f7 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

…ses it

test-int runs on its own in CI, where the tests package is not importable, so
'from tests.fake_embeddings' failed while loading its conftest. pytest's pythonpath
already puts tests/ on the import path; import the module by that name and teach
ty the same path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF
Signed-off-by: phernandez <paul@basicmachines.co>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7506dce642

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +46 to +47
await driver_connection.enable_load_extension(True)
await driver_connection.load_extension(sqlite_vec.loadable_path())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip extension setup when SQLite cannot load extensions

On Python builds without sqlite3.Connection.enable_load_extension—the exact macOS fallback environment this change preserves—this helper raises AttributeError before the status assertions run. Running the focused suite on such a build produced five failures in test_project_service_embedding_status.py; probe the capability and skip or use a non-vec test setup rather than unconditionally loading the extension.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 400265b: the helper only loads sqlite-vec when the driver exposes enable_load_extension. On builds that can't load extensions, search falls back to keywords (#711) and never creates the vec0 table, so a plain DROP TABLE IF EXISTS is enough.

Comment thread tests/cli/test_project_add_indexing.py Outdated
Comment on lines +64 to +67
# Semantic search is always on, so indexing embeds with the real model.
# The pristine HOME would otherwise put the model cache inside the temp
# profile and download it again for every test; share the real one.
FASTEMBED_CACHE_PATH=SHARED_FASTEMBED_CACHE,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep FastEmbed artifacts inside the test profile

When these subprocess tests run, SHARED_FASTEMBED_CACHE resolves from the developer or CI account's real profile before HOME is replaced, so the supposedly pristine test can download into—and FastEmbed's recovery path can delete corrupt subtrees from—the user's actual ~/.basic-memory/fastembed_cache. Use a session-scoped temporary cache shared by these tests instead, preserving download reuse without mutating state outside the isolated test directory.

AGENTS.md reference: AGENTS.md:L300-L303

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 400265b: the subprocess tests now share a module-scoped tmp_path_factory cache (one model download per module) and never touch the real profile's fastembed_cache.

…ess builds

- The subprocess CLI tests share a module-scoped temporary FastEmbed cache instead
  of the runner's real ~/.basic-memory/fastembed_cache, so they cannot download into
  or prune a developer's actual profile.
- The embedding-status helper loads sqlite-vec only when the driver can load
  extensions; on builds that cannot, search never created the vec0 table.

Addresses Codex review on #1696.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 400265bb7f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if app_config is None or not app_config.semantic_search_enabled:
return None
"""Return the semantic vector sync backend for local indexing."""
return dependencies.search_service

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve keyword-only project indexing when vectors are unavailable

On SQLite runtimes where sqlite-vec cannot load, startup deliberately falls back to keyword-only operation, but this factory creates a fresh repository and now always supplies its search service as the vector-sync backend. run_project_index_coordinator subsequently awaits vector synchronization, whose _ensure_vector_tables() raises SemanticDependenciesMissingError, causing bm project add and bm project index to fail after the full-text rows were written instead of completing in keyword-only mode. Only provide the vector sync backend when the runtime capability probe succeeds.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e8f56b7. SearchService.sync_entity_vectors[_batch] now return the entities as skipped when the repository runs keyword-only (#711), so project indexing, the watcher and write follow-ups finish without vectors. The same commit makes readiness owe no embeddings on such hosts (otherwise bm status --wait ran to its timeout). Test: test_vector_sync_skips_entities_when_the_vector_runtime_is_unavailable (fails without the fix).

Comment thread src/basic_memory/mcp/tools/search.py Outdated
return config.default_search_type

return "hybrid" if config.semantic_search_enabled else "text"
return "hybrid"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Default to text search after the vector runtime fallback

When SQLite extension loading is unsupported, initialization logs that search is falling back to keywords, but omitted search_type values are now always converted to hybrid. Per-request repositories are rebuilt with semantics enabled and their vector-table probe raises, so ordinary search_notes("...") calls return a semantic-unavailable error rather than keyword results; users must know to explicitly request search_type="text". The default mode needs to reflect the effective runtime capability, not merely the always-on configuration.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e8f56b7: the default search type is hybrid only when semantic_runtime_available() (a cached sqlite-vec load probe, always true on Postgres); otherwise text. Test: test_default_search_type_is_text_when_the_vector_runtime_cannot_load.

…s always on

With the flag gone, a host that cannot load sqlite-vec (#711) had no way to opt
out, and three paths still assumed vectors:

- project indexing and write follow-ups awaited vector sync, which raised once
  the repository had fallen back, failing `bm project add`/`index` after the
  full-text rows were written;
- readiness counted every note as owed embeddings, parking the project in
  PENDING and running `bm status --wait` to its timeout;
- `search_notes` defaulted omitted search types to hybrid, turning every plain
  search into a semantic-unavailable error.

One cached probe, `semantic_runtime_available` (sqlite-vec loadable, or
Postgres), now answers "can this host run vector search". Vector sync skips and
reports entities as skipped when the repository runs keyword-only, readiness
owes no embeddings there, and the search tool defaults to text there.

Addresses Codex review on #1696.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

Semantic search is always on, so app and MCP tests now reach pgvector (write_note's
similar-notes probe, vector sync). Production creates that storage at database
initialization; the per-test schema reset drops it, so the Postgres engine fixture
now recreates it for the configured provider's dimensions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF
Signed-off-by: phernandez <paul@basicmachines.co>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 75d5bbbc3f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/basic_memory/mcp/tools/search.py Outdated
Comment on lines +221 to +223
# A host that cannot load sqlite-vec runs keyword-only (#711); defaulting to
# hybrid there would turn every plain search into a semantic-unavailable error.
return "hybrid" if semantic_runtime_available(config) else "text"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve hybrid defaults for cloud-routed projects

When the MCP process runs on an extension-less SQLite host but the selected project is routed to Cloud (or through a custom factory), get_project_client() sends the request to the remote Postgres service while this check still probes the local SQLite runtime and returns text. Ordinary searches against a fully semantic-capable cloud project therefore silently lose hybrid retrieval; determine availability from the selected route, or default cloud/factory routes to hybrid.

AGENTS.md reference: AGENTS.md:L344-L350

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8d5c934: the default is hybrid when the local runtime can run vector search or when every selected project routes to Cloud (get_project_mode == CLOUD; Cloud runs Postgres). Test: test_default_search_type_is_hybrid_for_cloud_projects_on_a_keyword_only_host.

Comment on lines 1042 to +1045
@dataclass
class StubConfig:
semantic_search_enabled: bool = True
default_search_type: str | None = None
database_backend: DatabaseBackend = DatabaseBackend.SQLITE

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stub vector availability in semantic-enabled tests

On a supported Python build where sqlite3.Connection.enable_load_extension is absent, this SQLite stub invokes the real capability probe, so the test receives FTS instead of its asserted hybrid mode. Fresh evidence after the earlier extension-less helper fix: on Python 3.14.4, the focused command containing this test and test_an_unembedded_markdown_note_is_pending_embedding_work fails both cases, and the broader changed suites produce the same host-dependent failures; patch the probe to return true in tests that model an available semantic runtime while retaining the explicit fallback test.

AGENTS.md reference: AGENTS.md:L47-L47

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8d5c934: the shared autouse fixture (tests/fake_embeddings.py) now also patches semantic_runtime.sqlite_vector_runtime_available to True, so tests that model an available runtime are host-independent. The explicit fallback tests still inject False at the call sites.

…y hosts

- search_notes now picks its default from where the search runs: hybrid when the
  local runtime can run vector search or every selected project routes to Cloud
  (Postgres always can), text otherwise. Probing only the local SQLite runtime
  downgraded cloud projects on hosts without sqlite-vec.
- The shared test fixture now models a vector-capable host (the sqlite-vec probe
  answers yes), so ordinary tests no longer depend on whether this Python can load
  extensions. Fallback tests still inject the opposite.
- test-int's Postgres fixture recreates pgvector storage per test, as the unit
  fixture now does.

Addresses Codex review on #1696.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8d5c934781

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


# A host that cannot load sqlite-vec runs keyword-only (#711): there is
# nothing to embed into, so indexing and writes must finish without vectors.
if not await self.repository.semantic_effectively_enabled():

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard the entire vector reindex on keyword-only hosts

On a supported Python build that cannot load SQLite extensions, this check is reached too late to protect bm reindex --embeddings: _reindex() constructs a fresh repository with _semantic_enabled=True, and reindex_vectors() calls _purge_stale_search_rows() before this method and reconcile_vector_index() afterward; both invoke _ensure_vector_tables() and raise instead of honoring the keyword-only fallback. Check effective semantic availability around the whole reindex workflow so this maintenance command does not fail on the fallback environment.

AGENTS.md reference: AGENTS.md:L47-L47

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b4f8f41: reindex_vectors() checks semantic_effectively_enabled() before purging or reconciling, and returns every entity as skipped on a keyword-only host. Test: test_reindex_vectors_is_a_noop_when_the_vector_runtime_is_unavailable.

Comment thread src/basic_memory/mcp/tools/search.py Outdated
Comment on lines +227 to +230
routed_to_cloud = bool(project_names) and all(
config.get_project_mode(name) == ProjectMode.CLOUD for name in project_names
)
return "hybrid" if routed_to_cloud or semantic_runtime_available(config) else "text"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor explicit cloud and factory routing in search defaults

Fresh evidence after the prior cloud-routing fix: this still recognizes only per-project CLOUD mode, although get_client() routes factory injection and explicit --cloud ahead of project mode. On an extension-less local host, bm tool search-notes ... --cloud for a locally configured project—or a custom cloud client factory—therefore sends search_type="text" to the remote Postgres service and silently loses the intended hybrid default. Base this decision on the effective route, including factory and force-cloud routing.

AGENTS.md reference: AGENTS.md:L347-L350

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b4f8f41: new routes_off_host() in async_client follows get_client's precedence (injected factory, then --local/--cloud, then project mode); the search default uses it. Test: test_routes_off_host_follows_get_client_precedence.

Comment thread tests/fake_embeddings.py
"real_embedder"
):
return
monkeypatch.setattr(semantic_runtime, "sqlite_vector_runtime_available", lambda: True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make fake vector storage independent of extension support

Fresh evidence after the earlier extension-host test fix: patching this preflight helper does not affect SQLiteSearchRepository._ensure_sqlite_vec_loaded(), which still disables semantics when the actual driver lacks enable_load_extension. On Python 3.14.4, the focused changed suites consequently fail seven tests, including both test_reindex_vectors cases and five embedding-status cases, because vector sync is skipped or the vec0 table was never created. The fixture must emulate the repository/vector-storage path too, or those tests must be capability-gated.

AGENTS.md reference: AGENTS.md:L47-L47

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declining this one. These tests exercise sqlite-vec storage itself (vec0 tables, reindex, embedding status), so a Python build that cannot load SQLite extensions cannot run them meaningfully, and emulating vec0 in a fixture would test the fake rather than the storage. Supported installs and CI load sqlite-vec; the keyword-only fallback on extension-less builds is covered by its own tests (vector sync skip, reindex no-op, readiness, search default).

…p vector reindex on keyword-only hosts

- routes_off_host() answers "does this request leave this process" with
  get_client's precedence (injected factory, then --local/--cloud, then each
  project's mode). search_notes defaults to hybrid when the request runs remotely
  or the local runtime can run vector search.
- reindex_vectors() returns every entity as skipped on a keyword-only host (#711)
  instead of failing while opening vector tables it cannot load.
- Postgres tests that drop search_vector_chunks use CASCADE, since the pgvector
  table now exists in every test and references it.

Addresses Codex review on #1696.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

…n path

test-int's schema reset drops search_vector_chunks as well as the pgvector table,
so creating only the pgvector table failed on its foreign key. Both conftests now
run PostgresSearchRepository.init_search_index(), the same step database
initialization takes, which creates the chunk manifest and the pgvector table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF
Signed-off-by: phernandez <paul@basicmachines.co>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 496145d235

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

es = info.embedding_status
if not es.semantic_search_enabled:
right.add_row("[green]●[/green] Semantic Search", "Disabled")
right.add_row("[green]●[/green] Semantic Search", "Enabled")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report an unavailable vector runtime in project info

On a SQLite host that cannot load extensions—the supported keyword-only fallback—get_embedding_status() reports missing vector storage and recommends remediation, but this unconditional row still tells the user that semantic search is “Enabled.” Consequently, bm project info presents contradictory status and may direct the user to bm reindex --embeddings, which intentionally skips all work on that host. Expose the effective runtime availability in the status response and display semantic search as unavailable when the fallback is active.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8f40501: EmbeddingStatus gains vector_runtime_available. On a keyword-only host get_embedding_status() returns it False with no reindex recommendation, and bm project info shows 'Unavailable: sqlite-vec cannot load here, keyword-only'. Test: test_embedding_status_reports_keyword_only_host.

… info

On a SQLite host that cannot load sqlite-vec (#711), get_embedding_status() now
returns vector_runtime_available=False with no reindex recommendation, and bm
project info shows semantic search as unavailable instead of Enabled next to
advice to run a reindex that would skip every note.

Addresses Codex review on #1696.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8f40501078

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +79 to +81
# Semantic search is always on. init_search_index() turns it off for this
# instance only when sqlite-vec cannot load (#711), falling back to keywords.
self._semantic_enabled = True

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Convert sqlite-vec load failures into the keyword fallback

When sqlite_vec imports and the driver exposes enable_load_extension, but loading the binary fails—for example because the wheel is incompatible or extension loading is denied—this unconditional enablement sends startup through _ensure_vector_tables(). _ensure_sqlite_vec_loaded() lets the resulting sqlite3.OperationalError escape, while init_search_index() catches only SemanticDependenciesMissingError, so MCP/API startup aborts instead of using the documented keyword-only fallback, and the removed config flag no longer provides an escape hatch. Fresh evidence beyond the earlier AttributeError fix is that the new semantic_runtime probe explicitly classifies OperationalError as unavailable, but the repository path does not; translate the load failure into the typed dependency error as well.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a76c6ed: load_extension's sqlite3.OperationalError is converted to SemanticDependenciesMissingError, so init_search_index() takes the keyword-only fallback. Test: test_sqlite_vec_load_failure_falls_back_to_keyword_search (fails without the conversion).

Comment on lines +86 to +88
# False on a SQLite host that cannot load sqlite-vec, where search runs
# keyword-only (#711) and the counts below stay empty by design.
vector_runtime_available: bool = True

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the legacy embedding-status response field

When the cloud/API server is upgraded before an installed CLI, this response no longer includes semantic_search_enabled, but older releases define that field as required and ProjectClient.get_info() validates the remote JSON as ProjectInfoResponse. Consequently, bm project info against the upgraded server fails validation instead of rendering status; retain the deprecated field during the compatibility window while adding vector_runtime_available for the new distinction.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a76c6ed: EmbeddingStatus keeps semantic_search_enabled as a computed field mirroring vector_runtime_available, so older CLIs keep validating. Test: test_embedding_status_still_emits_the_deprecated_semantic_field.

… keep the legacy status field

- A sqlite-vec binary that imports but will not load (incompatible wheel, loading
  denied) raised sqlite3.OperationalError past init_search_index() and aborted
  startup. It now becomes SemanticDependenciesMissingError and takes the
  keyword-only fallback (#711), which matters now that there is no config flag to
  turn semantic search off.
- EmbeddingStatus keeps emitting semantic_search_enabled as a computed mirror of
  vector_runtime_available, so CLIs released before the flag was removed can still
  read project info from an upgraded server.

Addresses Codex review on #1696.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a76c6ed7dd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if is_memory_url:
query = resolved_query
effective_search_type = _search_type_for(search_type, query)
effective_search_type = _search_type_for(search_type, query, [active_project.name])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the resolved cloud route when choosing search mode

Fresh evidence after the earlier routing fixes: when project_id selects a cloud project whose bare name matches a locally configured LOCAL project, get_project_client() correctly routes by UUID/workspace, but this passes only the bare active_project.name back through routes_off_host(), which finds the local entry and returns false. On an extension-less local host, an ordinary search sent to Cloud is therefore silently downgraded from hybrid to text; carry the already-resolved route/workspace into the default-mode decision instead of re-deriving it from the possibly colliding name.

AGENTS.md reference: AGENTS.md:L347-L349

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declining. This needs three conditions at once: a cloud project selected by project_id whose bare name collides with a configured LOCAL project, on a host that cannot load sqlite-vec. The outcome is a working text search rather than hybrid, not a failure, and an explicit search_type still applies. Threading the resolved client route back into the default-mode choice would restructure search_notes for that case; not worth it in this PR.

semantic_search_enabled: bool
# False on a SQLite host that cannot load sqlite-vec, where search runs
# keyword-only (#711) and the counts below stay empty by design.
vector_runtime_available: bool = True

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept the legacy status field from older servers

When a newly upgraded CLI connects to an older Cloud/API server that emits only semantic_search_enabled: false, this new field is absent and defaults to true while the legacy input is ignored because it now names a computed field. Consequently ProjectInfoResponse.model_validate() reverses the server's status and bm project info reports semantic search as enabled; populate vector_runtime_available from the legacy field when the new field is missing so client-before-server rolling upgrades remain accurate.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f4d95f7: a before-validator fills vector_runtime_available from semantic_search_enabled when only the legacy field is present. Test: test_embedding_status_reads_the_legacy_field_from_older_servers.

An upgraded CLI talking to a server released before the flag was removed receives
only semantic_search_enabled. EmbeddingStatus now fills vector_runtime_available
from it when the new field is missing, so bm project info does not report a
disabled server as enabled during rolling upgrades.

Addresses Codex review on #1696.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8wjd6HrtSA2LN9ssC4NzF
Signed-off-by: phernandez <paul@basicmachines.co>
@phernandez
phernandez merged commit 61d965e into main Oct 9, 2026
42 checks passed
@phernandez
phernandez deleted the remove-semantic-flag branch October 9, 2026 04:58
phernandez added a commit that referenced this pull request Oct 9, 2026
#1696 removed `semantic_search_enabled` from BasicMemoryConfig, but it was
tested against a main that predated several milestone merges that still
read the flag, so main failed static checks and most test jobs:

- `index_project_and_report_readiness` (#1704) passed
  `embeddings=app_config.semantic_search_enabled`, which raised
  AttributeError on every `bm project add`. Semantic search is always on,
  so the add always runs the embedding pass.
- test_db_reindex (#1704) parametrized the add on the flag; it now
  asserts the embedding pass runs.
- test_deferred_embedding_resume (#1700) set the flag on its config.
- test_config_env_overrides_not_persisted (#1701) used the flag as its
  example env-overridden boolean; it now uses `format_on_save`.

Also makes #1704's cloud-prune test compare against str(Path) so it holds
on Windows, where the path prints with backslashes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APFUk2bjEwMptMQqpRhjea
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Oct 9, 2026
…gelog

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APFUk2bjEwMptMQqpRhjea
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Oct 9, 2026
…ated alias

#1696 adds vector_runtime_available and keeps emitting
semantic_search_enabled as a computed alias (and accepts it from older
servers), so the entry must not say the field was dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APFUk2bjEwMptMQqpRhjea
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Oct 9, 2026
…gelog

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APFUk2bjEwMptMQqpRhjea
Signed-off-by: phernandez <paul@basicmachines.co>
phernandez added a commit that referenced this pull request Oct 9, 2026
…ated alias

#1696 adds vector_runtime_available and keeps emitting
semantic_search_enabled as a computed alias (and accepts it from older
servers), so the entry must not say the field was dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APFUk2bjEwMptMQqpRhjea
Signed-off-by: phernandez <paul@basicmachines.co>
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