huskies: merge 626_refactor_introduce_services_bundle_and_migrate_appcontext_matrix_transport

This commit is contained in:
dave
2026-04-25 15:04:37 +00:00
parent aeff0b55be
commit 4b089c1ed8
21 changed files with 403 additions and 339 deletions
+16 -7
View File
@@ -1,8 +1,9 @@
//! Application context — shared state (`AppContext`) threaded through all HTTP handlers.
use crate::agents::{AgentPool, ReconciliationEvent};
use crate::agents::ReconciliationEvent;
use crate::io::watcher::WatcherEvent;
use crate::rebuild::{BotShutdownNotifier, ShutdownReason};
use crate::service::timer::TimerStore;
use crate::services::Services;
use crate::state::SessionState;
use crate::store::JsonFileStore;
use crate::workflow::WorkflowState;
@@ -64,7 +65,8 @@ pub struct AppContext {
pub state: Arc<SessionState>,
pub store: Arc<JsonFileStore>,
pub workflow: Arc<std::sync::Mutex<WorkflowState>>,
pub agents: Arc<AgentPool>,
/// Shared services bundle (agent pool, bot identity, permissions, etc.).
pub services: Arc<Services>,
/// Broadcast channel for filesystem watcher events. WebSocket handlers
/// subscribe to this to push lifecycle notifications to connected clients.
pub watcher_tx: broadcast::Sender<WatcherEvent>,
@@ -76,9 +78,6 @@ pub struct AppContext {
/// `prompt_permission` tool. The MCP handler sends a [`PermissionForward`]
/// and awaits the oneshot response.
pub perm_tx: mpsc::UnboundedSender<PermissionForward>,
/// Receiver for permission requests. The active WebSocket handler locks
/// this and polls for incoming permission forwards.
pub perm_rx: Arc<tokio::sync::Mutex<mpsc::UnboundedReceiver<PermissionForward>>>,
/// Child process of the QA app launched for manual testing.
/// Only one instance runs at a time.
pub qa_app_process: Arc<std::sync::Mutex<Option<std::process::Child>>>,
@@ -110,6 +109,7 @@ pub struct AppContext {
#[cfg(test)]
impl AppContext {
pub fn new_test(project_root: std::path::PathBuf) -> Self {
use crate::agents::AgentPool;
let state = SessionState::default();
*state.project_root.lock().unwrap() = Some(project_root.clone());
let store_path = project_root.join(".huskies_store.json");
@@ -119,15 +119,24 @@ impl AppContext {
let timer_store = Arc::new(TimerStore::load(
project_root.join(".huskies").join("timers.json"),
));
let services = Arc::new(Services {
project_root: project_root.clone(),
agents: Arc::new(AgentPool::new(3001, watcher_tx.clone())),
bot_name: "Assistant".to_string(),
bot_user_id: String::new(),
ambient_rooms: Arc::new(std::sync::Mutex::new(std::collections::HashSet::new())),
perm_rx: Arc::new(tokio::sync::Mutex::new(perm_rx)),
pending_perm_replies: Arc::new(tokio::sync::Mutex::new(HashMap::new())),
permission_timeout_secs: 120,
});
Self {
state: Arc::new(state),
store: Arc::new(JsonFileStore::new(store_path).unwrap()),
workflow: Arc::new(std::sync::Mutex::new(WorkflowState::default())),
agents: Arc::new(AgentPool::new(3001, watcher_tx.clone())),
services,
watcher_tx,
reconciliation_tx,
perm_tx,
perm_rx: Arc::new(tokio::sync::Mutex::new(perm_rx)),
qa_app_process: Arc::new(std::sync::Mutex::new(None)),
bot_shutdown: None,
matrix_shutdown_tx: None,