@@ -32,10 +32,13 @@ import {
3232 embedTextForProject ,
3333 embedUnembeddedCompartmentChunksForProject ,
3434 embedUnembeddedMemoriesForProject ,
35+ flushShadowEmbeddingBacklog ,
3536 getProjectEmbeddingSnapshot ,
37+ getShadowBackfillStopReason ,
3638 markProjectLoadUntrusted ,
3739 registerProjectEmbedding ,
3840 registerProjectInObservationMode ,
41+ registerProjectShadowEmbedding ,
3942 sweepAllRegisteredProjects ,
4043 sweepStaleEmbeddingIdentitiesForProject ,
4144} from "./project-embedding-registry" ;
@@ -1434,4 +1437,87 @@ describe("project embedding registry", () => {
14341437 expect ( outcome . embedded ) . toBeGreaterThanOrEqual ( 2 ) ;
14351438 expect ( outcome . embedded ) . toBeLessThan ( 4 ) ;
14361439 } ) ;
1440+
1441+ it ( "getShadowBackfillStopReason returns drained after a successful shadow backfill" , async ( ) => {
1442+ _setTestProviderFactoryForProject (
1443+ ( config ) =>
1444+ new FakeEmbeddingProvider ( config . provider === "local" ? config . model : "shadow" ) ,
1445+ ) ;
1446+ const db = useTempDb ( ) ;
1447+ const projectIdentity = "git:stop-drained" ;
1448+ registerProjectEmbedding (
1449+ db ,
1450+ projectIdentity ,
1451+ localConfig ( "model-primary" ) ,
1452+ { memoryEnabled : true , gitCommitEnabled : false } ,
1453+ "/tmp/stop-drained" ,
1454+ ) ;
1455+ // Seed a few primary memories so the shadow backfill has work to do.
1456+ for ( let i = 0 ; i < 3 ; i ++ ) {
1457+ const memory = insertMemory ( db , {
1458+ projectPath : projectIdentity ,
1459+ category : "CONSTRAINTS" ,
1460+ content : `drained memory ${ i } ` ,
1461+ } ) ;
1462+ saveEmbedding ( db , memory . id , new Float32Array ( [ i , 1 ] ) , currentModelId ( projectIdentity ) ) ;
1463+ }
1464+
1465+ registerProjectShadowEmbedding (
1466+ db ,
1467+ projectIdentity ,
1468+ {
1469+ provider : "synapse" ,
1470+ model : "synapse-model" ,
1471+ synapse_fingerprint : "fp-drained" ,
1472+ } as unknown as EmbeddingConfig ,
1473+ "/tmp/stop-drained" ,
1474+ ) ;
1475+ await flushShadowEmbeddingBacklog ( projectIdentity ) ;
1476+
1477+ expect ( getShadowBackfillStopReason ( projectIdentity , "memory" ) ) . toBe ( "drained" ) ;
1478+ } ) ;
1479+
1480+ it ( "getShadowBackfillStopReason returns stalled_no_progress when the provider cannot embed" , async ( ) => {
1481+ // Provider that yields null for every text → nothing persists → the
1482+ // pump sees the same missing ids twice and retires the scope as stalled.
1483+ _setTestProviderFactoryForProject (
1484+ ( config ) =>
1485+ new ( class extends FakeEmbeddingProvider {
1486+ override async embedBatch ( texts : string [ ] ) : Promise < Float32Array [ ] > {
1487+ return texts . map ( ( ) => null as unknown as Float32Array ) ;
1488+ }
1489+ } ) ( config . provider === "local" ? config . model : "shadow" ) ,
1490+ ) ;
1491+ const db = useTempDb ( ) ;
1492+ const projectIdentity = "git:stop-stalled" ;
1493+ registerProjectEmbedding (
1494+ db ,
1495+ projectIdentity ,
1496+ localConfig ( "model-primary" ) ,
1497+ { memoryEnabled : true , gitCommitEnabled : false } ,
1498+ "/tmp/stop-stalled" ,
1499+ ) ;
1500+ for ( let i = 0 ; i < 3 ; i ++ ) {
1501+ const memory = insertMemory ( db , {
1502+ projectPath : projectIdentity ,
1503+ category : "CONSTRAINTS" ,
1504+ content : `stalled memory ${ i } ` ,
1505+ } ) ;
1506+ saveEmbedding ( db , memory . id , new Float32Array ( [ i , 1 ] ) , currentModelId ( projectIdentity ) ) ;
1507+ }
1508+
1509+ registerProjectShadowEmbedding (
1510+ db ,
1511+ projectIdentity ,
1512+ {
1513+ provider : "synapse" ,
1514+ model : "synapse-model" ,
1515+ synapse_fingerprint : "fp-stalled" ,
1516+ } as unknown as EmbeddingConfig ,
1517+ "/tmp/stop-stalled" ,
1518+ ) ;
1519+ await flushShadowEmbeddingBacklog ( projectIdentity ) ;
1520+
1521+ expect ( getShadowBackfillStopReason ( projectIdentity , "memory" ) ) . toBe ( "stalled_no_progress" ) ;
1522+ } ) ;
14371523} ) ;
0 commit comments