diff --git a/.story_kit/work/2_current/72_bug_story_creation_does_not_quote_yaml_special_characters_in_name.md b/.story_kit/work/2_current/72_bug_story_creation_does_not_quote_yaml_special_characters_in_name.md new file mode 100644 index 0000000..470daea --- /dev/null +++ b/.story_kit/work/2_current/72_bug_story_creation_does_not_quote_yaml_special_characters_in_name.md @@ -0,0 +1,23 @@ +# Bug 72: Story creation does not quote YAML special characters in name + +## Description + +The create_story MCP tool writes the name value into YAML front matter without quoting. If the name contains YAML-special characters like colons, the resulting front matter is invalid YAML and fails to parse. + +## How to Reproduce + +1. Call create_story with a name containing a colon, e.g. "Server-owned agent completion: remove report_completion dependency" +2. Open the generated .md file +3. Observe the front matter parser rejects it + +## Actual Result + +Invalid front matter: mapping values are not allowed in this context + +## Expected Result + +The name value should be quoted in the front matter so special characters are safe, e.g. name: "My story: with colons" + +## Acceptance Criteria + +- [ ] Bug is fixed and verified diff --git a/server/src/http/workflow.rs b/server/src/http/workflow.rs index b2f0323..a6a625c 100644 --- a/server/src/http/workflow.rs +++ b/server/src/http/workflow.rs @@ -164,7 +164,7 @@ pub fn create_story_file( let mut content = String::new(); content.push_str("---\n"); - content.push_str(&format!("name: {name}\n")); + content.push_str(&format!("name: \"{}\"\n", name.replace('"', "\\\""))); content.push_str("test_plan: pending\n"); content.push_str("---\n\n"); content.push_str(&format!("# Story {story_number}: {name}\n\n")); @@ -917,7 +917,7 @@ mod tests { let mut content = String::new(); content.push_str("---\n"); - content.push_str("name: My New Feature\n"); + content.push_str("name: \"My New Feature\"\n"); content.push_str("test_plan: pending\n"); content.push_str("---\n\n"); content.push_str(&format!("# Story {number}: My New Feature\n\n")); @@ -932,13 +932,29 @@ mod tests { fs::write(&filepath, &content).unwrap(); let written = fs::read_to_string(&filepath).unwrap(); - assert!(written.starts_with("---\nname: My New Feature\ntest_plan: pending\n---")); + assert!(written.starts_with("---\nname: \"My New Feature\"\ntest_plan: pending\n---")); assert!(written.contains("# Story 37: My New Feature")); assert!(written.contains("- [ ] It works")); assert!(written.contains("- [ ] It is tested")); assert!(written.contains("## Out of Scope")); } + #[test] + fn create_story_with_colon_in_name_produces_valid_yaml() { + let tmp = tempfile::tempdir().unwrap(); + let name = "Server-owned agent completion: remove report_completion dependency"; + let result = create_story_file(tmp.path(), name, None, None, false); + assert!(result.is_ok(), "create_story_file failed: {result:?}"); + + let upcoming = tmp.path().join(".story_kit/work/1_upcoming"); + let story_id = result.unwrap(); + let filename = format!("{story_id}.md"); + let contents = fs::read_to_string(upcoming.join(&filename)).unwrap(); + + let meta = parse_front_matter(&contents).expect("front matter should be valid YAML"); + assert_eq!(meta.name.as_deref(), Some(name)); + } + #[test] fn create_story_rejects_duplicate() { let tmp = tempfile::tempdir().unwrap();