huskies: merge 793
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
//! Legacy `report_completion` — retained for backwards compatibility and testing.
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::super::super::super::{AgentEvent, AgentStatus, CompletionReport};
|
||||
use super::super::super::{AgentPool, composite_key};
|
||||
|
||||
impl AgentPool {
|
||||
/// Internal: report that an agent has finished work on a story.
|
||||
///
|
||||
/// **Note:** This is no longer exposed as an MCP tool. The server now
|
||||
/// automatically runs completion gates when an agent process exits
|
||||
/// (see `run_server_owned_completion`). This method is retained for
|
||||
/// backwards compatibility and testing.
|
||||
///
|
||||
/// - Rejects with an error if the worktree has uncommitted changes.
|
||||
/// - Runs acceptance gates (cargo clippy + cargo nextest run / cargo test).
|
||||
/// - Stores the `CompletionReport` on the agent record.
|
||||
/// - Transitions status to `Completed` (gates passed) or `Failed` (gates failed).
|
||||
/// - Emits a `Done` event so `wait_for_agent` unblocks.
|
||||
#[allow(dead_code)]
|
||||
pub async fn report_completion(
|
||||
&self,
|
||||
story_id: &str,
|
||||
agent_name: &str,
|
||||
summary: &str,
|
||||
) -> Result<CompletionReport, String> {
|
||||
let key = composite_key(story_id, agent_name);
|
||||
|
||||
// Verify agent exists, is Running, and grab its worktree path.
|
||||
let worktree_path = {
|
||||
let agents = self.agents.lock().map_err(|e| e.to_string())?;
|
||||
let agent = agents
|
||||
.get(&key)
|
||||
.ok_or_else(|| format!("No agent '{agent_name}' for story '{story_id}'"))?;
|
||||
|
||||
if agent.status != AgentStatus::Running {
|
||||
return Err(format!(
|
||||
"Agent '{agent_name}' for story '{story_id}' is not running (status: {}). \
|
||||
report_completion can only be called by a running agent.",
|
||||
agent.status
|
||||
));
|
||||
}
|
||||
|
||||
agent
|
||||
.worktree_info
|
||||
.as_ref()
|
||||
.map(|wt| wt.path.clone())
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Agent '{agent_name}' for story '{story_id}' has no worktree. \
|
||||
Cannot run acceptance gates."
|
||||
)
|
||||
})?
|
||||
};
|
||||
|
||||
let path = worktree_path.clone();
|
||||
|
||||
// Run gate checks in a blocking thread to avoid stalling the async runtime.
|
||||
let (gates_passed, gate_output) = tokio::task::spawn_blocking(move || {
|
||||
// Step 1: Reject if worktree is dirty.
|
||||
crate::agents::gates::check_uncommitted_changes(&path)?;
|
||||
// Step 2: Run clippy + tests and return (passed, output).
|
||||
crate::agents::gates::run_acceptance_gates(&path)
|
||||
})
|
||||
.await
|
||||
.map_err(|e| format!("Gate check task panicked: {e}"))??;
|
||||
|
||||
let report = CompletionReport {
|
||||
summary: summary.to_string(),
|
||||
gates_passed,
|
||||
gate_output,
|
||||
};
|
||||
|
||||
// Extract data for pipeline advance, then remove the entry so
|
||||
// completed agents never appear in list_agents.
|
||||
let (
|
||||
tx,
|
||||
session_id,
|
||||
project_root_for_advance,
|
||||
wt_path_for_advance,
|
||||
merge_failure_reported_for_advance,
|
||||
session_id_for_advance,
|
||||
) = {
|
||||
let mut agents = self.agents.lock().map_err(|e| e.to_string())?;
|
||||
let agent = agents.get_mut(&key).ok_or_else(|| {
|
||||
format!("Agent '{agent_name}' for story '{story_id}' disappeared during gate check")
|
||||
})?;
|
||||
agent.completion = Some(report.clone());
|
||||
let tx = agent.tx.clone();
|
||||
let sid = agent.session_id.clone();
|
||||
let pr = agent.project_root.clone();
|
||||
let wt = agent.worktree_info.as_ref().map(|w| w.path.clone());
|
||||
let mfr = agent.merge_failure_reported;
|
||||
let sid_advance = agent.session_id.clone();
|
||||
agents.remove(&key);
|
||||
(tx, sid, pr, wt, mfr, sid_advance)
|
||||
};
|
||||
|
||||
// Emit Done so wait_for_agent unblocks.
|
||||
let _ = tx.send(AgentEvent::Done {
|
||||
story_id: story_id.to_string(),
|
||||
agent_name: agent_name.to_string(),
|
||||
session_id,
|
||||
});
|
||||
|
||||
// Notify WebSocket clients that the agent is gone.
|
||||
Self::notify_agent_state_changed(&self.watcher_tx);
|
||||
|
||||
// Advance the pipeline state machine in a background task.
|
||||
let pool_clone = Self {
|
||||
agents: Arc::clone(&self.agents),
|
||||
port: self.port,
|
||||
child_killers: Arc::clone(&self.child_killers),
|
||||
watcher_tx: self.watcher_tx.clone(),
|
||||
status_broadcaster: Arc::clone(&self.status_broadcaster),
|
||||
};
|
||||
let sid = story_id.to_string();
|
||||
let aname = agent_name.to_string();
|
||||
let report_for_advance = report.clone();
|
||||
tokio::spawn(async move {
|
||||
pool_clone
|
||||
.run_pipeline_advance(
|
||||
&sid,
|
||||
&aname,
|
||||
report_for_advance,
|
||||
project_root_for_advance,
|
||||
wt_path_for_advance,
|
||||
merge_failure_reported_for_advance,
|
||||
session_id_for_advance,
|
||||
)
|
||||
.await;
|
||||
});
|
||||
|
||||
Ok(report)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user