huskies: merge 1085

This commit is contained in:
dave
2026-05-15 01:32:34 +00:00
parent 56179d712e
commit b053f14d58
11 changed files with 440 additions and 132 deletions
+2 -2
View File
@@ -41,8 +41,8 @@ mod tests;
#[allow(unused_imports)]
pub use types::{
AgentClaim, AgentName, ArchiveReason, BranchName, ExecutionState, GitSha, MergeFailureKind,
NodePubkey, PipelineItem, PlanState, Stage, StoryId, TransitionError, stage_dir_name,
stage_label,
NodePubkey, Pipeline, PipelineItem, PlanState, Stage, Status, StoryId, TransitionError,
stage_dir_name, stage_label,
};
#[allow(unused_imports)]
+138
View File
@@ -429,6 +429,144 @@ impl Stage {
}
}
// ── Display split (story 1085): Pipeline column + Status badge ─────────────
/// Column placement for a work item in the UI/chat status display.
///
/// Derived from [`Stage`] via [`Stage::pipeline`]. Display callers route items
/// to columns by this enum instead of pattern-matching `Stage` variants, so
/// new badges (e.g. `Frozen`, `Blocked`) do not produce new columns.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Pipeline {
/// Items in `Upcoming` or `Backlog` stages.
Backlog,
/// Items being coded (or blocked while in the coding lane).
Coding,
/// Items in QA or `ReviewHold`.
Qa,
/// Items in `Merge`, `MergeFailure`, or `MergeFailureFinal`.
Merge,
/// Items in `Done`.
Done,
/// Abandoned, superseded, or rejected items.
Closed,
/// Items swept into `Archived`.
Archived,
}
impl Pipeline {
/// Stable wire-format identifier (kebab-case).
pub fn as_str(&self) -> &'static str {
match self {
Pipeline::Backlog => "backlog",
Pipeline::Coding => "coding",
Pipeline::Qa => "qa",
Pipeline::Merge => "merge",
Pipeline::Done => "done",
Pipeline::Closed => "closed",
Pipeline::Archived => "archived",
}
}
}
/// Badge/indicator for a work item, orthogonal to its [`Pipeline`] column.
///
/// Derived from [`Stage`] via [`Stage::status`]. A `Frozen` story stays in
/// its underlying `Pipeline` column (e.g. `Coding`) and is decorated with
/// `Status::Frozen` for the display. `Status::Done` is reserved for items in
/// the `Done` column and is never produced for items still in flight, so a
/// done item never carries a `MergeFailure*` badge (story 1052).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", tag = "kind")]
pub enum Status {
/// No special badge — normal in-progress item.
Active,
/// Item is paused (`Stage::Frozen`).
Frozen,
/// Item is held for human review (`Stage::ReviewHold`).
ReviewHold,
/// Item is blocked (`Stage::Blocked` or legacy `Archived(Blocked)`).
Blocked,
/// Merge failed; mergemaster may still be recovering.
MergeFailure,
/// Merge failed beyond automatic recovery.
MergeFailureFinal,
/// User abandoned the item.
Abandoned,
/// Item was superseded by another work item.
Superseded,
/// Item was permanently rejected.
Rejected,
/// Item completed successfully.
Done,
}
impl Status {
/// Stable wire-format identifier (kebab-case).
pub fn as_str(&self) -> &'static str {
match self {
Status::Active => "active",
Status::Frozen => "frozen",
Status::ReviewHold => "review-hold",
Status::Blocked => "blocked",
Status::MergeFailure => "merge-failure",
Status::MergeFailureFinal => "merge-failure-final",
Status::Abandoned => "abandoned",
Status::Superseded => "superseded",
Status::Rejected => "rejected",
Status::Done => "done",
}
}
}
impl Stage {
/// Display column for this stage. `Frozen { resume_to }` recurses so a
/// paused story keeps its underlying column.
pub fn pipeline(&self) -> Pipeline {
match self {
Stage::Upcoming | Stage::Backlog => Pipeline::Backlog,
Stage::Coding { .. } | Stage::Blocked { .. } => Pipeline::Coding,
Stage::Qa | Stage::ReviewHold { .. } => Pipeline::Qa,
Stage::Merge { .. } | Stage::MergeFailure { .. } | Stage::MergeFailureFinal { .. } => {
Pipeline::Merge
}
Stage::Frozen { resume_to } => resume_to.pipeline(),
Stage::Done { .. } => Pipeline::Done,
Stage::Abandoned { .. } | Stage::Superseded { .. } | Stage::Rejected { .. } => {
Pipeline::Closed
}
Stage::Archived {
reason: ArchiveReason::Blocked { .. },
..
} => Pipeline::Coding,
Stage::Archived { .. } => Pipeline::Archived,
}
}
/// Display badge for this stage. `Frozen { resume_to }` returns
/// `Status::Frozen` regardless of the inner stage; callers wanting the
/// underlying badge inspect `resume_to` directly.
pub fn status(&self) -> Status {
match self {
Stage::Frozen { .. } => Status::Frozen,
Stage::ReviewHold { .. } => Status::ReviewHold,
Stage::Blocked { .. }
| Stage::Archived {
reason: ArchiveReason::Blocked { .. },
..
} => Status::Blocked,
Stage::MergeFailure { .. } => Status::MergeFailure,
Stage::MergeFailureFinal { .. } => Status::MergeFailureFinal,
Stage::Abandoned { .. } => Status::Abandoned,
Stage::Superseded { .. } => Status::Superseded,
Stage::Rejected { .. } => Status::Rejected,
Stage::Done { .. } => Status::Done,
_ => Status::Active,
}
}
}
// ── Per-node execution state ────────────────────────────────────────────────
/// Per-node execution tracking, stored in the CRDT under each node's pubkey.