Module wave_obj_bridge

Module wave_obj_bridge 

Source
Expand description

WaveObjUpdate broadcast bridge.

Subscribes to srv_events_tx (the internal sidecar event bus that the reducer publishes mutations to) and translates each event into one or more WaveObjUpdate records, broadcast to all connected WS clients via the existing event_bus.broadcast_event(...) plumbing — the same path that service.rs:39-52’s response-broadcast loop uses.

Why this exists: per-RPC handlers were responsible for attaching WaveObjUpdates to their responses (success_with_updates(...)). Forgetting that call left the frontend WOS cache stale (e.g. workspace renames not propagating to the OS title or the InstancePanel — see docs/specs/SPEC_REACTIVE_WORKSPACE_SYNC_2026-05-14.md).

With this bridge in place, any reducer event automatically reaches the frontend, so the per-handler convention becomes belt-and-suspenders instead of load-bearing.

Spec: docs/specs/SPEC_OBJ_UPDATE_BRIDGE_2026-05-14.md.

Phase 1 scope (this implementation): workspace events only — immediately fixes the user-reported bug. Phase 2 expands to tabs / blocks / windows / layouts; Phase 3 retires the per-handler success_with_updates(...) calls now that the bridge covers them.

Functions§

build_update_payload 🔒
JSON shape that gets broadcast as the data payload of a waveobj:update WS event. Matches the shape of WaveObjUpdate in agentmux-srv/src/backend/obj.rs:465-474 so the frontend’s existing updateWaveObject handler accepts it without changes.
dispatch_event 🔒
Translate one reducer event into zero or more waveobj:update broadcasts.
emit 🔒
Push one WaveObjUpdate payload to all connected WS clients via the shared event_bus. Mirrors the response-broadcast loop in service.rs:39-52.
emit_client_singleton 🔒
Broadcast the singleton Client WaveObj. SrvWindowOpened / SrvWindowClosed mutate Client.windowids (per apply_srv_window_opened in persist_subscriber.rs:518) so renderers holding a pinned Client need to see the new windowids list — without this broadcast they’d render stale window membership until reload. Codex P2 on PR #861.
emit_delete 🔒
Broadcast a “delete” waveobj:update for the given oid. No fetch needed — the frontend’s updateWaveObject (wos.ts:263-265) handles the delete arm with just the oid.
emit_fetched 🔒
Fetch one WaveObj by oid and broadcast it as a waveobj:update. The SQLite read is offloaded to the blocking thread pool per ReAgent P1 on PR #852 (WaveStore is std::sync::Mutex<Connection>; brief in steady state but a long reducer transaction would block the tokio worker thread). Silently logs + skips on missing/error to satisfy the §8.15 idempotency contract — duplicate or stale events fold to no-op.
emit_layout_for_tab 🔒
Layout events all reference a tab_id; the affected WaveObj is the LayoutState referenced by the tab’s layoutstate field. Two SQLite reads chained inside one spawn_blocking to keep the lock hold short.
run_wave_obj_bridge 🔒
spawn_wave_obj_bridge
Spawn the bridge task. Returns the JoinHandle so callers can keep it alive (typically forever — the task lives for the lifetime of the srv process). Per ReAgent P1 on PR #852: the loop is panic-resilient — a panic inside dispatch_event is caught and logged, and the loop continues processing subsequent events. Without this, a single malformed event could silently kill the entire bridge task and frontend WOS would stop seeing updates.