diff --git a/src/timeline/seed.rs b/src/timeline/seed.rs index caa44d0..fd26e31 100644 --- a/src/timeline/seed.rs +++ b/src/timeline/seed.rs @@ -48,6 +48,21 @@ pub async fn seed_timeline( }); } + // Guard: reject overly broad queries before running expensive ranked search. + // The count query (no bm25/snippet) is cheap even on broad matches. + const SEED_MATCH_CEILING: i64 = 10_000; + let match_count: i64 = conn.query_row( + "SELECT COUNT(*) FROM documents_fts WHERE documents_fts MATCH ?1", + [&fts_query], + |row| row.get(0), + )?; + if match_count > SEED_MATCH_CEILING { + return Err(crate::core::error::LoreError::Other(format!( + "Query too broad: matched {match_count} documents (ceiling: {SEED_MATCH_CEILING}). \ + Use a more specific query or narrow with --since.", + ))); + } + // Use hybrid search for seed entity discovery (better recall than FTS alone). // search_hybrid gracefully falls back to FTS-only when Ollama is unavailable. let filters = SearchFilters {