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
+8 -8
View File
@@ -46,7 +46,7 @@ pub(super) async fn tool_rebuild_and_restart(ctx: &AppContext) -> Result<String,
let project_root = ctx.state.get_project_root().unwrap_or_default();
let notifier = ctx.bot_shutdown.as_deref();
crate::rebuild::rebuild_and_restart(&ctx.agents, &project_root, notifier).await
crate::rebuild::rebuild_and_restart(&ctx.services.agents, &project_root, notifier).await
}
/// MCP tool called by Claude Code via `--permission-prompt-tool`.
@@ -84,7 +84,7 @@ pub(super) async fn tool_prompt_permission(
// Without this check, agent permission requests queue in the channel and
// get forwarded to Matrix/Slack/etc. at the start of the next user session,
// flooding chat with stale agent prompts.
if ctx.perm_rx.try_lock().is_ok() {
if ctx.services.perm_rx.try_lock().is_ok() {
crate::slog!(
"[permission] Auto-denied '{tool_name}' (no interactive session — agent mode)"
);
@@ -212,7 +212,7 @@ pub(super) fn tool_move_story(args: &Value, ctx: &AppContext) -> Result<String,
.and_then(|v| v.as_str())
.ok_or("Missing required argument: target_stage")?;
let project_root = ctx.agents.get_project_root(&ctx.state)?;
let project_root = ctx.services.agents.get_project_root(&ctx.state)?;
let (from_stage, to_stage) = move_story_to_stage(&project_root, story_id, target_stage)?;
@@ -273,7 +273,7 @@ pub(super) fn tool_get_version(ctx: &AppContext) -> Result<String, String> {
serde_json::to_string_pretty(&json!({
"version": env!("CARGO_PKG_VERSION"),
"build_hash": build_hash.trim(),
"port": ctx.agents.port(),
"port": ctx.services.agents.port(),
}))
.map_err(|e| format!("Serialization error: {e}"))
}
@@ -420,7 +420,7 @@ mod tests {
// then respond with approval. The try_lock() inside tool_prompt_permission
// must fail (lock held) so the request is forwarded rather than auto-denied.
let (ready_tx, ready_rx) = tokio::sync::oneshot::channel::<()>();
let perm_rx = ctx.perm_rx.clone();
let perm_rx = ctx.services.perm_rx.clone();
tokio::spawn(async move {
let mut rx = perm_rx.lock().await;
let _ = ready_tx.send(()); // signal: lock is held
@@ -459,7 +459,7 @@ mod tests {
// Simulate an interactive session: lock perm_rx first, then deny.
let (ready_tx, ready_rx) = tokio::sync::oneshot::channel::<()>();
let perm_rx = ctx.perm_rx.clone();
let perm_rx = ctx.services.perm_rx.clone();
tokio::spawn(async move {
let mut rx = perm_rx.lock().await;
let _ = ready_tx.send(()); // signal: lock is held
@@ -637,8 +637,8 @@ mod tests {
// then exec() will be called — which would replace our test process.
// So we only test that the function *runs* without panicking up to
// the agent-kill step. We do this by checking the pool is empty.
assert_eq!(ctx.agents.list_agents().unwrap().len(), 0);
ctx.agents.kill_all_children(); // should not panic on empty pool
assert_eq!(ctx.services.agents.list_agents().unwrap().len(), 0);
ctx.services.agents.kill_all_children(); // should not panic on empty pool
}
#[test]