Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/api/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,19 @@ export type GetGraphExecutionStateResponse = {
[key: string]: number;
};
};
/**
* Summary
*/
summary: ExecutionStatusSummary;
};

/**
* ExecutionStatusSummary
*/
export type ExecutionStatusSummary = {
total_nodes: number;
ended_nodes: number;
has_ended: boolean;
};

/**
Expand Down
1 change: 1 addition & 0 deletions src/components/PipelineRun/RunDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ describe("<RunDetails/>", () => {
execution1: { SUCCEEDED: 1 },
execution2: { RUNNING: 1 },
},
summary: { total_nodes: 2, ended_nodes: 1, has_ended: false },
};

const mockComponentSpec: ComponentSpec = {
Expand Down
3 changes: 3 additions & 0 deletions src/components/PipelineRun/RunToolbar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ describe("<RunToolbar/>", () => {
execution1: { SUCCEEDED: 1 },
execution2: { RUNNING: 1 },
},
summary: { total_nodes: 2, ended_nodes: 1, has_ended: false },
};

const mockComponentSpec: ComponentSpec = {
Expand Down Expand Up @@ -205,6 +206,7 @@ describe("<RunToolbar/>", () => {
execution1: { SUCCEEDED: 1 },
execution2: { CANCELLED: 1 },
},
summary: { total_nodes: 2, ended_nodes: 2, has_ended: true },
},
},
rootExecutionId: "456",
Expand Down Expand Up @@ -250,6 +252,7 @@ describe("<RunToolbar/>", () => {
execution1: { SUCCEEDED: 1 },
execution2: { CANCELLED: 1 },
},
summary: { total_nodes: 2, ended_nodes: 2, has_ended: true },
},
},
rootExecutionId: "456",
Expand Down
13 changes: 2 additions & 11 deletions src/components/PipelineRun/RunToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ import { cn } from "@/lib/utils";
import { useComponentSpec } from "@/providers/ComponentSpecProvider";
import { useExecutionData } from "@/providers/ExecutionDataProvider";
import { extractCanonicalName } from "@/utils/canonicalPipelineName";
import {
countInProgressFromStats,
flattenExecutionStatusStats,
isExecutionComplete,
} from "@/utils/executionStatus";

import { ViewYamlButton } from "../shared/Buttons/ViewYamlButton";
import { buildTakSpecShape } from "../shared/PipelineRunNameTemplate/types";
Expand Down Expand Up @@ -44,12 +39,8 @@ export const RunToolbar = () => {
return null;
}

const executionStatusStats =
metadata?.execution_status_stats ??
flattenExecutionStatusStats(state.child_execution_status_stats);

const isInProgress = countInProgressFromStats(executionStatusStats) > 0;
const isComplete = isExecutionComplete(executionStatusStats);
const isComplete = state.summary.has_ended;
const isInProgress = !isComplete;
Comment on lines +42 to +43
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does "complete" state include "failed" or "cancelled" states? if yes, then `isInProgress could be incorrect at times

Copy link
Collaborator Author

@yuechao-qin yuechao-qin Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does include those states here.

Interesting, can you help me understand what isInProgress is suppose to mean then in the UI?


const isViewingSubgraph = currentSubgraphPath.length > 1;

Expand Down
9 changes: 1 addition & 8 deletions src/hooks/usePipelineRunData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import {
fetchExecutionState,
fetchPipelineRun,
} from "@/services/executionService";
import {
flattenExecutionStatusStats,
isExecutionComplete,
} from "@/utils/executionStatus";

const useRootExecutionId = (id: string) => {
const { backendUrl } = useBackend();
Expand Down Expand Up @@ -79,10 +75,7 @@ export const usePipelineRunData = (id: string) => {
if (!state) {
return false;
}
const stats = flattenExecutionStatusStats(
state.child_execution_status_stats,
);
return isExecutionComplete(stats) ? false : 5000;
return state.summary?.has_ended ? false : 5000;
}
return false;
},
Expand Down
1 change: 1 addition & 0 deletions src/routes/PipelineRun/PipelineRun.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ describe("<PipelineRun/>", () => {
"execution-1": { SUCCEEDED: 1 },
"execution-2": { RUNNING: 1 },
},
summary: { total_nodes: 2, ended_nodes: 1, has_ended: false },
};

beforeEach(() => {
Expand Down
70 changes: 0 additions & 70 deletions src/utils/executionStatus.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import { describe, expect, test } from "vitest";

import type { GetGraphExecutionStateResponse } from "@/api/types.gen";
import {
countInProgressFromStats,
flattenExecutionStatusStats,
getExecutionStatusLabel,
getOverallExecutionStatusFromStats,
isExecutionComplete,
} from "@/utils/executionStatus";

type ChildExecutionStatusStats =
Expand Down Expand Up @@ -142,71 +140,3 @@ describe("getOverallExecutionStatusFromStats()", () => {
).toBe("UNINITIALIZED");
});
});

describe("countInProgressFromStats()", () => {
test("counts all in-progress statuses", () => {
expect(
countInProgressFromStats({
RUNNING: 2,
PENDING: 1,
QUEUED: 3,
SUCCEEDED: 10,
}),
).toBe(6);
});

test("returns 0 when no in-progress statuses", () => {
expect(
countInProgressFromStats({
SUCCEEDED: 5,
FAILED: 2,
}),
).toBe(0);
});

test("counts all in-progress status types", () => {
expect(
countInProgressFromStats({
RUNNING: 1,
PENDING: 1,
QUEUED: 1,
WAITING_FOR_UPSTREAM: 1,
CANCELLING: 1,
UNINITIALIZED: 1,
}),
).toBe(6);
});
});

describe("isExecutionComplete()", () => {
test("returns true when all tasks are in terminal states", () => {
expect(
isExecutionComplete({
SUCCEEDED: 5,
FAILED: 2,
}),
).toBe(true);
});

test("returns false when any tasks are in progress", () => {
expect(
isExecutionComplete({
SUCCEEDED: 5,
RUNNING: 1,
}),
).toBe(false);
});

test("returns false for empty stats", () => {
expect(isExecutionComplete({})).toBe(false);
});

test("returns true for cancelled/skipped executions", () => {
expect(
isExecutionComplete({
CANCELLED: 3,
SKIPPED: 2,
}),
).toBe(true);
});
});
31 changes: 0 additions & 31 deletions src/utils/executionStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,6 @@ export const EXECUTION_STATUS_BG_COLORS: Record<string, string> = {
UNINITIALIZED: "bg-yellow-400",
};

/**
* Statuses considered "in progress" (not terminal).
*/
const IN_PROGRESS_STATUSES = new Set([
"RUNNING",
"PENDING",
"QUEUED",
"WAITING_FOR_UPSTREAM",
"CANCELLING",
"UNINITIALIZED",
]);

/**
* Priority order for determining overall/aggregate execution status.
* Higher priority statuses appear first — if any task has SYSTEM_ERROR,
Expand Down Expand Up @@ -134,22 +122,3 @@ export function getOverallExecutionStatusFromStats(
const firstNonZero = Object.entries(stats).find(([, c]) => (c ?? 0) > 0);
return firstNonZero?.[0];
}

/**
* Count the number of in-progress tasks from execution stats.
*/
export function countInProgressFromStats(stats: ExecutionStatusStats): number {
let count = 0;
for (const status of IN_PROGRESS_STATUSES) {
count += stats[status] ?? 0;
}
return count;
}

/**
* Check if execution is complete based on stats (no in-progress tasks and at least one task).
*/
export function isExecutionComplete(stats: ExecutionStatusStats): boolean {
const total = Object.values(stats).reduce((sum, c) => sum + (c ?? 0), 0);
return total > 0 && countInProgressFromStats(stats) === 0;
}