Accept story 45: Deterministic Story Lifecycle Management

- accept_story MCP tool moves current/ to archived/
- move_story_to_archived helper with idempotent behavior
- start_agent auto-moves upcoming/ to current/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dave
2026-02-20 15:09:39 +00:00
parent 42640fba6a
commit 5c164f4855
3 changed files with 61 additions and 1 deletions

View File

@@ -1,3 +1,4 @@
use crate::agents::move_story_to_archived;
use crate::config::ProjectConfig;
use crate::http::context::AppContext;
use crate::http::settings::get_editor_command_from_store;
@@ -599,6 +600,20 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
},
"required": ["story_id", "agent_name", "summary"]
}
},
{
"name": "accept_story",
"description": "Accept a story: moves it from current/ to archived/.",
"inputSchema": {
"type": "object",
"properties": {
"story_id": {
"type": "string",
"description": "Story identifier (filename stem, e.g. '28_my_story')"
}
},
"required": ["story_id"]
}
}
]
}),
@@ -642,6 +657,8 @@ async fn handle_tools_call(
"get_editor_command" => tool_get_editor_command(&args, ctx),
// Completion reporting
"report_completion" => tool_report_completion(&args, ctx).await,
// Lifecycle tools
"accept_story" => tool_accept_story(&args, ctx),
_ => Err(format!("Unknown tool: {tool_name}")),
};
@@ -1041,6 +1058,18 @@ async fn tool_report_completion(args: &Value, ctx: &AppContext) -> Result<String
.map_err(|e| format!("Serialization error: {e}"))
}
fn tool_accept_story(args: &Value, ctx: &AppContext) -> Result<String, String> {
let story_id = args
.get("story_id")
.and_then(|v| v.as_str())
.ok_or("Missing required argument: story_id")?;
let project_root = ctx.agents.get_project_root(&ctx.state)?;
move_story_to_archived(&project_root, story_id)?;
Ok(format!("Story '{story_id}' accepted and moved to archived/."))
}
/// Run `git log <base>..HEAD --oneline` in the worktree and return the commit
/// summaries, or `None` if git is unavailable or there are no new commits.
async fn get_worktree_commits(worktree_path: &str, base_branch: &str) -> Option<Vec<String>> {
@@ -1192,7 +1221,8 @@ mod tests {
assert!(names.contains(&"remove_worktree"));
assert!(names.contains(&"get_editor_command"));
assert!(names.contains(&"report_completion"));
assert_eq!(tools.len(), 18);
assert!(names.contains(&"accept_story"));
assert_eq!(tools.len(), 19);
}
#[test]