refactor(core): move FTS execution behind FtsBackend and bind the read path to scope - #1563
Merged
phernandez merged 1 commit intoSep 15, 2026
Conversation
…d path to scope Second step of #1558. The project-bound repositories no longer own full-text execution, and every statement the shared read path issues binds the repository's ProjectScope instead of assuming a single :project_id. No query behavior changes. - search_filters.FtsBackend: the narrow contract for running a compiled full-text statement on one engine. SQLiteFts and PostgresFts implement it in the backend query modules, carrying the bodies that used to live in each repository's search()/count(): FTS5 syntax errors answer empty; Postgres retries a malformed strict tsquery relaxed inside a savepoint. - SearchRepositoryBase.search and count are concrete: shared vector/hybrid dispatch, then self._fts. The two repositories lose their search(), count(), pass-through _run_vector_query overrides, and the SQLite entity-column cache (now inside SQLiteFts). - Manifest hydration, candidate row fetch, readiness, and hydration-drop classification take the scope. current_vector_manifest_predicate replaces the :project_id constant; project_readiness uses it for its one project. - PreparedSearchQuery moves to repository.search_query with defaults so the repository layer can build and pass it. compile_fts_filter takes it. - The filter helpers both backends share, and the SearchIndexKey alias, move out of the base class into search_filters and search_index_row. - Test fakes that bypass __init__ set scope beside project_id, and their search() overrides carry the session parameter the base method has. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_019YW9ysxugGGBCNEGzsxtFV Signed-off-by: phernandez <[email protected]>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
phernandez
added this pull request to stack #1564
September 15, 2026 17:08
This was referenced Sep 15, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Part 2a of the #1558 stack, on top of #1562. The goal is one read pipeline parameterized by project scope, shared by the project route, the database-scoped route, and MCP
search_all_projects. This PR takes full-text execution out of the project-bound repositories and makes every statement the shared read path issues bind the repository'sProjectScope. No query behavior changes.Part 2b extracts the vector, fusion, relaxation, and chunk-hydration path into a
SearchReaderthe routes can construct without a repository. Splitting keeps this diff reviewable: 2a is scope plumbing and a moved FTS body; 2b is the class extraction and the test-double migration that comes with it.What changed
FtsBackendinsearch_filters: the narrow contract for running a compiled full-text statement on one engine.SQLiteFtsandPostgresFtsimplement it in the backend query modules with the bodies that used to live in each repository'ssearch()andcount(). FTS5 syntax errors answer empty. Postgres retries a malformed strict tsquery relaxed inside a savepoint so a caller-owned transaction survives.SearchRepositoryBase.searchandcountare concrete. Shared vector and hybrid dispatch first, thenself._fts. Both repositories lose theirsearch(),count(), the pass-through_run_vector_queryoverrides, and the SQLite entity-column cache, which now lives inSQLiteFts.read_manifest_readiness, andclassify_hydration_dropstake the scope.current_vector_manifest_predicate(scope, params)replaces the:project_idconstant;project_readinessuses it for its one project.PreparedSearchQuerymoves torepository/search_query.pywith defaults, so the repository layer can build and pass it.compile_fts_filtertakes it instead of thirteen keyword arguments. The service imports it from there.SearchIndexKeyalias, move out of the base class intosearch_filtersandsearch_index_row.__init__setscopebesideproject_id, and theirsearch()overrides carry thesessionparameter the base method has. Postgres tests patch the tsquery classifier onpostgres_search_query, wherePostgresFtsreads it.Kept for 2b on purpose: the
best_distanceandhasattr(_semantic_vector_index)compatibility branches in the base vector path. About fourteen test files patch_run_vector_queryand_ensure_vector_tableson repository instances; those move together with the reader.Testing
just fast-checkuv run pytest tests/repository tests/services tests/api/v2/test_search*.py tests/api/v2/test_cjk_search_guidance.py tests/mcp/test_tool_search.py --no-cov(SQLite)BASIC_MEMORY_TEST_POSTGRES=1 uv run pytestover the search repository, quoted-query, relaxation, temporal, candidate-restriction, rerank pipeline, trace, readiness, and search router suites (testcontainers)just doctor🤖 Generated with Claude Code
https://claude.ai/code/session_019YW9ysxugGGBCNEGzsxtFV