storkit: merge 425_story_chat_notification_when_a_story_blocks_with_reason

This commit is contained in:
dave
2026-03-28 09:36:15 +00:00
parent 740f1b5e6e
commit 98b5475160
5 changed files with 184 additions and 15 deletions
@@ -76,12 +76,17 @@ impl AgentPool {
.join(".storkit/work")
.join(stage_dir)
.join(format!("{story_id}.md"));
let empty_diff_reason = "Feature branch has no code changes — the coder agent \
did not produce any commits.";
let _ = crate::io::story_metadata::write_merge_failure(
&story_path,
"Feature branch has no code changes — the coder agent \
did not produce any commits.",
empty_diff_reason,
);
let _ = crate::io::story_metadata::write_blocked(&story_path);
let _ = self.watcher_tx.send(crate::io::watcher::WatcherEvent::StoryBlocked {
story_id: story_id.to_string(),
reason: empty_diff_reason.to_string(),
});
continue;
}
+29 -11
View File
@@ -122,8 +122,12 @@ impl AgentPool {
let story_path = project_root
.join(".storkit/work/2_current")
.join(format!("{story_id}.md"));
if should_block_story(&story_path, config.max_retries, story_id, "coder") {
if let Some(reason) = should_block_story(&story_path, config.max_retries, story_id, "coder") {
// Story has exceeded retry limit — do not restart.
let _ = self.watcher_tx.send(WatcherEvent::StoryBlocked {
story_id: story_id.to_string(),
reason,
});
} else {
slog!(
"[pipeline] Coder '{agent_name}' failed gates for '{story_id}'. Restarting."
@@ -221,8 +225,12 @@ impl AgentPool {
let story_path = project_root
.join(".storkit/work/3_qa")
.join(format!("{story_id}.md"));
if should_block_story(&story_path, config.max_retries, story_id, "qa-coverage") {
if let Some(reason) = should_block_story(&story_path, config.max_retries, story_id, "qa-coverage") {
// Story has exceeded retry limit — do not restart.
let _ = self.watcher_tx.send(WatcherEvent::StoryBlocked {
story_id: story_id.to_string(),
reason,
});
} else {
slog!(
"[pipeline] QA coverage gate failed for '{story_id}'. Restarting QA."
@@ -245,8 +253,12 @@ impl AgentPool {
let story_path = project_root
.join(".storkit/work/3_qa")
.join(format!("{story_id}.md"));
if should_block_story(&story_path, config.max_retries, story_id, "qa") {
if let Some(reason) = should_block_story(&story_path, config.max_retries, story_id, "qa") {
// Story has exceeded retry limit — do not restart.
let _ = self.watcher_tx.send(WatcherEvent::StoryBlocked {
story_id: story_id.to_string(),
reason,
});
} else {
slog!("[pipeline] QA failed gates for '{story_id}'. Restarting.");
let context = format!(
@@ -321,8 +333,12 @@ impl AgentPool {
let story_path = project_root
.join(".storkit/work/4_merge")
.join(format!("{story_id}.md"));
if should_block_story(&story_path, config.max_retries, story_id, "mergemaster") {
if let Some(reason) = should_block_story(&story_path, config.max_retries, story_id, "mergemaster") {
// Story has exceeded retry limit — do not restart.
let _ = self.watcher_tx.send(WatcherEvent::StoryBlocked {
story_id: story_id.to_string(),
reason,
});
} else {
slog!(
"[pipeline] Post-merge tests failed for '{story_id}'. Restarting mergemaster."
@@ -830,15 +846,15 @@ fn spawn_pipeline_advance(
/// Increment retry_count and block the story if it exceeds `max_retries`.
///
/// Returns `true` if the story is now blocked (caller should NOT restart the agent).
/// Returns `false` if the story may be retried.
/// Returns `Some(reason)` if the story is now blocked (caller should NOT restart the agent).
/// Returns `None` if the story may be retried.
/// When `max_retries` is 0, retry limits are disabled.
fn should_block_story(story_path: &Path, max_retries: u32, story_id: &str, stage_label: &str) -> bool {
fn should_block_story(story_path: &Path, max_retries: u32, story_id: &str, stage_label: &str) -> Option<String> {
use crate::io::story_metadata::{increment_retry_count, write_blocked};
if max_retries == 0 {
// Retry limits disabled.
return false;
return None;
}
match increment_retry_count(story_path) {
@@ -851,17 +867,19 @@ fn should_block_story(story_path: &Path, max_retries: u32, story_id: &str, stage
if let Err(e) = write_blocked(story_path) {
slog_error!("[pipeline] Failed to write blocked flag for '{story_id}': {e}");
}
true
Some(format!(
"Retry limit exceeded ({new_count}/{max_retries}) at {stage_label} stage"
))
} else {
slog!(
"[pipeline] Story '{story_id}' retry {new_count}/{max_retries} at {stage_label} stage."
);
false
None
}
}
Err(e) => {
slog_error!("[pipeline] Failed to increment retry_count for '{story_id}': {e}");
false // Don't block on error — allow retry.
None // Don't block on error — allow retry.
}
}
}