From 604fb55bd8d4015c16a1640567af62165bc1a0b0 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 13 May 2026 12:21:49 +0000 Subject: [PATCH] huskies: merge 959 --- server/src/agents/lifecycle.rs | 7 ++++-- server/src/agents/pool/auto_assign/scan.rs | 3 +-- server/src/http/mcp/story_tools/epic.rs | 14 +---------- server/src/http/workflow/pipeline.rs | 27 +++++++++------------- 4 files changed, 18 insertions(+), 33 deletions(-) diff --git a/server/src/agents/lifecycle.rs b/server/src/agents/lifecycle.rs index d3616ce2..8b56d241 100644 --- a/server/src/agents/lifecycle.rs +++ b/server/src/agents/lifecycle.rs @@ -385,8 +385,11 @@ pub fn move_story_to_stage(story_id: &str, target_stage: &str) -> Result<(String let item = read_typed_or_err(story_id)?; let from_name = stage_to_name(&item.stage); - // Idempotent: already in the target stage. - if item.stage.dir_name() == target_wire { + // Idempotent: already in the target stage. Compare via Stage discriminant + // so the check is typed rather than a raw string equality. + let already_there = Stage::from_dir(target_wire) + .is_some_and(|t| std::mem::discriminant(&item.stage) == std::mem::discriminant(&t)); + if already_there { return Ok((target_stage.to_string(), target_stage.to_string())); } diff --git a/server/src/agents/pool/auto_assign/scan.rs b/server/src/agents/pool/auto_assign/scan.rs index 6e94f08c..33762000 100644 --- a/server/src/agents/pool/auto_assign/scan.rs +++ b/server/src/agents/pool/auto_assign/scan.rs @@ -40,11 +40,10 @@ pub(super) fn scan_stage_items(_project_root: &Path, stage_dir: &str) -> Vec Result "backlog", - Stage::Coding => "current", - Stage::Qa => "qa", - Stage::Merge { .. } => "merge", - Stage::Done { .. } => "done", - Stage::Archived { .. } => "archived", - Stage::MergeFailure { .. } => "merge_failure", - Stage::MergeFailureFinal { .. } => "merge_failure_final", - Stage::Blocked { .. } => "blocked", - Stage::Frozen { .. } => "frozen", - Stage::ReviewHold { .. } => "review_hold", - }; + let stage_name = item.stage.dir_name(); if matches!(item.stage, Stage::Done { .. }) { done += 1; } diff --git a/server/src/http/workflow/pipeline.rs b/server/src/http/workflow/pipeline.rs index b6abff48..5d4c2d70 100644 --- a/server/src/http/workflow/pipeline.rs +++ b/server/src/http/workflow/pipeline.rs @@ -66,21 +66,14 @@ pub struct PipelineState { pub deterministic_merges_in_flight: Vec, } -/// Determine which pipeline bucket a frozen item's `resume_to` stage maps to. +/// Unwrap nested `Stage::Frozen` layers to find the innermost resume target. /// /// Mirrors the routing in `load_pipeline_state` for non-frozen items so that /// a frozen story always appears under the same section it was in before freezing. -fn frozen_resume_bucket(resume_to: &crate::pipeline_state::Stage) -> &'static str { - use crate::pipeline_state::Stage; - match resume_to { - Stage::Upcoming | Stage::Backlog => "backlog", - Stage::Coding | Stage::Blocked { .. } => "current", - Stage::Qa | Stage::ReviewHold { .. } => "qa", - Stage::Merge { .. } | Stage::MergeFailure { .. } | Stage::MergeFailureFinal { .. } => { - "merge" - } - Stage::Frozen { resume_to: inner } => frozen_resume_bucket(inner), - _ => "backlog", // Done, Archived → fall back to backlog (should not occur) +fn unwrap_frozen(stage: &crate::pipeline_state::Stage) -> &crate::pipeline_state::Stage { + match stage { + crate::pipeline_state::Stage::Frozen { resume_to: inner } => unwrap_frozen(inner), + other => other, } } @@ -182,10 +175,12 @@ pub fn load_pipeline_state(ctx: &AppContext) -> Result { Stage::Frozen { resume_to } => { // Route to the section matching the stage that was active when // the item was frozen, so it appears in-place. - match frozen_resume_bucket(resume_to) { - "current" => state.current.push(story), - "qa" => state.qa.push(story), - "merge" => state.merge.push(story), + match unwrap_frozen(resume_to) { + Stage::Coding | Stage::Blocked { .. } => state.current.push(story), + Stage::Qa | Stage::ReviewHold { .. } => state.qa.push(story), + Stage::Merge { .. } + | Stage::MergeFailure { .. } + | Stage::MergeFailureFinal { .. } => state.merge.push(story), _ => state.backlog.push(story), } }