@@ -256,6 +256,66 @@ async def test_reindex_vectors_respects_embed_opt_out(search_service, monkeypatc
256256 }
257257
258258
259+ @pytest .mark .asyncio
260+ async def test_reindex_vectors_purges_sqlite_vectors_before_sync (search_service , monkeypatch ):
261+ """Regression for #829: stale vec0 rows must be purged before batch sync."""
262+ repository = _sqlite_repo (search_service )
263+ repository ._semantic_enabled = True
264+ calls : list [str ] = []
265+
266+ monkeypatch .setattr (
267+ search_service .entity_repository ,
268+ "find_all" ,
269+ AsyncMock (return_value = [SimpleNamespace (id = 42 , entity_metadata = {})]),
270+ )
271+
272+ async def delete_stale_vector_rows ():
273+ calls .append ("purge" )
274+
275+ async def sync_entity_vectors_batch (entity_ids , progress_callback = None ):
276+ assert entity_ids == [42 ]
277+ assert progress_callback is None
278+ calls .append ("sync" )
279+ return VectorSyncBatchResult (entities_total = 1 , entities_synced = 1 , entities_failed = 0 )
280+
281+ monkeypatch .setattr (repository , "delete_stale_vector_rows" , delete_stale_vector_rows )
282+ monkeypatch .setattr (search_service , "sync_entity_vectors_batch" , sync_entity_vectors_batch )
283+
284+ stats = await search_service .reindex_vectors ()
285+
286+ assert calls == ["purge" , "sync" ]
287+ assert stats == {
288+ "total_entities" : 1 ,
289+ "embedded" : 1 ,
290+ "skipped" : 0 ,
291+ "errors" : 0 ,
292+ }
293+
294+
295+ @pytest .mark .asyncio
296+ async def test_reindex_all_uses_sqlite_vec_aware_drop (search_service , monkeypatch ):
297+ """Full service reindex should not drop vec0 tables through a raw connection."""
298+ repository = _sqlite_repo (search_service )
299+ executed_sql : list [str ] = []
300+ calls : list [str ] = []
301+
302+ async def execute_query (query , params = None ):
303+ executed_sql .append (str (query ))
304+
305+ async def drop_vector_tables ():
306+ calls .append ("drop_vector_tables" )
307+
308+ monkeypatch .setattr (repository , "execute_query" , execute_query )
309+ monkeypatch .setattr (repository , "drop_vector_tables" , drop_vector_tables )
310+ monkeypatch .setattr (search_service , "init_search_index" , AsyncMock ())
311+ monkeypatch .setattr (search_service .entity_repository , "find_all" , AsyncMock (return_value = []))
312+
313+ await search_service .reindex_all ()
314+
315+ assert calls == ["drop_vector_tables" ]
316+ assert all ("search_vector_embeddings" not in sql for sql in executed_sql )
317+
318+
259319@pytest .mark .asyncio
260320async def test_reindex_vectors_force_full_clears_project_vectors_before_resync (
261321 search_service , monkeypatch
0 commit comments