From 78379d244fd206bc594853f5d4495919eb93d424 Mon Sep 17 00:00:00 2001 From: kami Date: Mon, 25 May 2026 00:31:49 +0400 Subject: [PATCH] feat(tui-01): add snapshot phase, pending events, and cursor tracking to TuiState Add three new fields to support the snapshot/live streaming protocol phase: - snapshotPhase: Boolean (true during initial snapshot) - pendingEvents: List (FIFO buffer during snapshot) - cursors: Map (per-session high-water marks for deduplication) All fields include KDoc documenting their invariants and purpose. Additive change only; all existing tests continue to pass. --- .../com/correx/apps/tui/state/TuiState.kt | 17 +++++++++++++++++ .../correx/apps/tui/reducer/RootReducerTest.kt | 8 ++++++++ 2 files changed, 25 insertions(+) diff --git a/apps/tui/src/main/kotlin/com/correx/apps/tui/state/TuiState.kt b/apps/tui/src/main/kotlin/com/correx/apps/tui/state/TuiState.kt index 49070a04..8fb3ccbf 100644 --- a/apps/tui/src/main/kotlin/com/correx/apps/tui/state/TuiState.kt +++ b/apps/tui/src/main/kotlin/com/correx/apps/tui/state/TuiState.kt @@ -1,5 +1,7 @@ package com.correx.apps.tui.state +import com.correx.apps.server.protocol.ServerMessage + enum class InputMode { ROUTER, NAVIGATE, FILTER, STEER } enum class ToolDisplayStatus { REQUESTED, STARTED, COMPLETED, FAILED, REJECTED } @@ -33,6 +35,21 @@ data class TuiState( val providerType: ProviderType = ProviderType.LOCAL, val routerConnected: Boolean = false, val routerMessages: List = emptyList(), + /** + * True from connect until SnapshotComplete observed. Reset to true on Disconnected. + * When true, event-bearing messages are buffered in [pendingEvents]; snapshots are applied immediately. + */ + val snapshotPhase: Boolean = true, + /** + * FIFO buffer of event-bearing server messages received while snapshotPhase == true. + * Drained atomically when snapshot phase completes. + */ + val pendingEvents: List = emptyList(), + /** + * Per-session high-water mark keyed by SessionId.value. Updated only on applied live events. + * Used to deduplicate events during live streaming phase. + */ + val cursors: Map = emptyMap(), ) data class SessionSummary( 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 8a35ef34..ad09b380 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 @@ -12,6 +12,14 @@ class RootReducerTest { private val fixedClock: () -> Long = { 1000L } + @Test + fun `TuiState defaults include snapshot phase, pending events, and cursors`() { + val state = TuiState() + assertEquals(true, state.snapshotPhase) + assertEquals(emptyList(), state.pendingEvents) + assertEquals(emptyMap(), state.cursors) + } + @Test fun `SubmitInput in ROUTER mode emits StartSession effect and resets buffer`() { val initial = TuiState(inputMode = InputMode.ROUTER, inputBuffer = "my-workflow")