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:
@@ -197,6 +197,52 @@ pub fn list_sessions_blocking(
|
||||
rt.block_on(list_sessions(label))
|
||||
}
|
||||
|
||||
/// Análogo a `list_sessions` pero pide los matches activos del
|
||||
/// broker. La Card observer es la misma forma minimalista (sin
|
||||
/// flow.input/output) — el endpoint no requiere participar en
|
||||
/// matching.
|
||||
pub async fn list_matches(
|
||||
observer_label: impl Into<String>,
|
||||
) -> Result<brahman_handshake::messages::MatchList, ConsumerError> {
|
||||
let init_path = transport::default_socket_path();
|
||||
let card = Card {
|
||||
payload: Payload::Virtual,
|
||||
supervision: Supervision::OneShot,
|
||||
lifecycle: Lifecycle::Oneshot,
|
||||
priority: Priority::Normal,
|
||||
kind: CardKind::Ente,
|
||||
flow: Flows {
|
||||
input: vec![],
|
||||
output: vec![],
|
||||
},
|
||||
..Card::new(observer_label)
|
||||
};
|
||||
|
||||
let mut client = Client::connect(&init_path, card)
|
||||
.await
|
||||
.map_err(|source| ConsumerError::Connect {
|
||||
socket: init_path.clone(),
|
||||
source,
|
||||
})?;
|
||||
|
||||
let list = client.list_matches().await?;
|
||||
let _ = client.farewell().await;
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
/// Wrapper bloqueante de [`list_matches`].
|
||||
pub fn list_matches_blocking(
|
||||
observer_label: impl Into<String>,
|
||||
) -> Result<brahman_handshake::messages::MatchList, ConsumerError> {
|
||||
let label = observer_label.into();
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_io()
|
||||
.enable_time()
|
||||
.build()
|
||||
.map_err(|e| ConsumerError::Runtime(e.to_string()))?;
|
||||
rt.block_on(list_matches(label))
|
||||
}
|
||||
|
||||
fn describe_first_input(card: &Card) -> (String, String) {
|
||||
match card.flow.input.first() {
|
||||
Some(flow) => {
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
pub mod discovery;
|
||||
pub use discovery::{
|
||||
await_provider, await_provider_blocking, build_consumer_card, list_sessions,
|
||||
list_sessions_blocking, ConsumerError,
|
||||
await_provider, await_provider_blocking, build_consumer_card, list_matches,
|
||||
list_matches_blocking, list_sessions, list_sessions_blocking, ConsumerError,
|
||||
};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
Reference in New Issue
Block a user