@@ -206,17 +206,21 @@ describe('index versioning migration (MIGR-01)', () => {
206206 } ) ;
207207
208208 afterEach ( async ( ) => {
209+ const { clearProjects } = await import ( '../src/project-state.js' ) ;
210+ clearProjects ( ) ;
211+
209212 if ( originalArgv ) process . argv = originalArgv ;
210213 if ( originalEnvRoot === undefined ) delete process . env . CODEBASE_ROOT ;
211214 else process . env . CODEBASE_ROOT = originalEnvRoot ;
212215
213216 if ( tempRoot ) {
214- await fs . rm ( tempRoot , { recursive : true , force : true } ) ;
217+ // Background indexing (fire-and-forget) may still be writing — retry on ENOTEMPTY
218+ await fs . rm ( tempRoot , { recursive : true , force : true , maxRetries : 3 , retryDelay : 100 } ) ;
215219 tempRoot = null ;
216220 }
217221 } ) ;
218222
219- it ( 'refuses legacy indexes without index-meta.json and triggers auto-heal rebuild' , async ( ) => {
223+ it ( 'refuses legacy indexes without index-meta.json and triggers background rebuild' , async ( ) => {
220224 if ( ! tempRoot ) throw new Error ( 'tempRoot not initialized' ) ;
221225
222226 const ctxDir = path . join ( tempRoot , CODEBASE_CONTEXT_DIRNAME ) ;
@@ -241,14 +245,14 @@ describe('index versioning migration (MIGR-01)', () => {
241245 } ) ;
242246
243247 const payload = JSON . parse ( response . content [ 0 ] . text ) ;
244- expect ( payload . status ) . toBe ( 'success' ) ;
248+ expect ( payload . status ) . toBe ( 'indexing' ) ;
249+ expect ( payload . message ) . toContain ( 'retry' ) ;
245250 expect ( payload . index ) . toBeTruthy ( ) ;
246- expect ( payload . index . action ) . toBe ( 'rebuilt-and-served ' ) ;
251+ expect ( payload . index . action ) . toBe ( 'rebuild-started ' ) ;
247252 expect ( String ( payload . index . reason || '' ) ) . toContain ( 'Index meta' ) ;
248- expect ( indexerMocks . index ) . toHaveBeenCalledTimes ( 1 ) ;
249253 } ) ;
250254
251- it ( 'detects keyword index header mismatch and triggers rebuild (no silent empty results) ' , async ( ) => {
255+ it ( 'detects keyword index header mismatch and triggers background rebuild ' , async ( ) => {
252256 if ( ! tempRoot ) throw new Error ( 'tempRoot not initialized' ) ;
253257
254258 const ctxDir = path . join ( tempRoot , CODEBASE_CONTEXT_DIRNAME ) ;
@@ -302,13 +306,13 @@ describe('index versioning migration (MIGR-01)', () => {
302306 } ) ;
303307
304308 const payload = JSON . parse ( response . content [ 0 ] . text ) ;
305- expect ( payload . status ) . toBe ( 'success' ) ;
306- expect ( payload . index . action ) . toBe ( 'rebuilt-and-served' ) ;
309+ expect ( payload . status ) . toBe ( 'indexing' ) ;
310+ expect ( payload . message ) . toContain ( 'retry' ) ;
311+ expect ( payload . index . action ) . toBe ( 'rebuild-started' ) ;
307312 expect ( String ( payload . index . reason || '' ) ) . toContain ( 'Keyword index' ) ;
308- expect ( indexerMocks . index ) . toHaveBeenCalledTimes ( 1 ) ;
309313 } ) ;
310314
311- it ( 'detects vector DB build marker mismatch and triggers rebuild' , async ( ) => {
315+ it ( 'detects vector DB build marker mismatch and triggers background rebuild' , async ( ) => {
312316 if ( ! tempRoot ) throw new Error ( 'tempRoot not initialized' ) ;
313317
314318 const ctxDir = path . join ( tempRoot , CODEBASE_CONTEXT_DIRNAME ) ;
@@ -362,10 +366,10 @@ describe('index versioning migration (MIGR-01)', () => {
362366 } ) ;
363367
364368 const payload = JSON . parse ( response . content [ 0 ] . text ) ;
365- expect ( payload . status ) . toBe ( 'success' ) ;
366- expect ( payload . index . action ) . toBe ( 'rebuilt-and-served' ) ;
369+ expect ( payload . status ) . toBe ( 'indexing' ) ;
370+ expect ( payload . message ) . toContain ( 'retry' ) ;
371+ expect ( payload . index . action ) . toBe ( 'rebuild-started' ) ;
367372 expect ( String ( payload . index . reason || '' ) ) . toContain ( 'Vector DB' ) ;
368- expect ( indexerMocks . index ) . toHaveBeenCalledTimes ( 1 ) ;
369373 } ) ;
370374} ) ;
371375
@@ -382,9 +386,47 @@ describe('index-consuming allowlist enforcement', () => {
382386 tempRoot = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , 'index-versioning-allowlist-' ) ) ;
383387 process . env . CODEBASE_ROOT = tempRoot ;
384388 process . argv [ 2 ] = tempRoot ;
389+
390+ const ctxDir = path . join ( tempRoot , CODEBASE_CONTEXT_DIRNAME ) ;
391+ const buildId = 'allowlist-build' ;
392+ const generatedAt = new Date ( ) . toISOString ( ) ;
393+
394+ await fs . mkdir ( path . join ( ctxDir , VECTOR_DB_DIRNAME ) , { recursive : true } ) ;
395+ await fs . writeFile (
396+ path . join ( ctxDir , VECTOR_DB_DIRNAME , 'index-build.json' ) ,
397+ JSON . stringify ( { buildId, formatVersion : INDEX_FORMAT_VERSION } ) ,
398+ 'utf-8'
399+ ) ;
400+ await fs . writeFile (
401+ path . join ( ctxDir , KEYWORD_INDEX_FILENAME ) ,
402+ JSON . stringify ( { header : { buildId, formatVersion : INDEX_FORMAT_VERSION } , chunks : [ ] } ) ,
403+ 'utf-8'
404+ ) ;
405+ await fs . writeFile (
406+ path . join ( ctxDir , INDEX_META_FILENAME ) ,
407+ JSON . stringify (
408+ {
409+ metaVersion : INDEX_META_VERSION ,
410+ formatVersion : INDEX_FORMAT_VERSION ,
411+ buildId,
412+ generatedAt,
413+ toolVersion : 'test' ,
414+ artifacts : {
415+ keywordIndex : { path : KEYWORD_INDEX_FILENAME } ,
416+ vectorDb : { path : VECTOR_DB_DIRNAME , provider : 'lancedb' }
417+ }
418+ } ,
419+ null ,
420+ 2
421+ ) ,
422+ 'utf-8'
423+ ) ;
385424 } ) ;
386425
387426 afterEach ( async ( ) => {
427+ const { clearProjects } = await import ( '../src/project-state.js' ) ;
428+ clearProjects ( ) ;
429+
388430 if ( originalArgv ) process . argv = originalArgv ;
389431 if ( originalEnvRoot === undefined ) delete process . env . CODEBASE_ROOT ;
390432 else process . env . CODEBASE_ROOT = originalEnvRoot ;
0 commit comments