[{"type":"text","text":"## Unified search handler implements Chroma-first with FTS5 fallback on zero results\n*Source: claude-mem://observation/10757*\n\n**Lines 403-489 show current implementation attempts Chroma first, falls back to FTS5 when empty.**\n\nThe unified search handler in search-server.ts (lines 390-489) reveals the current implementation architecture. The handler first attempts Chroma semantic search when chromaClient is available, calling queryChroma with the query parameter at line 419. If Chroma throws an error, it catches at line 465 and falls through to FTS5. However, the problematic FTS5 fallback logic at line 470 triggers when all result arrays are empty, not just on errors. This contradicts the insight that FTS5 contains identical data to Chroma, making zero-result fallback pointless. Additionally, lines 476-483 call search methods (searchObservations, searchSessions, searchUserPrompts) with the query parameter without checking if it's undefined, which will cause failures when query is not provided. The handler needs modification to skip Chroma when query is undefined and only fall back to FTS5 on Chroma errors.\n\n---\nType: discovery | Facts: Line 403 checks `if (chromaClient)` to attempt Chroma semantic search first; Line 419 calls `queryChroma(query, 100, whereFilter)` with query parameter as required; Lines 465-467 catch Chroma errors and fall through to FTS5 fallback; Line 470 triggers FTS5 fallback when all result arrays have zero length; Lines 476-483 call SessionSearch methods with query parameter without checking if undefined; Hybrid search applies 90-day recency filter at lines 424-431 | Concepts: how-it-works, problem-solution, gotcha | Files: src/servers/search-server.ts\n\n---\nDate: 11/17/2025, 11:50:29 PM\n\n---\n\n## Fixed Incorrect Parameter Array in searchUserPrompts FTS5 Path\n*Source: claude-mem://observation/10756*\n\n**Changed params to ftsParams in FTS5 query execution to use correct parameter array with ftsQuery.**\n\nA parameter array mismatch bug was fixed in the searchUserPrompts method's FTS5 code path. The method creates two separate parameter arrays: 'params' for the filter-only path and 'ftsParams' for the FTS5 path. The FTS5 path correctly initialized ftsParams with the escaped query and rebuilt all filter conditions into this new array, but then incorrectly used the 'params' array when executing the SQL query. This would have caused a parameter binding mismatch where the SQL query expected parameters in one order (starting with ftsQuery) but received them in a different order or with missing values. The fix ensures that when the FTS5 path is taken (query text provided), the query uses ftsParams.push() for limit/offset and passes ftsParams to the db.prepare().all() call, maintaining correct parameter alignment throughout the FTS5 execution path.\n\n---\nType: bugfix | Facts: File modified: /Users/alexnewman/Scripts/claude-mem/src/services/sqlite/SessionSearch.ts; Bug was in searchUserPrompts method's FTS5 execution path using wrong parameter array; Changed params.push(limit, offset) to ftsParams.push(limit, offset); Changed this.db.prepare(sql).all(...params) to this.db.prepare(sql).all(...ftsParams); The ftsParams array was created separately for FTS5 path but not being used in query execution; Bug would have caused SQL parameter binding mismatch in FTS5 search path for user prompts | Concepts: problem-solution, what-changed, gotcha | Files: /Users/alexnewman/Scripts/claude-mem/src/services/sqlite/SessionSearch.ts\n\n---\nDate: 11/17/2025, 11:50:17 PM\n\n---\n\n## Add filter-only query path to searchUserPrompts method\n*Source: claude-mem://observation/10755*\n\n**Method accepts undefined query and handles dual-path logic with separate parameter arrays for clarity.**\n\nThe searchUserPrompts method has been enhanced with filter-only query support, completing the pattern established in searchObservations and searchSessions. The method signature now accepts optional query parameter. The implementation builds base filter conditions once for project and date range filters, then diverges into two paths. The filter-only path (when query is undefined) validates that at least some filters exist, then queries the user_prompts table directly with a WHERE clause, joining sdk_sessions for project filtering. The FTS5 path rebuilds filter conditions into a separate ftsParams array to avoid parameter ordering conflicts between the two paths. This dual-path approach enables both semantic/keyword search via FTS5 and pure metadata filtering via direct SQLite queries, completing the architectural pattern across all three search methods.\n\n---\nType: feature | Facts: searchUserPrompts signature changed to accept `query: string | undefined` at line 541; Filter conditions built once and shared between filter-only and FTS5 paths at lines 546-563; Filter-only path at lines 566-588 validates filters exist and queries user_prompts table directly; FTS5 path at lines 591-616 rebuilds filter conditions with separate ftsParams array to avoid parameter conflicts; Filter-only path joins sdk_sessions table for project filtering support; Documentation updated to clarify dual-mode operation matching searchObservations and searchSessions patterns | Concepts: what-changed, how-it-works, pattern, gotcha | Files: src/services/sqlite/SessionSearch.ts\n\n---\nDate: 11/17/2025, 11:50:08 PM\n\n---\n\n## Line 472 Incorrectly Falls Back to FTS5 on Empty ChromaDB Results\n*Source: claude-mem://observation/10740*\n\n**The condition checks for zero results instead of ChromaDB failure, causing inappropriate FTS5 fallback on valid empty responses.**\n\nLine 472 in the current code contains the bug causing inappropriate FTS5 fallback behavior. The condition `if (observations.length === 0 && sessions.length === 0 && prompts.length === 0)` checks whether ChromaDB returned empty result arrays, but this check cannot distinguish between two scenarios: ChromaDB erroring (caught at line 465) versus ChromaDB successfully returning no matching results. The catch block at line 465 handles ChromaDB errors and execution falls through to line 472, where the empty arrays condition triggers FTS5 fallback. However, when ChromaDB succeeds but finds no semantic matches, it also returns empty arrays, incorrectly triggering the same FTS5 fallback. The solution is to introduce a chromaFailed boolean flag that is set to true only within the catch block when ChromaDB actually errors. Line 472 should then check this flag instead of checking array lengths, ensuring FTS5 fallback only occurs on actual ChromaDB failures, not on valid empty results.\n\n---\nType: discovery | Facts: Line 472 contains condition checking if observations, sessions, and prompts arrays are all empty; The catch block at line 465 handles ChromaDB errors and falls through to line 472; Line 472's zero-length check triggers FTS5 fallback both when ChromaDB errors AND when ChromaDB successfully returns empty results; ChromaDB returning zero results is a valid answer that should not trigger FTS5 fallback; The fix requires introducing a chromaFailed flag set only in the catch block, not on successful empty results; Line 472 condition should check the chromaFailed flag instead of checking result array lengths | Concepts: problem-solution, gotcha, how-it-works\n\n---\nDate: 11/17/2025, 11:42:30 PM\n\n---\n\n## Chroma requires query text; FTS5 fallback logic is incorrect\n*Source: claude-mem://observation/10735*\n\n**Chroma cannot do filter-only queries, and FTS5 fallback on zero results is pointless.**\n\nTwo critical insights emerge about the search architecture. First, Chroma cannot perform filter-only queries without query text because it fundamentally operates on semantic search via vector embeddings. When query is undefined, the system must bypass Chroma entirely and proceed directly to SQLite structured filtering. Second, the current FTS5 fallback logic at line 472 is fundamentally flawed - it triggers when Chroma returns zero results. However, since FTS5 maintains a 1:1 copy of the SQLite data that Chroma also indexes, if Chroma returns zero results, FTS5 will also return zero results. FTS5 fallback should only activate when Chroma is unavailable or encounters an error, not when it successfully returns an empty result set.\n\n---\nType: discovery | Facts: Chroma vector database requires query text for semantic search operations; Filter-only queries must skip Chroma and use SQLite structured filtering directly; FTS5 fallback at line 472 triggers on zero results, not on Chroma errors; FTS5 contains 1:1 copy of SQLite data, so zero Chroma results means zero FTS5 results; FTS5 fallback should only activate when Chroma is unavailable or errors, not on empty results | Concepts: problem-solution, gotcha, how-it-works, why-it-exists | Files: src/servers/search-server.ts\n\n---\nDate: 11/17/2025, 11:41:43 PM"}]