huskies: merge 575_bug_unblock_command_reads_story_files_from_filesystem_instead_of_crdt
This commit is contained in:
@@ -6,8 +6,7 @@
|
|||||||
|
|
||||||
use super::CommandContext;
|
use super::CommandContext;
|
||||||
use crate::io::story_metadata::{
|
use crate::io::story_metadata::{
|
||||||
clear_front_matter_field, clear_front_matter_field_in_content, parse_front_matter,
|
clear_front_matter_field_in_content, parse_front_matter, set_front_matter_field,
|
||||||
set_front_matter_field,
|
|
||||||
};
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@@ -34,9 +33,9 @@ pub(super) fn handle_unblock(ctx: &CommandContext) -> Option<String> {
|
|||||||
/// Returns a Markdown-formatted response string suitable for all transports.
|
/// Returns a Markdown-formatted response string suitable for all transports.
|
||||||
/// Also used by the MCP `unblock` tool.
|
/// Also used by the MCP `unblock` tool.
|
||||||
///
|
///
|
||||||
/// Lookup priority: CRDT → content store → filesystem (Story 512).
|
/// Lookup priority: CRDT → content store.
|
||||||
pub(crate) fn unblock_by_number(project_root: &Path, story_number: &str) -> String {
|
pub(crate) fn unblock_by_number(project_root: &Path, story_number: &str) -> String {
|
||||||
let (story_id, _stage_dir, path, _content) =
|
let (story_id, _, _, _) =
|
||||||
match crate::chat::lookup::find_story_by_number(project_root, story_number) {
|
match crate::chat::lookup::find_story_by_number(project_root, story_number) {
|
||||||
Some(found) => found,
|
Some(found) => found,
|
||||||
None => {
|
None => {
|
||||||
@@ -44,15 +43,7 @@ pub(crate) fn unblock_by_number(project_root: &Path, story_number: &str) -> Stri
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prefer DB-backed unblock when the story is in the content store.
|
unblock_by_story_id(&story_id)
|
||||||
// Note: `content` may have come from the filesystem fallback in
|
|
||||||
// `find_story_by_number`, so we must re-check the DB rather than
|
|
||||||
// relying on `content.is_some()` alone.
|
|
||||||
if crate::db::read_content(&story_id).is_some() {
|
|
||||||
unblock_by_story_id(&story_id)
|
|
||||||
} else {
|
|
||||||
unblock_by_path(&path, &story_id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unblock a story using the content store (DB-backed).
|
/// Unblock a story using the content store (DB-backed).
|
||||||
@@ -105,64 +96,6 @@ fn unblock_by_story_id(story_id: &str) -> String {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Core unblock logic: reset blocked state for a known story file path.
|
|
||||||
///
|
|
||||||
/// Reads front matter, verifies the story is blocked, clears the `blocked`
|
|
||||||
/// flag, and resets `retry_count` to 0. Also used by the MCP `unblock` tool
|
|
||||||
/// when the caller has already resolved the story path from a full `story_id`.
|
|
||||||
pub(crate) fn unblock_by_path(path: &Path, story_id: &str) -> String {
|
|
||||||
let contents = match std::fs::read_to_string(path) {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => return format!("Failed to read story file: {e}"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let meta = match parse_front_matter(&contents) {
|
|
||||||
Ok(m) => m,
|
|
||||||
Err(e) => return format!("Failed to parse front matter for **{story_id}**: {e}"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let story_name = meta.name.as_deref().unwrap_or(story_id).to_string();
|
|
||||||
|
|
||||||
let has_blocked = meta.blocked == Some(true);
|
|
||||||
let has_merge_failure = meta.merge_failure.is_some();
|
|
||||||
|
|
||||||
if !has_blocked && !has_merge_failure {
|
|
||||||
return format!("**{story_name}** ({story_id}) is not blocked. Nothing to unblock.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the blocked flag if present.
|
|
||||||
if has_blocked && let Err(e) = clear_front_matter_field(path, "blocked") {
|
|
||||||
return format!("Failed to clear blocked flag on **{story_id}**: {e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear merge_failure if present.
|
|
||||||
if has_merge_failure && let Err(e) = clear_front_matter_field(path, "merge_failure") {
|
|
||||||
return format!("Failed to clear merge_failure on **{story_id}**: {e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset retry_count to 0 (re-read the updated file, modify, write).
|
|
||||||
let updated_contents = match std::fs::read_to_string(path) {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(e) => return format!("Failed to re-read story file after unblocking: {e}"),
|
|
||||||
};
|
|
||||||
let with_retry_reset = set_front_matter_field(&updated_contents, "retry_count", "0");
|
|
||||||
if let Err(e) = std::fs::write(path, &with_retry_reset) {
|
|
||||||
return format!("Failed to reset retry_count on **{story_id}**: {e}");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cleared = Vec::new();
|
|
||||||
if has_blocked {
|
|
||||||
cleared.push("blocked");
|
|
||||||
}
|
|
||||||
if has_merge_failure {
|
|
||||||
cleared.push("merge_failure");
|
|
||||||
}
|
|
||||||
format!(
|
|
||||||
"Unblocked **{story_name}** ({story_id}). Cleared: {}. Retry count reset to 0.",
|
|
||||||
cleared.join(", ")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Tests
|
// Tests
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user