@@ -535,4 +535,70 @@ describe("MobileChatSurface", () => {
535535 expect ( text . indexOf ( "Completed shell" ) ) . toBeLessThan ( text . indexOf ( "Second assistant" ) ) ;
536536 } ) ;
537537
538+ it ( "polls Claude history for a busy terminal-owned turn and stops when the agent goes idle" , async ( ) => {
539+ const userMessage = {
540+ id : "user-1" ,
541+ turnId : "turn-1" ,
542+ order : 0 ,
543+ role : "user" as const ,
544+ kind : "text" as const ,
545+ text : "Build the feature" ,
546+ status : "completed" as const ,
547+ createdAt : "2026-05-28T10:00:00.000Z" ,
548+ } ;
549+ vi . mocked ( attachWorktreeConversation ) . mockResolvedValue (
550+ createConversationResponse ( "claudeCode" , {
551+ running : false ,
552+ activeTurnId : null ,
553+ messages : [ userMessage ] ,
554+ } ) ,
555+ ) ;
556+ vi . mocked ( fetchWorktreeConversationHistory ) . mockResolvedValue (
557+ createConversationResponse ( "claudeCode" , {
558+ running : false ,
559+ activeTurnId : null ,
560+ messages : [
561+ userMessage ,
562+ {
563+ id : "assistant-1" ,
564+ turnId : "turn-1" ,
565+ order : 1 ,
566+ role : "assistant" ,
567+ kind : "text" ,
568+ text : "Done from terminal" ,
569+ status : "completed" ,
570+ createdAt : "2026-05-28T10:00:01.000Z" ,
571+ } ,
572+ ] ,
573+ } ) ,
574+ ) ;
575+
576+ const { rerender } = render ( MobileChatSurface , {
577+ props : {
578+ worktree : createWorktree ( { agent : "working" , status : "running" } ) ,
579+ } ,
580+ } ) ;
581+
582+ await screen . findByText ( "Build the feature" ) ;
583+ // A terminal-owned turn has no backend stream to subscribe to.
584+ expect ( connectWorktreeConversationStream ) . not . toHaveBeenCalled ( ) ;
585+
586+ // Polling surfaces the terminal claude's flushed response live.
587+ await vi . advanceTimersByTimeAsync ( 1000 ) ;
588+ await waitFor ( ( ) => {
589+ expect ( fetchWorktreeConversationHistory ) . toHaveBeenCalledWith ( "feature/mobile-chat" ) ;
590+ } ) ;
591+ await screen . findByText ( "Done from terminal" ) ;
592+
593+ // Polling keeps running while the agent is busy (it must not settle early).
594+ await vi . advanceTimersByTimeAsync ( 5000 ) ;
595+ const callsWhileBusy = vi . mocked ( fetchWorktreeConversationHistory ) . mock . calls . length ;
596+ expect ( callsWhileBusy ) . toBeGreaterThan ( 1 ) ;
597+
598+ // When the run settles and the agent goes idle, polling stops.
599+ await rerender ( { worktree : createWorktree ( { agent : "waiting" , status : "idle" } ) } ) ;
600+ await vi . advanceTimersByTimeAsync ( 5000 ) ;
601+ expect ( vi . mocked ( fetchWorktreeConversationHistory ) . mock . calls . length ) . toBe ( callsWhileBusy ) ;
602+ } ) ;
603+
538604} ) ;
0 commit comments