diff --git a/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/RootReducer.kt b/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/RootReducer.kt index 60489bff..fdec8d53 100644 --- a/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/RootReducer.kt +++ b/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/RootReducer.kt @@ -16,10 +16,10 @@ object RootReducer { val phase = SnapshotPhaseReducer.process(state, action) var current = phase.state val allEffects = mutableListOf() - val isReplay = action is Action.ServerEventReceived && + val replayingBufferedEvents = action is Action.ServerEventReceived && action.message is ServerMessage.SnapshotComplete for (a in phase.actionsToDispatch) { - if (isReplay) { + if (replayingBufferedEvents) { val inner = SnapshotPhaseReducer.process(current, a) current = inner.state for (ia in inner.actionsToDispatch) { diff --git a/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/SessionsReducer.kt b/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/SessionsReducer.kt index 35b091ad..582878b0 100644 --- a/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/SessionsReducer.kt +++ b/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/SessionsReducer.kt @@ -337,7 +337,7 @@ object SessionsReducer { msg: ServerMessage.SessionStarted, clock: () -> Long, sessions: SessionsState, - ): Pair> { + ): Pair> { val summary = SessionSummary( id = msg.sessionId.value, status = "ACTIVE", @@ -348,8 +348,7 @@ object SessionsReducer { lastOutput = null, ) val selected = sessions.selectedId ?: msg.sessionId.value - return sessions.copy(sessions = sessions.sessions + summary, selectedId = selected) to - listOf(Effect.ConnectSession(msg.sessionId)) + return sessions.copy(sessions = sessions.sessions + summary, selectedId = selected) to emptyList() } private fun processInferenceCompletedMessage( diff --git a/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/SnapshotPhaseReducer.kt b/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/SnapshotPhaseReducer.kt index 402fb129..1ba264d7 100644 --- a/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/SnapshotPhaseReducer.kt +++ b/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/SnapshotPhaseReducer.kt @@ -77,6 +77,7 @@ object SnapshotPhaseReducer { is ServerMessage.ToolFailed -> msg.sessionId.value is ServerMessage.ToolRejected -> msg.sessionId.value is ServerMessage.ApprovalRequired -> msg.sessionId.value + is ServerMessage.SessionSnapshot -> msg.sessionId.value else -> null } diff --git a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/RootReducerTest.kt b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/RootReducerTest.kt index c7c76e1d..4cc4b982 100644 --- a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/RootReducerTest.kt +++ b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/RootReducerTest.kt @@ -118,6 +118,20 @@ class RootReducerTest { assertEquals(state.sessions.sessions, newState.sessions.sessions) } + @Test + fun `SnapshotComplete with empty pendingEvents flips phase and produces no sessions`() { + val initial = TuiState(snapshotPhase = true, pendingEvents = emptyList()) + val (result, effects) = RootReducer.reduce( + initial, + Action.ServerEventReceived(com.correx.apps.server.protocol.ServerMessage.SnapshotComplete), + fixedClock, + ) + assertEquals(false, result.snapshotPhase) + assertTrue(result.pendingEvents.isEmpty()) + assertTrue(result.sessions.sessions.isEmpty()) + assertTrue(effects.isEmpty()) + } + @Test fun `AppendChar followed by Backspace returns to original buffer`() { val initial = TuiState(inputMode = InputMode.ROUTER, inputBuffer = "ab") diff --git a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/SessionsReducerTest.kt b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/SessionsReducerTest.kt index 6a2420e8..fe447092 100644 --- a/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/SessionsReducerTest.kt +++ b/apps/tui/src/test/kotlin/com/correx/apps/tui/reducer/SessionsReducerTest.kt @@ -96,8 +96,7 @@ class SessionsReducerTest { assertEquals(1, state.sessions.size) assertEquals("s1", state.sessions[0].id) assertEquals("s1", state.selectedId) - assertEquals(1, effects.size) - assertTrue(effects[0] is Effect.ConnectSession) + assertTrue(effects.isEmpty()) } @Test