@@ -10,12 +10,17 @@ import {
1010 replaceCompartmentChunkEmbeddings ,
1111} from "./compartment-chunk-embedding" ;
1212import { appendCompartments , getCompartments } from "./compartment-storage" ;
13+ import type { GitCommit } from "./git-commits/git-log-reader" ;
14+ import { countEmbeddedCommits } from "./git-commits/storage-git-commit-embeddings" ;
15+ import { upsertCommits } from "./git-commits/storage-git-commits" ;
16+ import { acquireGitSweepLease , releaseGitSweepLease } from "./git-commits/sweep-coordinator" ;
1317import type { EmbeddingProvider , EmbeddingPurpose } from "./memory/embedding-provider" ;
1418import { insertMemory } from "./memory/storage-memory" ;
1519import { getStoredModelId , saveEmbedding } from "./memory/storage-memory-embeddings" ;
1620import {
1721 _resetProjectEmbeddingRegistryForTests ,
1822 _setTestProviderFactoryForProject ,
23+ drainCommitBacklogForProject ,
1924 embedSessionCompartmentChunks ,
2025 embedTextForProject ,
2126 embedUnembeddedCompartmentChunksForProject ,
@@ -72,6 +77,17 @@ function localConfig(model: string, maxInputTokens?: number): EmbeddingConfig {
7277 } ;
7378}
7479
80+ function makeGitCommit ( shaSeed : string , committedAtMs : number ) : GitCommit {
81+ const sha = shaSeed . padEnd ( 40 , shaSeed ) ;
82+ return {
83+ sha,
84+ shortSha : sha . slice ( 0 , 7 ) ,
85+ message : `commit ${ shaSeed } ` ,
86+ author : "dev@example.com" ,
87+ committedAtMs,
88+ } ;
89+ }
90+
7591function seedCompartmentWithFts (
7692 db : NonNullable < ReturnType < typeof openDatabase > > ,
7793 sessionId : string ,
@@ -151,6 +167,92 @@ describe("project embedding registry", () => {
151167 tempDirs . length = 0 ;
152168 } ) ;
153169
170+ it ( "drainCommitBacklogForProject embeds pre-indexed commits with no new git log work" , async ( ) => {
171+ _setTestProviderFactoryForProject (
172+ ( config ) =>
173+ new FakeEmbeddingProvider ( config . provider === "local" ? config . model : "off" ) ,
174+ ) ;
175+ const db = useTempDb ( ) ;
176+ const projectIdentity = "git:commit-backlog" ;
177+ upsertCommits ( db , projectIdentity , [
178+ makeGitCommit ( "backlog-a" , 1000 ) ,
179+ makeGitCommit ( "backlog-b" , 2000 ) ,
180+ ] ) ;
181+ registerProjectEmbeddingAndMaybeWipe (
182+ db ,
183+ projectIdentity ,
184+ localConfig ( "model-commits" ) ,
185+ { memoryEnabled : true , gitCommitEnabled : true } ,
186+ "/tmp/commits" ,
187+ ) ;
188+
189+ expect ( countEmbeddedCommits ( db , projectIdentity ) ) . toBe ( 0 ) ;
190+ const drained = await drainCommitBacklogForProject (
191+ db ,
192+ projectIdentity ,
193+ Date . now ( ) + 60_000 ,
194+ ) ;
195+ expect ( drained ) . toBe ( 2 ) ;
196+ expect ( countEmbeddedCommits ( db , projectIdentity ) ) . toBe ( 2 ) ;
197+ } ) ;
198+
199+ it ( "drainCommitBacklogForProject skips when git commit indexing is disabled" , async ( ) => {
200+ _setTestProviderFactoryForProject (
201+ ( config ) =>
202+ new FakeEmbeddingProvider ( config . provider === "local" ? config . model : "off" ) ,
203+ ) ;
204+ const db = useTempDb ( ) ;
205+ const projectIdentity = "git:commits-off" ;
206+ upsertCommits ( db , projectIdentity , [ makeGitCommit ( "off-a" , 1000 ) ] ) ;
207+ registerProjectEmbeddingAndMaybeWipe (
208+ db ,
209+ projectIdentity ,
210+ localConfig ( "model-commits" ) ,
211+ { memoryEnabled : true , gitCommitEnabled : false } ,
212+ "/tmp/commits-off" ,
213+ ) ;
214+
215+ const drained = await drainCommitBacklogForProject (
216+ db ,
217+ projectIdentity ,
218+ Date . now ( ) + 60_000 ,
219+ ) ;
220+ expect ( drained ) . toBe ( 0 ) ;
221+ expect ( countEmbeddedCommits ( db , projectIdentity ) ) . toBe ( 0 ) ;
222+ } ) ;
223+
224+ it ( "drainCommitBacklogForProject short-circuits when the git sweep lease is held" , async ( ) => {
225+ _setTestProviderFactoryForProject (
226+ ( config ) =>
227+ new FakeEmbeddingProvider ( config . provider === "local" ? config . model : "off" ) ,
228+ ) ;
229+ const db = useTempDb ( ) ;
230+ const projectIdentity = "git:lease-block" ;
231+ upsertCommits ( db , projectIdentity , [ makeGitCommit ( "lease-a" , 1000 ) ] ) ;
232+ registerProjectEmbeddingAndMaybeWipe (
233+ db ,
234+ projectIdentity ,
235+ localConfig ( "model-commits" ) ,
236+ { memoryEnabled : true , gitCommitEnabled : true } ,
237+ "/tmp/lease" ,
238+ ) ;
239+
240+ const holder = acquireGitSweepLease ( db , projectIdentity , "other-holder" ) ;
241+ expect ( holder . acquired ) . toBe ( true ) ;
242+
243+ const drained = await drainCommitBacklogForProject (
244+ db ,
245+ projectIdentity ,
246+ Date . now ( ) + 60_000 ,
247+ ) ;
248+ expect ( drained ) . toBe ( 0 ) ;
249+ expect ( countEmbeddedCommits ( db , projectIdentity ) ) . toBe ( 0 ) ;
250+
251+ if ( holder . acquired ) {
252+ releaseGitSweepLease ( db , projectIdentity , holder . holderId ) ;
253+ }
254+ } ) ;
255+
154256 it ( "keeps independent snapshots and providers for two projects in one process" , async ( ) => {
155257 _setTestProviderFactoryForProject (
156258 ( config ) =>
0 commit comments