@@ -162,6 +162,7 @@ function commandResult(stdout = '', stderr = '', exitCode = 0) {
162162}
163163
164164function makeRunner ( options : {
165+ cloneResult ?: ReturnType < typeof commandResult >
165166 prepareStdout ?: string | string [ ]
166167 pushResult ?: ReturnType < typeof commandResult >
167168 roundFile ?: string
@@ -186,7 +187,7 @@ function makeRunner(options: {
186187 ) => {
187188 runCalls . push ( { command, envs : runOptions . envs , timeoutMs : runOptions . timeoutMs } )
188189 if ( command . includes ( 'git clone' ) ) {
189- return commandResult ( '__GIT_CONFIG_DIGEST__=digest-1\n' )
190+ return options . cloneResult ?? commandResult ( '__GIT_CONFIG_DIGEST__=digest-1\n' )
190191 }
191192 if ( command . includes ( 'pi -p --mode json' ) ) {
192193 runOptions . onStdout ?.( '{"type":"agent_end"}\n' )
@@ -312,6 +313,58 @@ describe('runBabysitPiWithOptions', () => {
312313 expect ( mockReviewLanded ) . toHaveBeenCalledTimes ( 1 )
313314 } )
314315
316+ it ( 'preserves known clean flags when the Babysit clone fails after the review request' , async ( ) => {
317+ mockFetchSnapshot . mockResolvedValue ( snapshot )
318+ mockFetchThreads . mockResolvedValue ( {
319+ actionable : [ ] ,
320+ skipped : [ ] ,
321+ totalUnresolved : 0 ,
322+ latestReview : null ,
323+ } )
324+ mockFetchChecks . mockResolvedValue ( greenChecks )
325+ const { runner } = makeRunner ( {
326+ cloneResult : commandResult ( '' , 'clone failed' , 1 ) ,
327+ } )
328+ mockWithPiSandbox . mockImplementation ( async ( callback ) => callback ( runner ) )
329+
330+ const result = await runBabysitPiWithOptions ( params ( ) , { onEvent : vi . fn ( ) } )
331+
332+ expect ( result ) . toMatchObject ( {
333+ stopReason : 'agent_failure' ,
334+ threadsClean : true ,
335+ checksGreen : true ,
336+ rounds : 0 ,
337+ commitsPushed : 0 ,
338+ } )
339+ } )
340+
341+ it ( 'uses remaining time for wait-only polling without reserving an agent round' , async ( ) => {
342+ mockFetchSnapshot . mockResolvedValue ( snapshot )
343+ mockFetchThreads . mockResolvedValue ( {
344+ actionable : [ ] ,
345+ skipped : [ ] ,
346+ totalUnresolved : 0 ,
347+ latestReview : null ,
348+ } )
349+ mockFetchChecks . mockResolvedValue ( greenChecks )
350+ const { runner } = makeRunner ( { } )
351+ mockWithPiSandbox . mockImplementation ( async ( callback ) => callback ( runner ) )
352+
353+ const result = await runBabysitPiWithOptions (
354+ params ( { executionBudgetMs : 2 * 60 * 1000 } ) ,
355+ { onEvent : vi . fn ( ) } ,
356+ { roundWaitMs : 30_000 }
357+ )
358+
359+ expect ( mockReviewLanded ) . toHaveBeenCalledTimes ( 1 )
360+ expect ( result ) . toMatchObject ( {
361+ stopReason : 'clean' ,
362+ threadsClean : true ,
363+ checksGreen : true ,
364+ rounds : 0 ,
365+ } )
366+ } )
367+
315368 it ( 'refuses excess failing checks before fetching discarded diagnostics' , async ( ) => {
316369 const failures = Array . from ( { length : 21 } , ( _ , index ) => ( {
317370 ...failingCheck ,
0 commit comments