feat(tui): truthful-state §2 — last-event clock + unknown-event raw fallback

Implements the load-bearing TUI-requirements §2 hard requirements:

- Last-event clock: "last event Ns ago" always visible in the status bar while
  in-session, updating every frame tick. Turns warn-colored when an active session
  has gone quiet past the stale threshold — the "thinking vs hung" tell. The
  WebSocket connection indicator (●/◌/○) already covers immediate drop visibility.
- Unknown-event raw fallback: applyServer now surfaces any unrecognized,
  session-scoped event type as a raw row in the event stream instead of silently
  dropping it (the frontend must not lie by omission). Recognized-but-unrendered
  types (router.response, protocol_error) get an explicit no-op so the default
  branch means genuinely unknown.

Tests: agoLabel formatting, unknown-event surfaces-as-row, known-ignored
no-spurious-row. Preview "session" frame exercises the clock.

Deferred: the full Go↔Kotlin per-event-type render matrix (§2 last bullet) — a
larger fixture effort than the core behaviors landed here.
This commit is contained in:
2026-06-13 10:46:06 +04:00
parent 4f6bfa85f7
commit 8b896b519a
4 changed files with 116 additions and 0 deletions
+16
View File
@@ -322,6 +322,22 @@ func (m *Model) applyServer(msg protocol.ServerMessage) {
if m.configIndex >= len(m.configFields) {
m.configIndex = 0
}
case protocol.TypeRouterResponse, protocol.TypeProtocolError:
// Recognized but intentionally not rendered here: router.response is
// superseded by chat.turn events on the global stream, and protocol_error is
// a transport diagnostic. Explicit no-op so the default below means *unknown*.
default:
// Unknown-event raw fallback (TUI-requirements §2): a session-scoped event
// type this client doesn't recognize is surfaced as a raw row in the event
// stream, never silently dropped — the frontend must not lie by omission.
if msg.SessionID != "" {
s := m.ensureSession(msg.SessionID)
at := msg.OccurredAt
if at == 0 {
at = nowMillis()
}
s.addEvent(at, msg.Type, "(raw — unrendered event)")
}
}
// Background-update badge for non-selected sessions.