@@ -31,6 +31,7 @@ let embedBatchCalls = 0;
3131let embedBatchInputCalls = 0 ;
3232let providerRuntimeBatchCalls : string [ ] [ ] = [ ] ;
3333let providerRuntimeBatchGate : Promise < void > | null = null ;
34+ let providerRuntimeBatchFailuresRemaining = 0 ;
3435let providerRuntimeActiveBatchCalls = 0 ;
3536let providerRuntimeMaxActiveBatchCalls = 0 ;
3637let providerCloseCalls = 0 ;
@@ -162,6 +163,10 @@ vi.mock("./embeddings.js", () => {
162163 try {
163164 await providerRuntimeBatchGate ;
164165 providerRuntimeBatchCalls . push ( batch . chunks . map ( ( chunk ) => chunk . text ) ) ;
166+ if ( providerRuntimeBatchFailuresRemaining > 0 ) {
167+ providerRuntimeBatchFailuresRemaining -= 1 ;
168+ throw new Error ( "provider runtime batch failed" ) ;
169+ }
165170 return batch . chunks . map ( ( chunk ) => embedText ( chunk . text ) ) ;
166171 } finally {
167172 providerRuntimeActiveBatchCalls -= 1 ;
@@ -251,6 +256,7 @@ describe("memory index", () => {
251256 embedBatchInputCalls = 0 ;
252257 providerRuntimeBatchCalls = [ ] ;
253258 providerRuntimeBatchGate = null ;
259+ providerRuntimeBatchFailuresRemaining = 0 ;
254260 providerRuntimeActiveBatchCalls = 0 ;
255261 providerRuntimeMaxActiveBatchCalls = 0 ;
256262 providerCloseCalls = 0 ;
@@ -481,6 +487,40 @@ describe("memory index", () => {
481487 }
482488 } ) ;
483489
490+ it ( "maps source-wide batch fallback results to missing chunks after cache hits" , async ( ) => {
491+ const cfg = createCfg ( {
492+ provider : "batch-wide-test" ,
493+ batchEnabled : true ,
494+ storePath : path . join ( workspaceDir , "index-cross-file-batch-fallback-cache.sqlite" ) ,
495+ } ) ;
496+ const manager = await getFreshManager ( cfg ) ;
497+ try {
498+ await manager . sync ( { reason : "test" } ) ;
499+
500+ await fs . writeFile ( path . join ( memoryDir , "2026-01-13.md" ) , "# Log\nBeta memory line." ) ;
501+ providerRuntimeBatchCalls = [ ] ;
502+ providerRuntimeBatchFailuresRemaining = 1 ;
503+ embedBatchCalls = 0 ;
504+
505+ await manager . sync ( { reason : "test" , force : true } ) ;
506+
507+ expect ( providerRuntimeBatchCalls ) . toEqual ( [ [ "# Log\nBeta memory line." ] ] ) ;
508+ expect ( embedBatchCalls ) . toBe ( 1 ) ;
509+ const betaRow = (
510+ manager as unknown as {
511+ db : { prepare : ( sql : string ) => { get : ( ...args : unknown [ ] ) => unknown } } ;
512+ }
513+ ) . db
514+ . prepare ( "SELECT embedding FROM chunks WHERE path LIKE ? AND source = ?" )
515+ . get ( "%2026-01-13.md" , "memory" ) as { embedding : string } | undefined ;
516+
517+ expect ( betaRow ) . toBeDefined ( ) ;
518+ expect ( JSON . parse ( betaRow ?. embedding ?? "[]" ) ) . toEqual ( [ 0 , 1 , 0 , 0 ] ) ;
519+ } finally {
520+ await manager . close ?.( ) ;
521+ }
522+ } ) ;
523+
484524 it ( "splits oversized source-wide embedding requests at the request cap" , ( ) => {
485525 expect ( splitSourceWideEmbeddingChunks ( [ "one" , "two" , "three" , "four" , "five" ] , 2 ) ) . toEqual ( [
486526 [ "one" , "two" ] ,
0 commit comments