feat(server-01): extend wire protocol with sequence cursors and SnapshotComplete
Every event-derived ServerMessage gains sequence (global) and sessionSequence (per-session) cursor fields. SessionSnapshot adds lastSequence and lastSessionSequence at projection-read time. SnapshotComplete data object terminates the snapshot phase and is registered in the polymorphic module. replaySnapshot() now emits SnapshotComplete after all SessionSnapshot messages. SessionStarted audit deferred with TODO comment. DomainEventMapper propagates both cursors to all mapped messages including InferenceCompleted.
This commit is contained in:
@@ -98,6 +98,8 @@ class ApprovalReducerTest {
|
||||
riskSummary = RiskSummaryDto(level = "HIGH", factors = emptyList(), recommendedAction = "review"),
|
||||
toolName = "bash",
|
||||
preview = "rm -rf /",
|
||||
sequence = 1L,
|
||||
sessionSequence = 1L,
|
||||
)
|
||||
val (state, effects) = reduce(action = Action.ServerEventReceived(msg))
|
||||
assertEquals("req-2", state.active?.requestId)
|
||||
|
||||
@@ -43,11 +43,15 @@ class RootReducerTest {
|
||||
val startMsg = com.correx.apps.server.protocol.ServerMessage.SessionStarted(
|
||||
sessionId = com.correx.core.events.types.SessionId("s1"),
|
||||
workflowId = "wf",
|
||||
sequence = 1L,
|
||||
sessionSequence = 1L,
|
||||
)
|
||||
val (state1, _) = RootReducer.reduce(state0, Action.ServerEventReceived(startMsg), fixedClock)
|
||||
val startMsg2 = com.correx.apps.server.protocol.ServerMessage.SessionStarted(
|
||||
sessionId = com.correx.core.events.types.SessionId("s2"),
|
||||
workflowId = "wf",
|
||||
sequence = 2L,
|
||||
sessionSequence = 1L,
|
||||
)
|
||||
val (state2, _) = RootReducer.reduce(state1, Action.ServerEventReceived(startMsg2), fixedClock)
|
||||
// selected is s1 (first); navigating down should move to s2
|
||||
|
||||
@@ -91,7 +91,7 @@ class SessionsReducerTest {
|
||||
|
||||
@Test
|
||||
fun `ServerEventReceived SessionStarted appends session and sets selection`() {
|
||||
val msg = ServerMessage.SessionStarted(sessionId = SessionId("s1"), workflowId = "wf1")
|
||||
val msg = ServerMessage.SessionStarted(sessionId = SessionId("s1"), workflowId = "wf1", sequence = 1L, sessionSequence = 1L)
|
||||
val (state, effects) = reduce(action = Action.ServerEventReceived(msg))
|
||||
assertEquals(1, state.sessions.size)
|
||||
assertEquals("s1", state.sessions[0].id)
|
||||
@@ -103,7 +103,7 @@ class SessionsReducerTest {
|
||||
@Test
|
||||
fun `ServerEventReceived SessionStarted preserves existing selection`() {
|
||||
val existing = SessionsState(sessions = listOf(session("existing")), selectedId = "existing")
|
||||
val msg = ServerMessage.SessionStarted(sessionId = SessionId("new"), workflowId = "wf")
|
||||
val msg = ServerMessage.SessionStarted(sessionId = SessionId("new"), workflowId = "wf", sequence = 1L, sessionSequence = 1L)
|
||||
val (state, _) = reduce(sessions = existing, action = Action.ServerEventReceived(msg))
|
||||
assertEquals("existing", state.selectedId)
|
||||
assertEquals(2, state.sessions.size)
|
||||
@@ -112,7 +112,7 @@ class SessionsReducerTest {
|
||||
@Test
|
||||
fun `ServerEventReceived SessionCompleted updates status to COMPLETED`() {
|
||||
val s = SessionsState(sessions = listOf(session("s1")), selectedId = "s1")
|
||||
val msg = ServerMessage.SessionCompleted(sessionId = SessionId("s1"))
|
||||
val msg = ServerMessage.SessionCompleted(sessionId = SessionId("s1"), sequence = 1L, sessionSequence = 1L)
|
||||
val (state, _) = reduce(sessions = s, action = Action.ServerEventReceived(msg))
|
||||
assertEquals("COMPLETED", state.sessions[0].status)
|
||||
assertEquals(1000L, state.sessions[0].lastEventAt)
|
||||
@@ -121,7 +121,7 @@ class SessionsReducerTest {
|
||||
@Test
|
||||
fun `ServerEventReceived SessionFailed updates status to FAILED`() {
|
||||
val s = SessionsState(sessions = listOf(session("s1")), selectedId = "s1")
|
||||
val msg = ServerMessage.SessionFailed(sessionId = SessionId("s1"), reason = "oops")
|
||||
val msg = ServerMessage.SessionFailed(sessionId = SessionId("s1"), reason = "oops", sequence = 1L, sessionSequence = 1L)
|
||||
val (state, _) = reduce(sessions = s, action = Action.ServerEventReceived(msg))
|
||||
assertEquals("FAILED", state.sessions[0].status)
|
||||
}
|
||||
@@ -136,6 +136,8 @@ class SessionsReducerTest {
|
||||
sessionId = SessionId("s1"),
|
||||
stageId = StageId("stage-1"),
|
||||
occurredAt = fixedClock(),
|
||||
sequence = 1L,
|
||||
sessionSequence = 1L,
|
||||
)
|
||||
val (state, _) = reduce(sessions = s, action = Action.ServerEventReceived(msg))
|
||||
assertEquals(null, state.sessions[0].currentStage)
|
||||
@@ -153,6 +155,8 @@ class SessionsReducerTest {
|
||||
stageId = StageId("stage-1"),
|
||||
reason = "error",
|
||||
occurredAt = fixedClock(),
|
||||
sequence = 1L,
|
||||
sessionSequence = 1L,
|
||||
)
|
||||
val (state, _) = reduce(sessions = s, action = Action.ServerEventReceived(msg))
|
||||
assertEquals(null, state.sessions[0].currentStage)
|
||||
@@ -183,6 +187,8 @@ class SessionsReducerTest {
|
||||
sessionId = SessionId("s1"), stageId = StageId("stage-1"),
|
||||
outputSummary = "summary", responseText = "full response",
|
||||
occurredAt = fixedClock(),
|
||||
sequence = 1L,
|
||||
sessionSequence = 1L,
|
||||
)
|
||||
val (state, _) = reduce(sessions = s, action = Action.ServerEventReceived(msg))
|
||||
assertEquals("summary", state.sessions[0].lastOutput)
|
||||
@@ -197,6 +203,8 @@ class SessionsReducerTest {
|
||||
sessionId = SessionId("s1"), stageId = StageId("stage-1"),
|
||||
outputSummary = "", responseText = "",
|
||||
occurredAt = fixedClock(),
|
||||
sequence = 1L,
|
||||
sessionSequence = 1L,
|
||||
)
|
||||
val (state, _) = reduce(sessions = s, action = Action.ServerEventReceived(msg))
|
||||
assertEquals(null, state.sessions[0].lastResponseText)
|
||||
@@ -209,6 +217,8 @@ class SessionsReducerTest {
|
||||
sessionId = SessionId("s1"),
|
||||
stageId = StageId("stage-1"),
|
||||
occurredAt = fixedClock(),
|
||||
sequence = 1L,
|
||||
sessionSequence = 1L,
|
||||
)
|
||||
val (state, _) = reduce(sessions = s, action = Action.ServerEventReceived(msg))
|
||||
assertEquals("stage-1", state.sessions[0].currentStage)
|
||||
@@ -223,6 +233,8 @@ class SessionsReducerTest {
|
||||
toolName = "file_write",
|
||||
outputSummary = "wrote 3 lines",
|
||||
occurredAt = fixedClock(),
|
||||
sequence = 1L,
|
||||
sessionSequence = 1L,
|
||||
)
|
||||
val (state, _) = reduce(sessions = s, action = Action.ServerEventReceived(msg))
|
||||
assertEquals("file_write: wrote 3 lines", state.sessions[0].lastOutput)
|
||||
@@ -232,7 +244,7 @@ class SessionsReducerTest {
|
||||
@Test
|
||||
fun `ServerEventReceived SessionPaused with APPROVAL_PENDING sets status label`() {
|
||||
val s = SessionsState(sessions = listOf(session("s1")), selectedId = "s1")
|
||||
val msg = ServerMessage.SessionPaused(sessionId = SessionId("s1"), reason = PauseReason.APPROVAL_PENDING)
|
||||
val msg = ServerMessage.SessionPaused(sessionId = SessionId("s1"), reason = PauseReason.APPROVAL_PENDING, sequence = 1L, sessionSequence = 1L)
|
||||
val (state, _) = reduce(sessions = s, action = Action.ServerEventReceived(msg))
|
||||
assertEquals("PAUSED awaiting approval", state.sessions[0].status)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user