1- import { describe , expect , mock , test } from "bun:test" ;
1+ import { afterEach , beforeEach , describe , expect , test } from "bun:test" ;
22import { Database } from "../../../shared/sqlite" ;
33import { closeQuietly } from "../../../shared/sqlite-helpers" ;
4+ import {
5+ _resetProjectEmbeddingRegistryForTests ,
6+ _setTestProviderFactoryForProject ,
7+ type ProjectEmbeddingRegistrationSnapshot ,
8+ registerProjectEmbedding ,
9+ } from "../memory/embedding" ;
10+ import type { EmbeddingProvider , EmbeddingPurpose } from "../memory/embedding-provider" ;
411import { float32ArrayToBlob } from "../memory/storage-memory-embeddings" ;
512import { runMigrations } from "../migrations" ;
613import { initializeDatabase } from "../storage-db" ;
@@ -21,6 +28,45 @@ function makeDb(): Database {
2128 return db ;
2229}
2330
31+ let EMBED_UP = true ;
32+
33+ function installRecallTestProvider ( ) : void {
34+ _setTestProviderFactoryForProject (
35+ ( ) : EmbeddingProvider => ( {
36+ modelId : "test-provider-model" ,
37+ initialize : async ( ) => true ,
38+ embed : async ( text : string , _signal ?: AbortSignal , _purpose ?: EmbeddingPurpose ) => {
39+ if ( ! EMBED_UP ) return null ;
40+ return text . includes ( "auth" ) ? new Float32Array ( [ 1 , 0 ] ) : new Float32Array ( [ 0 , 1 ] ) ;
41+ } ,
42+ embedBatch : async (
43+ texts : string [ ] ,
44+ _signal ?: AbortSignal ,
45+ _purpose ?: EmbeddingPurpose ,
46+ ) =>
47+ texts . map ( ( t ) => {
48+ if ( ! EMBED_UP ) return new Float32Array ( ) ;
49+ return t . includes ( "auth" ) ? new Float32Array ( [ 1 , 0 ] ) : new Float32Array ( [ 0 , 1 ] ) ;
50+ } ) ,
51+ dispose : async ( ) => { } ,
52+ isLoaded : ( ) => true ,
53+ } ) ,
54+ ) ;
55+ }
56+
57+ function registerRecallTestProject (
58+ db : Database ,
59+ identity : string ,
60+ ) : ProjectEmbeddingRegistrationSnapshot {
61+ return registerProjectEmbedding (
62+ db ,
63+ identity ,
64+ { provider : "local" , model : "mock-model" } ,
65+ { memoryEnabled : true , gitCommitEnabled : false } ,
66+ identity ,
67+ ) ;
68+ }
69+
2470describe ( "flatRecall" , ( ) => {
2571 test ( "returns empty array when no notes exist (cold-start rung 5)" , ( ) => {
2672 const db = makeDb ( ) ;
@@ -119,20 +165,6 @@ describe("rankRung1", () => {
119165 } ) ;
120166} ) ;
121167
122- let EMBED_UP = true ;
123- mock . module ( "../memory/embedding" , ( ) => ( {
124- embedTextForProject : async ( _p : string , text : string ) =>
125- EMBED_UP
126- ? {
127- vector : text . includes ( "auth" )
128- ? new Float32Array ( [ 1 , 0 ] )
129- : new Float32Array ( [ 0 , 1 ] ) ,
130- modelId : "m1" ,
131- generation : 1 ,
132- }
133- : null ,
134- } ) ) ;
135-
136168function modeOf ( block : string ) : string | null {
137169 return block . match ( / < s k i l l - m e m o r y [ ^ > ] * \b m o d e = " ( [ ^ " ] + ) " / ) ?. [ 1 ] ?? null ;
138170}
@@ -144,9 +176,21 @@ const cfg = {
144176} ;
145177
146178describe ( "recallSkillMemoryBlock (intent-scoped rungs)" , ( ) => {
179+ beforeEach ( ( ) => {
180+ _resetProjectEmbeddingRegistryForTests ( ) ;
181+ _setTestProviderFactoryForProject ( null ) ;
182+ installRecallTestProvider ( ) ;
183+ } ) ;
184+
185+ afterEach ( ( ) => {
186+ _resetProjectEmbeddingRegistryForTests ( ) ;
187+ _setTestProviderFactoryForProject ( null ) ;
188+ } ) ;
189+
147190 test ( "rung 1 full: provider up + intent + a model-matched embedded note" , async ( ) => {
148191 const db = makeDb ( ) ;
149192 EMBED_UP = true ;
193+ const snap = registerRecallTestProject ( db , "git:x" ) ;
150194 insertSkillMemoryNote ( db , {
151195 skillId : "s" ,
152196 resolvedPath : "/p" ,
@@ -160,7 +204,7 @@ describe("recallSkillMemoryBlock (intent-scoped rungs)", () => {
160204 createdAt : 1 ,
161205 intentEmbedding : float32ArrayToBlob ( new Float32Array ( [ 1 , 0 ] ) ) ,
162206 deltaEmbedding : float32ArrayToBlob ( new Float32Array ( [ 1 , 0 ] ) ) ,
163- embeddingModelVersion : "m1" ,
207+ embeddingModelVersion : snap . modelId ,
164208 } ) ;
165209 const block = await recallSkillMemoryBlock ( db , {
166210 skill : "s" ,
@@ -175,6 +219,7 @@ describe("recallSkillMemoryBlock (intent-scoped rungs)", () => {
175219 test ( "rung 2 no-intent: provider up, no intent → flat" , async ( ) => {
176220 const db = makeDb ( ) ;
177221 EMBED_UP = true ;
222+ registerRecallTestProject ( db , "git:x" ) ;
178223 insertSkillMemoryNote ( db , {
179224 skillId : "s" ,
180225 resolvedPath : "/p" ,
@@ -199,6 +244,7 @@ describe("recallSkillMemoryBlock (intent-scoped rungs)", () => {
199244 test ( "rung 3 fts5-fallback: provider down + intent → FTS" , async ( ) => {
200245 const db = makeDb ( ) ;
201246 EMBED_UP = false ;
247+ registerRecallTestProject ( db , "git:x" ) ;
202248 insertSkillMemoryNote ( db , {
203249 skillId : "s" ,
204250 resolvedPath : "/p" ,
@@ -224,6 +270,7 @@ describe("recallSkillMemoryBlock (intent-scoped rungs)", () => {
224270 test ( "rung 4 flat-fts: provider down + UNINDEXABLE intent (sanitize→empty) → flat" , async ( ) => {
225271 const db = makeDb ( ) ;
226272 EMBED_UP = false ;
273+ registerRecallTestProject ( db , "git:x" ) ;
227274 insertSkillMemoryNote ( db , {
228275 skillId : "s" ,
229276 resolvedPath : "/p" ,
@@ -249,6 +296,7 @@ describe("recallSkillMemoryBlock (intent-scoped rungs)", () => {
249296 test ( "rung 5 cold: no notes → empty block" , async ( ) => {
250297 const db = makeDb ( ) ;
251298 EMBED_UP = true ;
299+ registerRecallTestProject ( db , "git:x" ) ;
252300 const block = await recallSkillMemoryBlock ( db , {
253301 skill : "s" ,
254302 intent : "x" ,
@@ -262,6 +310,7 @@ describe("recallSkillMemoryBlock (intent-scoped rungs)", () => {
262310 test ( "zero model-matched → falls to rung 3, never empty full block" , async ( ) => {
263311 const db = makeDb ( ) ;
264312 EMBED_UP = true ;
313+ registerRecallTestProject ( db , "git:x" ) ;
265314 insertSkillMemoryNote ( db , {
266315 skillId : "s" ,
267316 resolvedPath : "/p" ,
@@ -289,6 +338,7 @@ describe("recallSkillMemoryBlock (intent-scoped rungs)", () => {
289338 test ( "intent-scoped recall matches a note sharing SOME (not all) intent tokens (OR semantics)" , async ( ) => {
290339 const db = makeDb ( ) ;
291340 EMBED_UP = false ; // force rung 3 FTS path
341+ registerRecallTestProject ( db , "git:abc" ) ;
292342 try {
293343 insertSkillMemoryNote ( db , {
294344 skillId : "tdd" ,
@@ -335,6 +385,7 @@ describe("recallSkillMemoryBlock (intent-scoped rungs)", () => {
335385 test ( "pinned notes appear even when intent doesn't match them (M2)" , async ( ) => {
336386 const db = makeDb ( ) ;
337387 EMBED_UP = true ;
388+ const snap = registerRecallTestProject ( db , "git:x" ) ;
338389 db . prepare (
339390 `INSERT INTO skill_memory (skill_id,resolved_path,tier,project_identity,intent,kind,delta,normalized_hash,hit_count,pinned,created_at)
340391 VALUES ('s','/p','global','*','old auth fix','fix','rotate token','h1',0,1,1)` ,
@@ -353,7 +404,7 @@ describe("recallSkillMemoryBlock (intent-scoped rungs)", () => {
353404 createdAt : 1000 + i ,
354405 intentEmbedding : float32ArrayToBlob ( new Float32Array ( [ 0 , 1 ] ) ) ,
355406 deltaEmbedding : float32ArrayToBlob ( new Float32Array ( [ 0 , 1 ] ) ) ,
356- embeddingModelVersion : "m1" ,
407+ embeddingModelVersion : snap . modelId ,
357408 } ) ;
358409 }
359410 const block = await recallSkillMemoryBlock ( db , {
@@ -407,9 +458,23 @@ describe("buildSkillMemoryBlock", () => {
407458} ) ;
408459
409460describe ( "cross-project global recall" , ( ) => {
461+ beforeEach ( ( ) => {
462+ _resetProjectEmbeddingRegistryForTests ( ) ;
463+ _setTestProviderFactoryForProject ( null ) ;
464+ installRecallTestProvider ( ) ;
465+ EMBED_UP = true ;
466+ } ) ;
467+
468+ afterEach ( ( ) => {
469+ _resetProjectEmbeddingRegistryForTests ( ) ;
470+ _setTestProviderFactoryForProject ( null ) ;
471+ } ) ;
472+
410473 test ( "a global note learned in repo A surfaces when recalled from repo B" , async ( ) => {
411474 const db = makeDb ( ) ;
412475 try {
476+ registerRecallTestProject ( db , "git:repoA" ) ;
477+ registerRecallTestProject ( db , "git:repoB" ) ;
413478 promoteSkillObservations ( db , "git:repoA" , [
414479 {
415480 skillId : "council" ,
@@ -436,6 +501,7 @@ describe("cross-project global recall", () => {
436501 // '*' rows, so historian notes were orphaned for project-local skills.
437502 const db = makeDb ( ) ;
438503 try {
504+ registerRecallTestProject ( db , "git:repoA" ) ;
439505 promoteSkillObservations ( db , "git:repoA" , [
440506 {
441507 skillId : "tdd" ,
@@ -459,6 +525,7 @@ describe("cross-project global recall", () => {
459525 test ( "a project-local AGENT note and a global HISTORIAN note both surface for the same skill" , async ( ) => {
460526 const db = makeDb ( ) ;
461527 try {
528+ registerRecallTestProject ( db , "git:repoLocal" ) ;
462529 // Agent-written, project-tier note.
463530 insertSkillMemoryNote ( db , {
464531 skillId : "tdd" ,
@@ -491,9 +558,22 @@ describe("cross-project global recall", () => {
491558} ) ;
492559
493560describe ( "recallSkillMemoryBlock bumps recall_count for surfaced notes" , ( ) => {
561+ beforeEach ( ( ) => {
562+ _resetProjectEmbeddingRegistryForTests ( ) ;
563+ _setTestProviderFactoryForProject ( null ) ;
564+ installRecallTestProvider ( ) ;
565+ EMBED_UP = true ;
566+ } ) ;
567+
568+ afterEach ( ( ) => {
569+ _resetProjectEmbeddingRegistryForTests ( ) ;
570+ _setTestProviderFactoryForProject ( null ) ;
571+ } ) ;
572+
494573 test ( "a surfaced note's recall_count increments per recall (no-intent rung)" , async ( ) => {
495574 const db = makeDb ( ) ;
496575 try {
576+ registerRecallTestProject ( db , "git:abc" ) ;
497577 insertSkillMemoryNote ( db , {
498578 skillId : "tdd" ,
499579 resolvedPath : "/p/SKILL.md" ,
@@ -537,6 +617,7 @@ describe("recallSkillMemoryBlock bumps recall_count for surfaced notes", () => {
537617 test ( "a cold-start recall (no notes) bumps nothing and returns empty" , async ( ) => {
538618 const db = makeDb ( ) ;
539619 try {
620+ registerRecallTestProject ( db , "git:abc" ) ;
540621 const block = await recallSkillMemoryBlock ( db , {
541622 skill : "ghost" ,
542623 scope : "global" ,
0 commit comments