feat(maestro): daemon WS /api/events for live catalog updates (NAP-248)
ci / rust (push) Failing after 7m28s

New module `local_events::LocalEventBus` (broadcast<RegistryEvent>) with
two publishers:

* `spawn_session_publisher` — translates SessionManager's own session
  events into the matching `RegistryEvent` variants (with daemon_id
  filled in for the SessionRemoved case).
* `CatalogMirror::apply` — re-publishes peer events after applying them
  to the local mirror, so subscribers see the post-apply state.

New `WS /api/events` route on the daemon: subscribes to the bus, sends
an initial `Snapshot` built from manager.snapshot() ∪ catalog state,
then forwards live deltas. `Lagged` follows the registry's convention
(client resyncs via REST).

Revert the REST-seed hack in web/src/events.ts — the WS endpoint now
exists, so `CatalogStore::start()` can go back to just `connect()`.

E2E: subscribe to a daemon's events WS, expect Snapshot then a
SessionAdded after POSTing /api/sessions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 22:01:08 +00:00
co-authored by Claude Opus 4.7
parent 54b5763871
commit a671e15114
9 changed files with 369 additions and 37 deletions
+9 -2
View File
@@ -11,7 +11,7 @@ use std::time::Duration;
use common::DaemonFixture;
use futures_util::{SinkExt, StreamExt};
use maestro_daemon::{local_api, BrokerHandle, CatalogMirror};
use maestro_daemon::{local_api, BrokerHandle, CatalogMirror, LocalEventBus};
use maestro_protocol::{CreateSessionRequest, DaemonInfo, RegistryEvent, Session};
use tokio::net::TcpListener;
use tokio::sync::oneshot;
@@ -33,7 +33,14 @@ async fn spawn(daemon_id: &str, hostname: &str, daemon_url: &str) -> Harness {
let fx = DaemonFixture::new(daemon_id);
let manager = fx.manager.clone();
let catalog = CatalogMirror::new();
let state = local_api::state_for(manager.clone(), hostname, daemon_url, catalog.clone());
let events = LocalEventBus::new();
let state = local_api::state_for(
manager.clone(),
hostname,
daemon_url,
catalog.clone(),
events,
);
// NAP-247: tests don't exercise the cross-host broker path; hand the
// router an isolated BrokerHandle whose receiver is dropped, so any
// resolve_attach attempt fails fast with NoControlLink.