Remove test_plan gate from the codebase

The test_plan field was a gate from the old interactive web UI workflow
where a human would approve a test plan before the LLM could write code.
With autonomous coder agents, this gate is dead weight — coders sometimes
obey the README's "wait for approval" instruction and produce no code.

Removes: TestPlanStatus enum, ensure_test_plan_approved checks in fs/shell,
set_test_plan MCP tool + handler, test_plan from story/bug front matter
creation, test_plan validation in validate_story_dirs, and all related tests.
Updates README to remove Step 2 (Test Planning) and renumber steps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dave
2026-02-23 19:12:05 +00:00
parent cc2511b792
commit 31037f5bf5
7 changed files with 23 additions and 363 deletions

View File

@@ -4,7 +4,7 @@ use crate::http::context::AppContext;
use crate::http::settings::get_editor_command_from_store;
use crate::http::workflow::{
check_criterion_in_file, create_bug_file, create_story_file, list_bug_files,
load_upcoming_stories, set_test_plan_in_file, validate_story_dirs,
load_upcoming_stories, validate_story_dirs,
};
use crate::worktree;
use crate::io::story_metadata::{parse_front_matter, parse_unchecked_todos};
@@ -614,24 +614,6 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
"required": ["story_id", "criterion_index"]
}
},
{
"name": "set_test_plan",
"description": "Update the test_plan front-matter field of a story file and auto-commit to master. Common values: 'pending', 'approved'.",
"inputSchema": {
"type": "object",
"properties": {
"story_id": {
"type": "string",
"description": "Story identifier (filename stem, e.g. '28_my_story')"
},
"status": {
"type": "string",
"description": "New value for the test_plan field (e.g. 'approved', 'pending')"
}
},
"required": ["story_id", "status"]
}
},
{
"name": "create_bug",
"description": "Create a bug file in .story_kit/bugs/ with a deterministic filename and auto-commit to master. Returns the bug_id.",
@@ -787,7 +769,6 @@ async fn handle_tools_call(
"accept_story" => tool_accept_story(&args, ctx),
// Story mutation tools (auto-commit to master)
"check_criterion" => tool_check_criterion(&args, ctx),
"set_test_plan" => tool_set_test_plan(&args, ctx),
// Bug lifecycle tools
"create_bug" => tool_create_bug(&args, ctx),
"list_bugs" => tool_list_bugs(ctx),
@@ -1193,24 +1174,6 @@ fn tool_check_criterion(args: &Value, ctx: &AppContext) -> Result<String, String
))
}
fn tool_set_test_plan(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 status = args
.get("status")
.and_then(|v| v.as_str())
.ok_or("Missing required argument: status")?;
let root = ctx.state.get_project_root()?;
set_test_plan_in_file(&root, story_id, status)?;
Ok(format!(
"test_plan set to '{status}' for story '{story_id}'. Committed to master."
))
}
// ── Bug lifecycle tool implementations ───────────────────────────
fn tool_create_bug(args: &Value, ctx: &AppContext) -> Result<String, String> {
@@ -1536,14 +1499,13 @@ mod tests {
assert!(!names.contains(&"report_completion"));
assert!(names.contains(&"accept_story"));
assert!(names.contains(&"check_criterion"));
assert!(names.contains(&"set_test_plan"));
assert!(names.contains(&"create_bug"));
assert!(names.contains(&"list_bugs"));
assert!(names.contains(&"close_bug"));
assert!(names.contains(&"merge_agent_work"));
assert!(names.contains(&"move_story_to_merge"));
assert!(names.contains(&"request_qa"));
assert_eq!(tools.len(), 26);
assert_eq!(tools.len(), 25);
}
#[test]
@@ -1626,7 +1588,7 @@ mod tests {
fs::create_dir_all(&current_dir).unwrap();
fs::write(
current_dir.join("1_test.md"),
"---\nname: Test\ntest_plan: approved\n---\n## AC\n- [ ] First\n- [x] Done\n- [ ] Second\n",
"---\nname: Test\n---\n## AC\n- [ ] First\n- [x] Done\n- [ ] Second\n",
)
.unwrap();
@@ -2105,7 +2067,7 @@ mod tests {
let current_dir = tmp.path().join(".story_kit/work/2_current");
std::fs::create_dir_all(&current_dir).unwrap();
let story_file = current_dir.join("24_story_test.md");
std::fs::write(&story_file, "---\nname: Test\ntest_plan: approved\n---\n").unwrap();
std::fs::write(&story_file, "---\nname: Test\n---\n").unwrap();
std::process::Command::new("git")
.args(["add", "."])
.current_dir(tmp.path())