From 144f07f412a2425f7b351d1a709050005acfab68 Mon Sep 17 00:00:00 2001 From: dave Date: Mon, 27 Apr 2026 11:18:41 +0000 Subject: [PATCH] huskies: merge 644_story_chat_transport_consumers_slack_discord_whatsapp_matrix_for_the_unified_status_broadcaster --- server/src/chat/transport/matrix/bot/run.rs | 36 +++++++++++++++++++++ server/src/config.rs | 15 +++++++++ server/src/worktree.rs | 12 +++++++ 3 files changed, 63 insertions(+) diff --git a/server/src/chat/transport/matrix/bot/run.rs b/server/src/chat/transport/matrix/bot/run.rs index df8a5610..b88ecc94 100644 --- a/server/src/chat/transport/matrix/bot/run.rs +++ b/server/src/chat/transport/matrix/bot/run.rs @@ -233,6 +233,42 @@ pub async fn run_bot( watcher_rx_auto, ); + // Subscribe to the status broadcaster if the matrix_status_consumer toggle is + // enabled (default: true). The subscriber formats each StatusEvent via the + // common formatter and sends the resulting text to all configured Matrix rooms. + // The task exits automatically when the broadcaster is dropped (channel closed) + // on bot shutdown. + { + use crate::config::ProjectConfig; + use crate::service::status::format::format_status_event; + + let status_enabled = ProjectConfig::load(project_root) + .map(|c| c.matrix_status_consumer) + .unwrap_or(true); + + if status_enabled { + let mut sub = services.status.subscribe(); + let status_transport = Arc::clone(&transport); + let status_rooms: Vec = + announce_room_ids.iter().map(|r| r.to_string()).collect(); + tokio::spawn(async move { + while let Some(event) = sub.recv().await { + let plain = format_status_event(&event); + let html = markdown_to_html(&plain); + for room_id in &status_rooms { + if let Err(e) = status_transport.send_message(room_id, &plain, &html).await + { + crate::slog!( + "[matrix-bot] Failed to send status event to {room_id}: {e}" + ); + } + } + } + crate::slog!("[matrix-bot] Status subscriber task exiting — broadcaster dropped"); + }); + } + } + let ctx = BotContext { services, matrix_user_id: bot_user_id, diff --git a/server/src/config.rs b/server/src/config.rs index ce6008d7..c8df85df 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -49,6 +49,13 @@ pub struct ProjectConfig { /// Default: `true`. #[serde(default = "default_web_ui_status_consumer")] pub web_ui_status_consumer: bool, + /// Whether the Matrix bot subscribes to the status broadcaster and forwards + /// pipeline events to its configured rooms. + /// Set to `false` to silence Matrix status notifications without affecting + /// other consumers (web UI, Slack, Discord, WhatsApp, agent context). + /// Default: `true`. + #[serde(default = "default_matrix_status_consumer")] + pub matrix_status_consumer: bool, /// IANA timezone name (e.g. `"Europe/London"`, `"America/New_York"`). /// When set, timer HH:MM inputs are interpreted in this timezone instead /// of the container/host local time. Falls back to `chrono::Local` when absent. @@ -133,6 +140,10 @@ fn default_web_ui_status_consumer() -> bool { true } +fn default_matrix_status_consumer() -> bool { + true +} + fn default_max_mesh_peers() -> usize { 3 } @@ -263,6 +274,7 @@ impl Default for ProjectConfig { base_branch: None, rate_limit_notifications: default_rate_limit_notifications(), web_ui_status_consumer: default_web_ui_status_consumer(), + matrix_status_consumer: default_matrix_status_consumer(), timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -345,6 +357,7 @@ impl ProjectConfig { base_branch: legacy.base_branch, rate_limit_notifications: legacy.rate_limit_notifications, web_ui_status_consumer: default_web_ui_status_consumer(), + matrix_status_consumer: default_matrix_status_consumer(), timezone: legacy.timezone, rendezvous: None, trusted_keys: Vec::new(), @@ -378,6 +391,7 @@ impl ProjectConfig { base_branch: legacy.base_branch, rate_limit_notifications: legacy.rate_limit_notifications, web_ui_status_consumer: default_web_ui_status_consumer(), + matrix_status_consumer: default_matrix_status_consumer(), timezone: legacy.timezone, rendezvous: None, trusted_keys: Vec::new(), @@ -399,6 +413,7 @@ impl ProjectConfig { base_branch: legacy.base_branch, rate_limit_notifications: legacy.rate_limit_notifications, web_ui_status_consumer: default_web_ui_status_consumer(), + matrix_status_consumer: default_matrix_status_consumer(), timezone: legacy.timezone, rendezvous: None, trusted_keys: Vec::new(), diff --git a/server/src/worktree.rs b/server/src/worktree.rs index dcc110e8..c2cbdfdd 100644 --- a/server/src/worktree.rs +++ b/server/src/worktree.rs @@ -529,6 +529,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -559,6 +560,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -589,6 +591,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -619,6 +622,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -648,6 +652,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -684,6 +689,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -761,6 +767,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -796,6 +803,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -878,6 +886,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -916,6 +925,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -944,6 +954,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(), @@ -978,6 +989,7 @@ mod tests { base_branch: None, rate_limit_notifications: true, web_ui_status_consumer: true, + matrix_status_consumer: true, timezone: None, rendezvous: None, trusted_keys: Vec::new(),