huskies: merge 492_story_remove_filesystem_pipeline_state_and_store_story_content_in_database
This commit is contained in:
@@ -131,19 +131,6 @@ fn build_metadata(front: FrontMatter) -> StoryMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
/// Write or update a `coverage_baseline:` field in the YAML front matter of a story file.
|
||||
///
|
||||
/// If front matter is present, adds or replaces `coverage_baseline:` before the closing `---`.
|
||||
/// If no front matter is present, this is a no-op (returns Ok).
|
||||
pub fn write_coverage_baseline(path: &Path, coverage_pct: f64) -> Result<(), String> {
|
||||
let contents =
|
||||
fs::read_to_string(path).map_err(|e| format!("Failed to read story file: {e}"))?;
|
||||
|
||||
let updated = set_front_matter_field(&contents, "coverage_baseline", &format!("{coverage_pct:.1}%"));
|
||||
fs::write(path, &updated).map_err(|e| format!("Failed to write story file: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write or update a `merge_failure:` field in the YAML front matter of a story file.
|
||||
///
|
||||
/// The reason is stored as a quoted YAML string so that colons, hashes, and newlines
|
||||
@@ -254,25 +241,6 @@ pub fn set_front_matter_field(contents: &str, key: &str, value: &str) -> String
|
||||
result
|
||||
}
|
||||
|
||||
/// Increment the `retry_count` field in the story file's front matter.
|
||||
///
|
||||
/// Reads the current value (defaulting to 0), increments by 1, and writes back.
|
||||
/// Returns the new retry count.
|
||||
pub fn increment_retry_count(path: &Path) -> Result<u32, String> {
|
||||
let contents =
|
||||
fs::read_to_string(path).map_err(|e| format!("Failed to read story file: {e}"))?;
|
||||
|
||||
let current = parse_front_matter(&contents)
|
||||
.ok()
|
||||
.and_then(|m| m.retry_count)
|
||||
.unwrap_or(0);
|
||||
let new_count = current + 1;
|
||||
|
||||
let updated = set_front_matter_field(&contents, "retry_count", &new_count.to_string());
|
||||
fs::write(path, &updated).map_err(|e| format!("Failed to write story file: {e}"))?;
|
||||
Ok(new_count)
|
||||
}
|
||||
|
||||
/// Write `blocked: true` to the YAML front matter of a story file.
|
||||
///
|
||||
/// Used to mark stories that have exceeded the retry limit and should not
|
||||
@@ -351,18 +319,63 @@ fn dep_is_done(project_root: &Path, dep_number: u32) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Append rejection notes to a story file body.
|
||||
///
|
||||
/// Adds a `## QA Rejection Notes` section at the end of the file so the coder
|
||||
/// agent can see what needs fixing.
|
||||
pub fn write_rejection_notes(path: &Path, notes: &str) -> Result<(), String> {
|
||||
let contents =
|
||||
fs::read_to_string(path).map_err(|e| format!("Failed to read story file: {e}"))?;
|
||||
// ── In-memory content variants (no filesystem access) ───────────────
|
||||
|
||||
/// Remove a key from the YAML front matter of a markdown string (pure function).
|
||||
///
|
||||
/// Returns the updated content. If no front matter or key is not found,
|
||||
/// returns the original content unchanged.
|
||||
pub fn clear_front_matter_field_in_content(contents: &str, key: &str) -> String {
|
||||
remove_front_matter_field(contents, key)
|
||||
}
|
||||
|
||||
/// Append rejection notes to a markdown string (pure function).
|
||||
///
|
||||
/// Returns the updated content with a `## QA Rejection Notes` section appended.
|
||||
pub fn write_rejection_notes_to_content(contents: &str, notes: &str) -> String {
|
||||
let section = format!("\n\n## QA Rejection Notes\n\n{notes}\n");
|
||||
let updated = format!("{contents}{section}");
|
||||
fs::write(path, &updated).map_err(|e| format!("Failed to write story file: {e}"))?;
|
||||
Ok(())
|
||||
format!("{contents}{section}")
|
||||
}
|
||||
|
||||
/// Resolve the effective QA mode from story content (no filesystem access).
|
||||
///
|
||||
/// Parses front matter from `contents` and returns the `qa` field if present,
|
||||
/// otherwise returns `default`.
|
||||
pub fn resolve_qa_mode_from_content(contents: &str, default: QaMode) -> QaMode {
|
||||
match parse_front_matter(contents) {
|
||||
Ok(meta) => meta.qa.unwrap_or(default),
|
||||
Err(_) => default,
|
||||
}
|
||||
}
|
||||
|
||||
/// Increment the `retry_count` field in story content (pure function).
|
||||
///
|
||||
/// Returns `(updated_content, new_count)`.
|
||||
pub fn increment_retry_count_in_content(contents: &str) -> (String, u32) {
|
||||
let current = parse_front_matter(contents)
|
||||
.ok()
|
||||
.and_then(|m| m.retry_count)
|
||||
.unwrap_or(0);
|
||||
let new_count = current + 1;
|
||||
let updated = set_front_matter_field(contents, "retry_count", &new_count.to_string());
|
||||
(updated, new_count)
|
||||
}
|
||||
|
||||
/// Write `blocked: true` to story content (pure function).
|
||||
pub fn write_blocked_in_content(contents: &str) -> String {
|
||||
set_front_matter_field(contents, "blocked", "true")
|
||||
}
|
||||
|
||||
/// Write or update `merge_failure` in story content (pure function).
|
||||
pub fn write_merge_failure_in_content(contents: &str, reason: &str) -> String {
|
||||
let escaped = reason.replace('"', "\\\"").replace('\n', " ").replace('\r', "");
|
||||
let yaml_value = format!("\"{escaped}\"");
|
||||
set_front_matter_field(contents, "merge_failure", &yaml_value)
|
||||
}
|
||||
|
||||
/// Write `review_hold: true` to story content (pure function).
|
||||
pub fn write_review_hold_in_content(contents: &str) -> String {
|
||||
set_front_matter_field(contents, "review_hold", "true")
|
||||
}
|
||||
|
||||
/// Resolve the effective QA mode for a story file.
|
||||
@@ -442,16 +455,6 @@ workflow: tdd
|
||||
assert_eq!(output, input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_coverage_baseline_updates_file() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let path = tmp.path().join("story.md");
|
||||
std::fs::write(&path, "---\nname: Test\n---\n# Story\n").unwrap();
|
||||
write_coverage_baseline(&path, 82.3).unwrap();
|
||||
let contents = std::fs::read_to_string(&path).unwrap();
|
||||
assert!(contents.contains("coverage_baseline: 82.3%"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_missing_front_matter() {
|
||||
let input = "# Story 26\n";
|
||||
@@ -691,14 +694,4 @@ workflow: tdd
|
||||
assert!(!dep_is_done(tmp.path(), 101));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_rejection_notes_appends_section() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let path = tmp.path().join("story.md");
|
||||
std::fs::write(&path, "---\nname: Test\n---\n# Story\n").unwrap();
|
||||
write_rejection_notes(&path, "Button color is wrong").unwrap();
|
||||
let contents = std::fs::read_to_string(&path).unwrap();
|
||||
assert!(contents.contains("## QA Rejection Notes"));
|
||||
assert!(contents.contains("Button color is wrong"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user