From 8482df2f4ea5593856f42bb8cbea15408d804b45 Mon Sep 17 00:00:00 2001 From: dave Date: Tue, 14 Apr 2026 16:11:34 +0000 Subject: [PATCH] huskies: merge 570_bug_merge_agent_work_should_check_if_story_is_already_done_before_attempting_merge --- server/src/http/mcp/merge_tools.rs | 71 ++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/server/src/http/mcp/merge_tools.rs b/server/src/http/mcp/merge_tools.rs index c26b488e..cf84daea 100644 --- a/server/src/http/mcp/merge_tools.rs +++ b/server/src/http/mcp/merge_tools.rs @@ -15,6 +15,23 @@ pub(super) async fn tool_merge_agent_work( .and_then(|v| v.as_str()) .ok_or("Missing required argument: story_id")?; + // Check CRDT stage before attempting merge — if already done or archived, + // return success immediately to avoid spurious error notifications. + if let Some(item) = crate::crdt_state::read_item(story_id) + && (item.stage == "5_done" || item.stage == "6_archived") + { + return serde_json::to_string_pretty(&json!({ + "story_id": story_id, + "status": "completed", + "success": true, + "message": format!( + "Story '{}' is already in '{}' — no merge needed.", + story_id, item.stage + ), + })) + .map_err(|e| format!("Serialization error: {e}")); + } + let project_root = ctx.agents.get_project_root(&ctx.state)?; ctx.agents.start_merge_agent_work(&project_root, story_id)?; @@ -258,6 +275,60 @@ mod tests { assert!(result.unwrap_err().contains("story_id")); } + #[tokio::test] + async fn tool_merge_agent_work_already_done_returns_success() { + crate::crdt_state::init_for_test(); + crate::crdt_state::write_item( + "99_story_already_done", + "5_done", + Some("Already done story"), + None, + None, + None, + None, + None, + None, + None, + ); + let tmp = tempfile::tempdir().unwrap(); + let ctx = test_ctx(tmp.path()); + let result = + tool_merge_agent_work(&json!({"story_id": "99_story_already_done"}), &ctx).await; + assert!(result.is_ok(), "expected Ok, got: {result:?}"); + let body = result.unwrap(); + let v: serde_json::Value = serde_json::from_str(&body).unwrap(); + assert_eq!(v["status"], "completed"); + assert_eq!(v["success"], true); + assert!(v["message"].as_str().unwrap().contains("5_done")); + } + + #[tokio::test] + async fn tool_merge_agent_work_already_archived_returns_success() { + crate::crdt_state::init_for_test(); + crate::crdt_state::write_item( + "98_story_already_archived", + "6_archived", + Some("Already archived story"), + None, + None, + None, + None, + None, + None, + None, + ); + let tmp = tempfile::tempdir().unwrap(); + let ctx = test_ctx(tmp.path()); + let result = + tool_merge_agent_work(&json!({"story_id": "98_story_already_archived"}), &ctx).await; + assert!(result.is_ok(), "expected Ok, got: {result:?}"); + let body = result.unwrap(); + let v: serde_json::Value = serde_json::from_str(&body).unwrap(); + assert_eq!(v["status"], "completed"); + assert_eq!(v["success"], true); + assert!(v["message"].as_str().unwrap().contains("6_archived")); + } + #[tokio::test] async fn tool_move_story_to_merge_missing_story_id() { let tmp = tempfile::tempdir().unwrap();