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:
@@ -6,6 +6,60 @@ ratio/diff ver `git show <sha>`.
|
||||
|
||||
## 2026-05-10
|
||||
|
||||
### feat(brahman-handshake): ListMatches endpoint + timeline en broker-explorer
|
||||
Iter 21. Cierra el loop de observabilidad iniciado en iter 20: ahora
|
||||
se ven no sólo las sesiones registradas sino también qué matches
|
||||
consumer↔producer está computando el broker en cada momento, y la
|
||||
historia de cómo cambian.
|
||||
|
||||
`brahman-handshake/messages.rs`:
|
||||
- **`Frame::ListMatches(ListMatches{session})`**: pedido (mismo
|
||||
patrón de validación session-id).
|
||||
- **`Frame::MatchList(MatchList{matches: Vec<brahman_broker::Match>})`**:
|
||||
respuesta. Cada `Match` ya es serializable y lleva `consumer`,
|
||||
`consumer_label`, `producer`, `producer_label`, `ty`, `via`, `pinned`.
|
||||
|
||||
`brahman-handshake/server.rs`:
|
||||
- `run_post_handshake` ahora pasa también `broker_for_match: Option<&SharedBroker>`
|
||||
al `handle_inbound_frame`.
|
||||
- Si el server tiene broker configurado, `ListMatches` responde con
|
||||
`broker.all_matches()`. Si no (server sin broker), responde
|
||||
`MatchList { matches: vec![] }` — refleja "no hay matching activo",
|
||||
no es un error.
|
||||
|
||||
`brahman-handshake/client.rs`: `Client::list_matches()` análogo a
|
||||
`list_sessions()`, drena `MatchEvent`s intermedios al buffer.
|
||||
|
||||
`brahman-sidecar/discovery.rs`: `list_matches` y `list_matches_blocking`
|
||||
con la misma forma de Card observer minimalista.
|
||||
|
||||
`brahman-broker-explorer`:
|
||||
- Cada poll-tick ahora pide TANTO `list_sessions` COMO `list_matches`.
|
||||
- `Explorer.last_match_keys: HashSet<MatchKey>` mantiene el estado
|
||||
del último snapshot. La key es `(consumer.session, consumer.flow,
|
||||
producer.session, producer.flow)`.
|
||||
- `Explorer.timeline: VecDeque<TimelineEntry>` con cap `TIMELINE_CAP=50`.
|
||||
- Función pura `diff_matches(last_keys, list) -> (entries, new_keys)`:
|
||||
emite `Available` para keys nuevas y `Lost` para keys desaparecidas.
|
||||
Primer tick (last_keys vacío) marca todo como Available — cubre
|
||||
el boot sin que la UI quede vacía.
|
||||
- Render: `stat_card` "Timeline de matches" con count + 20 entries
|
||||
formateadas como `HH:MM:SS {+/-} consumer.flow ← producer.flow [via]`.
|
||||
Más reciente arriba.
|
||||
|
||||
Tests broker-explorer: 5 totales.
|
||||
- `diff_matches_first_snapshot_marks_everything_available`
|
||||
- `diff_matches_emits_lost_when_match_disappears`
|
||||
- `diff_matches_no_change_emits_nothing`
|
||||
- `pending_is_default_state_at_boot` (existente)
|
||||
- `poll_and_probe_constants_are_sane` (existente)
|
||||
|
||||
Decisión: timeline polled (cada `POLL_INTERVAL=5s`), no push.
|
||||
Razón: los `MatchEvent` push del broker son consumer-céntricos
|
||||
(cada session sólo ve sus propios matches). Para "system-wide
|
||||
timeline" haría falta una API broker-level "subscribe a todos" —
|
||||
mucho más scope. Polling cada 5s es suficiente para observabilidad.
|
||||
|
||||
### feat(brahman-handshake): ListSessions endpoint + cliente + UI broker-explorer
|
||||
Iter 20. Nuevo flujo end-to-end para observabilidad: cualquier
|
||||
módulo conectado puede preguntar al broker la lista de sesiones
|
||||
|
||||
Reference in New Issue
Block a user