huskies: merge 560_story_make_merge_agent_work_return_results_like_run_tests_instead_of_polling
This commit is contained in:
@@ -6,7 +6,10 @@ use crate::slog;
|
||||
use crate::slog_warn;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
pub(super) fn tool_merge_agent_work(args: &Value, ctx: &AppContext) -> Result<String, String> {
|
||||
pub(super) async fn tool_merge_agent_work(
|
||||
args: &Value,
|
||||
ctx: &AppContext,
|
||||
) -> Result<String, String> {
|
||||
let story_id = args
|
||||
.get("story_id")
|
||||
.and_then(|v| v.as_str())
|
||||
@@ -16,53 +19,26 @@ pub(super) fn tool_merge_agent_work(args: &Value, ctx: &AppContext) -> Result<St
|
||||
ctx.agents.start_merge_agent_work(&project_root, story_id)?;
|
||||
|
||||
// Block until the merge completes instead of returning immediately.
|
||||
// Uses tokio::time::sleep so the async executor is not blocked.
|
||||
// This prevents the mergemaster from burning all its turns polling
|
||||
// get_merge_status in a tight loop.
|
||||
let sid = story_id.to_string();
|
||||
let agents = ctx.agents.clone();
|
||||
loop {
|
||||
std::thread::sleep(std::time::Duration::from_secs(10));
|
||||
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
||||
if let Some(job) = agents.get_merge_status(&sid) {
|
||||
match &job.status {
|
||||
crate::agents::merge::MergeJobStatus::Running => continue,
|
||||
_ => return tool_get_merge_status_inner(&sid, &job),
|
||||
_ => break,
|
||||
}
|
||||
} else {
|
||||
return Err(format!("Merge job disappeared for '{sid}'."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tool_get_merge_status_inner(
|
||||
story_id: &str,
|
||||
job: &crate::agents::merge::MergeJob,
|
||||
) -> Result<String, String> {
|
||||
match &job.status {
|
||||
crate::agents::merge::MergeJobStatus::Running => serde_json::to_string_pretty(&json!({
|
||||
"story_id": story_id,
|
||||
"status": "running",
|
||||
"message": "Merge pipeline is still running."
|
||||
}))
|
||||
.map_err(|e| format!("Serialization error: {e}")),
|
||||
crate::agents::merge::MergeJobStatus::Completed(report) => {
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"story_id": story_id,
|
||||
"status": "completed",
|
||||
"success": report.success,
|
||||
"had_conflicts": report.had_conflicts,
|
||||
"conflicts_resolved": report.conflicts_resolved,
|
||||
"gates_passed": report.gates_passed,
|
||||
"gate_output": report.gate_output,
|
||||
}))
|
||||
.map_err(|e| format!("Serialization error: {e}"))
|
||||
}
|
||||
crate::agents::merge::MergeJobStatus::Failed(err) => serde_json::to_string_pretty(&json!({
|
||||
"story_id": story_id,
|
||||
"status": "failed",
|
||||
"error": err,
|
||||
}))
|
||||
.map_err(|e| format!("Serialization error: {e}")),
|
||||
}
|
||||
// Return the full result (same fields as get_merge_status) so the caller
|
||||
// has everything it needs without a second round-trip.
|
||||
tool_get_merge_status(args, ctx)
|
||||
}
|
||||
|
||||
pub(super) fn tool_get_merge_status(args: &Value, ctx: &AppContext) -> Result<String, String> {
|
||||
@@ -80,7 +56,7 @@ pub(super) fn tool_get_merge_status(args: &Value, ctx: &AppContext) -> Result<St
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"story_id": story_id,
|
||||
"status": "running",
|
||||
"message": "Merge pipeline is still running. Poll again in 10-15 seconds."
|
||||
"message": "Merge pipeline is still running."
|
||||
}))
|
||||
.map_err(|e| format!("Serialization error: {e}"))
|
||||
}
|
||||
@@ -273,11 +249,11 @@ mod tests {
|
||||
assert!(!req_names.contains(&"agent_name"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_merge_agent_work_missing_story_id() {
|
||||
#[tokio::test]
|
||||
async fn tool_merge_agent_work_missing_story_id() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let ctx = test_ctx(tmp.path());
|
||||
let result = tool_merge_agent_work(&json!({}), &ctx);
|
||||
let result = tool_merge_agent_work(&json!({}), &ctx).await;
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("story_id"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user