huskies: merge 783

This commit is contained in:
dave
2026-04-28 11:17:40 +00:00
parent d70719e23c
commit 6c2bdde695
8 changed files with 1087 additions and 1069 deletions
@@ -0,0 +1,65 @@
//! Orphan detection: marks running agents whose backing task has exited.
use std::collections::HashMap;
use std::sync::Mutex;
use tokio::sync::broadcast;
use crate::agents::pool::StoryAgent;
use crate::agents::{AgentEvent, AgentStatus};
use crate::slog;
/// Scan the agent pool for Running entries whose backing tokio task has already
/// finished and mark them as Failed.
///
/// This handles the case where the PTY read loop or the spawned task exits
/// without updating the agent status — for example when the process is killed
/// externally and the PTY master fd returns EOF before our inactivity timeout
/// fires, but some other edge case prevents the normal cleanup path from running.
pub(super) fn check_orphaned_agents(agents: &Mutex<HashMap<String, StoryAgent>>) -> usize {
let mut lock = match agents.lock() {
Ok(l) => l,
Err(_) => return 0,
};
// Collect orphaned entries: Running or Pending agents whose task handle is finished.
// Pending agents can be orphaned if worktree creation panics before setting status.
let orphaned: Vec<(String, String, broadcast::Sender<AgentEvent>, AgentStatus)> = lock
.iter()
.filter_map(|(key, agent)| {
if matches!(agent.status, AgentStatus::Running | AgentStatus::Pending)
&& let Some(handle) = &agent.task_handle
&& handle.is_finished()
{
let story_id = key
.rsplit_once(':')
.map(|(s, _)| s.to_string())
.unwrap_or_else(|| key.clone());
return Some((
key.clone(),
story_id,
agent.tx.clone(),
agent.status.clone(),
));
}
None
})
.collect();
let count = orphaned.len();
for (key, story_id, tx, prev_status) in orphaned {
if let Some(agent) = lock.get_mut(&key) {
agent.status = AgentStatus::Failed;
slog!(
"[watchdog] Orphaned agent '{key}': task finished but status was {prev_status}. \
Marking Failed."
);
let _ = tx.send(AgentEvent::Error {
story_id,
agent_name: agent.agent_name.clone(),
message: "Agent process terminated unexpectedly (watchdog detected orphan)"
.to_string(),
});
}
}
count
}