huskies: merge 1085
This commit is contained in:
@@ -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)]
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user