feat(brahman-handshake): ListMatches endpoint + timeline en broker-explorer

Iter 21. Cierra el loop iniciado en iter 20: ahora se ven sesiones
+ matches actuales + cómo cambian a través del tiempo.

brahman-handshake/messages:
- Frame::ListMatches → Frame::MatchList(Vec<brahman_broker::Match>).

brahman-handshake/server:
- run_post_handshake pasa Option<&SharedBroker> a handle_inbound_frame.
- Sin broker configurado → MatchList vacía (no error).

brahman-handshake/client + brahman-sidecar:
- Client::list_matches() análogo a list_sessions, drena MatchEvents.
- list_matches / list_matches_blocking, mismo patrón.

brahman-broker-explorer:
- Poll-tick agrega list_matches_blocking además de list_sessions.
- last_match_keys: HashSet<MatchKey> para diff entre ticks.
- timeline: VecDeque<TimelineEntry> cap 50.
- diff_matches (free fn): Available para keys nuevas, Lost para
  desaparecidas. Primer tick marca todo Available (boot UX).
- Render: stat_card "Timeline" con HH:MM:SS {+/-} formato compacto.

5 tests broker-explorer (3 nuevos del diff). Stack verde.

Decisión: timeline polled cada POLL_INTERVAL=5s, no push. MatchEvents
del broker son consumer-céntricos (cada session ve sólo SUS matches);
"system-wide timeline" requeriría broker subscribe-all (mucho scope).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-05-10 15:53:38 +00:00
parent 99cd685dc1
commit a97f6b98f3
9 changed files with 499 additions and 16 deletions
+21 -2
View File
@@ -194,13 +194,30 @@ pub struct SessionList {
pub entries: Vec<SessionEntry>,
}
/// Pedido del listado de matches actuales del broker. La `session`
/// se valida igual que `ListSessions`. Si el server no tiene broker
/// configurado, devuelve la lista vacía (no es un error — refleja
/// que no hay matching activo).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ListMatches {
pub session: SessionId,
}
/// Respuesta a `ListMatches` con el snapshot de matches consumidor↔productor
/// actualmente computados por el broker.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MatchList {
pub matches: Vec<brahman_broker::Match>,
}
/// Frame único de wire — discriminada por variante. Cada conexión es un
/// stream de frames.
///
/// Direcciones:
/// - Cliente → Server: `Hello`, `Ping`, `Farewell`, `ListSessions`.
/// - Cliente → Server: `Hello`, `Ping`, `Farewell`, `ListSessions`,
/// `ListMatches`.
/// - Server → Cliente: `HelloAck`, `Pong`, `Error`, `MatchEvent`,
/// `SessionList`.
/// `SessionList`, `MatchList`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Frame {
Hello(Hello),
@@ -212,4 +229,6 @@ pub enum Frame {
MatchEvent(MatchEvent),
ListSessions(ListSessions),
SessionList(SessionList),
ListMatches(ListMatches),
MatchList(MatchList),
}