huskies: merge 1045
This commit is contained in:
@@ -66,15 +66,36 @@ const STAGE_LABELS: Record<string, string> = {
|
||||
archived: "Archived",
|
||||
};
|
||||
|
||||
/// Derive a short label from a merge failure string based on the failure kind.
|
||||
function mergeFailureKindLabel(failure: string): string {
|
||||
if (failure.includes("Merge conflict") || failure.includes("CONFLICT")) {
|
||||
return "ConflictDetected";
|
||||
}
|
||||
if (failure.includes("Quality gates failed") || failure.includes("gates failed")) {
|
||||
return "GatesFailed";
|
||||
}
|
||||
if (failure.includes("no code changes") || failure.includes("empty diff")) {
|
||||
return "EmptyDiff";
|
||||
}
|
||||
if (failure.includes("No commits")) {
|
||||
return "NoCommits";
|
||||
}
|
||||
return "✕ FAILED";
|
||||
}
|
||||
|
||||
/// A single story row inside a project pipeline card.
|
||||
/** Render one story row in a gateway-aggregate panel: `#<id> <name>` with stage badge. */
|
||||
export function StoryRow({ item }: { item: PipelineItem }) {
|
||||
export function StoryRow({ item, mergeQueuePos }: { item: PipelineItem; mergeQueuePos?: number }) {
|
||||
const isStuck = item.merge_failure != null || item.blocked;
|
||||
const isMergeActive = item.stage === "merge" && !isStuck && item.agent?.status === "running";
|
||||
|
||||
let color: string;
|
||||
let label: string;
|
||||
|
||||
if (isStuck) {
|
||||
if (isMergeActive) {
|
||||
color = "#58a6ff";
|
||||
label = "▶ MERGING";
|
||||
} else if (isStuck) {
|
||||
const agentStatus = item.agent?.status;
|
||||
if (agentStatus === "running") {
|
||||
color = "#e3b341";
|
||||
@@ -82,9 +103,24 @@ export function StoryRow({ item }: { item: PipelineItem }) {
|
||||
} else if (agentStatus === "pending") {
|
||||
color = "#e3b341";
|
||||
label = "⏳ QUEUED";
|
||||
} else if (item.merge_failure != null) {
|
||||
color = "#f85149";
|
||||
label = mergeFailureKindLabel(item.merge_failure);
|
||||
} else {
|
||||
color = "#f85149";
|
||||
label = item.merge_failure != null ? "✕ FAILED" : "⊘ BLOCKED";
|
||||
label = "⊘ BLOCKED";
|
||||
}
|
||||
} else if (item.stage === "merge" && item.agent?.status === "pending") {
|
||||
color = "#e3b341";
|
||||
label = "⏳ QUEUED";
|
||||
} else if (item.stage === "merge") {
|
||||
color = "#6e7681";
|
||||
if (mergeQueuePos === 1) {
|
||||
label = "NEXT IN QUEUE";
|
||||
} else if (mergeQueuePos != null) {
|
||||
label = `awaiting-slot (#${mergeQueuePos})`;
|
||||
} else {
|
||||
label = "awaiting-slot";
|
||||
}
|
||||
} else {
|
||||
color = STAGE_COLORS[item.stage] ?? "#8b949e";
|
||||
@@ -101,6 +137,10 @@ export function StoryRow({ item }: { item: PipelineItem }) {
|
||||
gap: "8px",
|
||||
padding: "4px 0",
|
||||
fontSize: "0.82em",
|
||||
background: isMergeActive ? "#58a6ff0a" : undefined,
|
||||
borderRadius: isMergeActive ? "4px" : undefined,
|
||||
paddingLeft: isMergeActive ? "4px" : undefined,
|
||||
paddingRight: isMergeActive ? "4px" : undefined,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@@ -446,10 +486,12 @@ function ProjectStoryRow({
|
||||
project,
|
||||
item,
|
||||
showProject,
|
||||
mergeQueuePos,
|
||||
}: {
|
||||
project: string;
|
||||
item: PipelineItem;
|
||||
showProject: boolean;
|
||||
mergeQueuePos?: number;
|
||||
}) {
|
||||
return (
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
@@ -470,7 +512,7 @@ function ProjectStoryRow({
|
||||
</span>
|
||||
)}
|
||||
<div style={{ flex: 1, minWidth: 0 }}>
|
||||
<StoryRow item={item} />
|
||||
<StoryRow item={item} mergeQueuePos={mergeQueuePos} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -503,6 +545,20 @@ function InProgressTabContent({
|
||||
(s) => byStage[s].length > 0,
|
||||
);
|
||||
|
||||
// Compute queue position among clean awaiting merge items (Stage::Merge, no failure, no running agent).
|
||||
const mergeQueuePosMap = new Map<string, number>();
|
||||
let queuePos = 0;
|
||||
for (const { project, item } of byStage.merge) {
|
||||
if (
|
||||
!item.blocked &&
|
||||
!item.merge_failure &&
|
||||
item.agent?.status !== "running"
|
||||
) {
|
||||
queuePos += 1;
|
||||
mergeQueuePosMap.set(`${project}:${item.story_id}`, queuePos);
|
||||
}
|
||||
}
|
||||
|
||||
if (allItems.length === 0) {
|
||||
return (
|
||||
<p style={{ color: "#6e7681", padding: "16px 0" }}>
|
||||
@@ -538,6 +594,11 @@ function InProgressTabContent({
|
||||
project={project}
|
||||
item={item}
|
||||
showProject={multiProject}
|
||||
mergeQueuePos={
|
||||
stage === "merge"
|
||||
? mergeQueuePosMap.get(`${project}:${item.story_id}`)
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user