huskies: merge 868

This commit is contained in:
dave
2026-04-29 23:28:57 +00:00
parent e02e566648
commit 1d86202abb
15 changed files with 135 additions and 60 deletions
+24
View File
@@ -248,6 +248,28 @@ pub fn transition_to_blocked(story_id: &str, reason: &str) -> Result<(), String>
.map_err(|e| e.to_string())
}
/// Transition a story from `Stage::Merge` to `Stage::MergeFailure` via the state machine.
///
/// Builds a `PipelineEvent::MergeFailed { reason }`, validates the transition, writes
/// the resulting `Stage::MergeFailure` to the CRDT, and persists the reason to front
/// matter so it survives server restarts.
/// Returns `Err` on `TransitionError` — callers must NOT fall back to direct register writes.
pub fn transition_to_merge_failure(story_id: &str, reason: &str) -> Result<(), String> {
let reason_owned = reason.to_string();
let transform: Box<dyn Fn(&str) -> String> = Box::new(move |content: &str| {
crate::io::story_metadata::write_merge_failure_in_content(content, &reason_owned)
});
apply_transition(
story_id,
PipelineEvent::MergeFailed {
reason: reason.to_string(),
},
Some(&*transform),
)
.map(|_| ())
.map_err(|e| e.to_string())
}
/// Transition a story out of `Blocked` back to `Coding` via the state machine.
///
/// Builds a `PipelineEvent::Unblock`, validates the transition, writes the
@@ -301,6 +323,7 @@ fn map_stage_move_to_event(
}),
(Stage::Coding | Stage::Qa | Stage::Backlog, "done") => Ok(PipelineEvent::Close),
(Stage::Blocked { .. }, "current") => Ok(PipelineEvent::Unblock),
(Stage::MergeFailure { .. }, "backlog") => Ok(PipelineEvent::Unblock),
(
Stage::Archived {
reason: ArchiveReason::Blocked { .. },
@@ -388,6 +411,7 @@ fn stage_to_name(s: &Stage) -> &'static str {
Stage::Blocked { .. } => "blocked",
Stage::Qa => "qa",
Stage::Merge { .. } => "merge",
Stage::MergeFailure { .. } => "merge_failure",
Stage::Done { .. } => "done",
Stage::Archived { .. } => "archived",
Stage::Frozen { .. } => "frozen",
@@ -114,16 +114,6 @@ impl AgentPool {
Err(e) => e.clone(),
};
let is_no_commits = reason.contains("no commits to merge");
if !is_no_commits {
// Write merge_failure to content for non-blocking failures.
if let Some(contents) = crate::db::read_content(&sid) {
let updated = crate::io::story_metadata::write_merge_failure_in_content(
&contents, &reason,
);
crate::db::write_content(&sid, &updated);
crate::db::write_item_with_content(&sid, "4_merge", &updated);
}
}
if is_no_commits {
if let Err(e) = crate::agents::lifecycle::transition_to_blocked(&sid, &reason) {
crate::slog_error!("[merge] Failed to transition '{sid}' to Blocked: {e}");
@@ -135,6 +125,14 @@ impl AgentPool {
reason,
});
} else {
// Transition through the state machine (Merge → MergeFailure).
if let Err(e) =
crate::agents::lifecycle::transition_to_merge_failure(&sid, &reason)
{
crate::slog_error!(
"[merge] Failed to transition '{sid}' to MergeFailure: {e}"
);
}
let _ = pool
.watcher_tx
.send(crate::io::watcher::WatcherEvent::MergeFailure {