@@ -963,6 +963,50 @@ describe("tui-event-handlers: streaming watchdog", () => {
963963 handlers . dispose ?.( ) ;
964964 } ) ;
965965
966+ it ( "reloads history only once when reconnect recovery and deferred history refresh overlap" , ( ) => {
967+ const { state, loadHistory, noteLocalRunId, handlers } = createHarness ( {
968+ streamingWatchdogMs : 5_000 ,
969+ } ) ;
970+
971+ handlers . handleChatEvent ( {
972+ runId : "run-reconnect" ,
973+ sessionKey : state . currentSessionKey ,
974+ state : "delta" ,
975+ message : { content : "hello" } ,
976+ } satisfies ChatEvent ) ;
977+
978+ noteLocalRunId ( "run-local-empty" ) ;
979+ handlers . handleChatEvent ( {
980+ runId : "run-local-empty" ,
981+ sessionKey : state . currentSessionKey ,
982+ state : "final" ,
983+ } satisfies ChatEvent ) ;
984+
985+ handlers . pauseStreamingWatchdog ( ) ;
986+ handlers . reconnectStreamingWatchdog ( ) ;
987+ vi . advanceTimersByTime ( 5_001 ) ;
988+
989+ expect ( loadHistory ) . toHaveBeenCalledTimes ( 1 ) ;
990+
991+ handlers . dispose ?.( ) ;
992+ } ) ;
993+
994+ it ( "resets to idle when reconnect drops an active run that is no longer tracked" , ( ) => {
995+ const { state, setActivityStatus, handlers } = createHarness ( {
996+ streamingWatchdogMs : 5_000 ,
997+ } ) ;
998+ state . activeChatRunId = "run-stale" ;
999+ state . activityStatus = "streaming" ;
1000+
1001+ handlers . reconnectStreamingWatchdog ( ) ;
1002+
1003+ expect ( state . activeChatRunId ) . toBeNull ( ) ;
1004+ expect ( state . activityStatus ) . toBe ( "idle" ) ;
1005+ expect ( setActivityStatus ) . toHaveBeenLastCalledWith ( "idle" ) ;
1006+
1007+ handlers . dispose ?.( ) ;
1008+ } ) ;
1009+
9661010 it ( "keeps reconnect recovery armed when only terminal lifecycle arrives after reconnect" , ( ) => {
9671011 const { state, chatLog, setActivityStatus, loadHistory, handlers } = createHarness ( {
9681012 streamingWatchdogMs : 5_000 ,
0 commit comments