huskies: merge 895

This commit is contained in:
dave
2026-05-13 08:48:36 +00:00
parent 4a8ed4348b
commit 6bd11d41f9
7 changed files with 405 additions and 49 deletions
+42 -8
View File
@@ -33,9 +33,12 @@ pub struct UpcomingStory {
/// Number of retries at the current pipeline stage.
#[serde(skip_serializing_if = "Option::is_none")]
pub retry_count: Option<u32>,
/// True when the story has exceeded its retry limit and will not be auto-assigned.
/// True when the item is in `Stage::Blocked` (awaiting human unblock).
#[serde(skip_serializing_if = "Option::is_none")]
pub blocked: Option<bool>,
/// True when the item is in `Stage::Frozen` (paused at its current stage).
#[serde(skip_serializing_if = "Option::is_none")]
pub frozen: Option<bool>,
/// Story numbers this story depends on.
#[serde(skip_serializing_if = "Option::is_none")]
pub depends_on: Option<Vec<u32>>,
@@ -63,6 +66,24 @@ pub struct PipelineState {
pub deterministic_merges_in_flight: Vec<String>,
}
/// Determine which pipeline bucket a frozen item's `resume_to` stage maps to.
///
/// 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)
}
}
/// Load the full pipeline state (all 5 active stages).
///
/// Reads from the CRDT document and enriches with content from the
@@ -131,6 +152,11 @@ pub fn load_pipeline_state(ctx: &AppContext) -> Result<PipelineState, String> {
} else {
None
},
frozen: if item.stage.is_frozen() {
Some(true)
} else {
None
},
depends_on: if item.depends_on.is_empty() {
None
} else {
@@ -143,12 +169,6 @@ pub fn load_pipeline_state(ctx: &AppContext) -> Result<PipelineState, String> {
},
epic_id,
};
// Frozen items (CRDT flag) are routed to the backlog bucket regardless
// of their underlying stage — they're paused, not progressing.
if item.is_frozen() {
state.backlog.push(story);
continue;
}
match &item.stage {
Stage::Upcoming => state.backlog.push(story), // upcoming shown with backlog
Stage::Backlog => state.backlog.push(story),
@@ -159,7 +179,16 @@ pub fn load_pipeline_state(ctx: &AppContext) -> Result<PipelineState, String> {
Stage::MergeFailure { .. } => state.merge.push(story), // show merge failures with merge
Stage::MergeFailureFinal { .. } => state.merge.push(story), // mergemaster gave up
Stage::ReviewHold { .. } => state.qa.push(story), // review-held shown with QA
Stage::Frozen { .. } => state.backlog.push(story), // paused, route to backlog
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),
_ => state.backlog.push(story),
}
}
Stage::Done { .. } => state.done.push(story),
Stage::Archived { .. } => {} // skip archived
}
@@ -244,6 +273,11 @@ pub fn load_upcoming_stories(_ctx: &AppContext) -> Result<Vec<UpcomingStory>, St
} else {
None
},
frozen: if item.stage.is_frozen() {
Some(true)
} else {
None
},
depends_on: if item.depends_on.is_empty() {
None
} else {