feat(search): support multiple folders in the semantic-search path filter

Extend the ADR-027 Phase 2 path filter from a single path_prefix to a
list of folders. The new normalize_path_prefixes() helper is the single
source of truth for trimming, dropping blanks, and de-duplicating, and
folds the legacy single path_prefix into the list for backward
compatibility.

build_base_filter_conditions() adds one MatchText to the must clause for
a single folder (unchanged shape) and OR-s multiple folders via a nested
Filter(should=[...]) so a file under any selected folder matches while
still AND-ing against the ACL/doc_type/date conditions.

path_prefixes is threaded through every search surface: the
nc_semantic_search MCP tool, the visualization API (JSON body), and the
viz route (CSV query param). The Astrolabe frontend folder picker that
produces these lists ships in a companion astrolabe PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-06-03 12:51:37 +02:00
co-authored by Claude Opus 4.8
parent b91af923d2
commit de6c4b360d
9 changed files with 269 additions and 50 deletions
+7 -2
View File
@@ -55,6 +55,7 @@ class SemanticSearchAlgorithm(SearchAlgorithm):
modified_after: int | None = None,
modified_before: int | None = None,
path_prefix: str | None = None,
path_prefixes: list[str] | None = None,
**kwargs: Any,
) -> list[SearchResult]:
"""Execute semantic search using vector similarity.
@@ -79,8 +80,11 @@ class SemanticSearchAlgorithm(SearchAlgorithm):
seconds, UTC); ``None`` ⇒ open-ended (ADR-027).
modified_before: Inclusive upper bound on ``modified_at`` (Unix
seconds, UTC); ``None`` ⇒ open-ended (ADR-027).
path_prefix: Folder/path filter on ``file_path`` (files only);
``None`` ⇒ no path filter (ADR-027 Phase 2).
path_prefix: Deprecated single folder filter; folded into
``path_prefixes`` (ADR-027 Phase 2).
path_prefixes: Folder/path filters on ``file_path`` (files only),
OR-ed together; ``None``/empty ⇒ no path filter (ADR-027
Phase 2).
**kwargs:
- score_threshold (float): override the instance default
@@ -122,6 +126,7 @@ class SemanticSearchAlgorithm(SearchAlgorithm):
modified_after=modified_after,
modified_before=modified_before,
path_prefix=path_prefix,
path_prefixes=path_prefixes,
)
# ACL pre-filter (design §11), opt-in via ACL_PREFILTER_ENABLED and OFF