[wip] workflow cancellation policy
Some checks failed
CI / Rustfmt (push) Successful in 21s
CI / Cargo Audit & Deny (push) Successful in 32s
CI / Web Blocking Checks (push) Successful in 50s
CI / Security Blocking Checks (push) Successful in 9s
CI / Clippy (push) Failing after 1m58s
CI / Web Advisory Checks (push) Successful in 34s
CI / Security Advisory Checks (push) Successful in 1m26s
CI / Tests (push) Successful in 8m47s

This commit is contained in:
2026-03-09 14:08:01 -05:00
parent 87d830f952
commit 9e7e35cbe3
7 changed files with 451 additions and 32 deletions

View File

@@ -40,6 +40,7 @@ import type {
WorkflowBuilderState,
PaletteAction,
TransitionPreset,
CancellationPolicy,
} from "@/types/workflow";
import {
generateUniqueTaskName,
@@ -53,6 +54,7 @@ import {
removeTaskFromTransitions,
renameTaskInTransitions,
findStartingTaskIds,
CANCELLATION_POLICY_LABELS,
} from "@/types/workflow";
const INITIAL_STATE: WorkflowBuilderState = {
@@ -67,6 +69,7 @@ const INITIAL_STATE: WorkflowBuilderState = {
tasks: [],
tags: [],
enabled: true,
cancellationPolicy: "allow_finish",
};
export default function WorkflowBuilderPage() {
@@ -135,6 +138,7 @@ export default function WorkflowBuilderPage() {
const name =
refParts.length > 1 ? refParts.slice(1).join(".") : workflow.ref;
const defn = workflow.definition as Record<string, unknown> | undefined;
const builderState = definitionToBuilderState(
{
ref: workflow.ref,
@@ -143,10 +147,15 @@ export default function WorkflowBuilderPage() {
version: workflow.version,
parameters: workflow.param_schema || undefined,
output: workflow.out_schema || undefined,
tasks:
((workflow.definition as Record<string, unknown>)
?.tasks as WorkflowYamlDefinition["tasks"]) || [],
vars: (defn?.vars as Record<string, unknown>) || undefined,
tasks: (defn?.tasks as WorkflowYamlDefinition["tasks"]) || [],
output_map: (defn?.output_map as Record<string, string>) || undefined,
tags: workflow.tags,
cancellation_policy:
(defn?.cancellation_policy as
| "allow_finish"
| "cancel_running"
| undefined) || undefined,
},
workflow.pack_ref,
name,
@@ -843,6 +852,24 @@ export default function WorkflowBuilderPage() {
/>
Enabled
</label>
<select
value={state.cancellationPolicy}
onChange={(e) =>
updateMetadata({
cancellationPolicy: e.target.value as CancellationPolicy,
})
}
className="px-2 py-1 border border-gray-200 rounded text-xs text-gray-600 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 bg-white"
title="Cancellation policy: controls how running tasks behave when the workflow is cancelled"
>
{Object.entries(CANCELLATION_POLICY_LABELS).map(
([value, label]) => (
<option key={value} value={value}>
{label}
</option>
),
)}
</select>
</div>
</div>
</div>

View File

@@ -185,6 +185,23 @@ export interface WorkflowEdge {
labelPosition?: number;
}
/**
* Cancellation policy for a workflow.
*
* Controls what happens to running tasks when a workflow is cancelled:
* - `allow_finish` (default): Running tasks complete naturally; only
* pending/requested tasks are cancelled and no new tasks are dispatched.
* - `cancel_running`: All running and pending tasks are forcefully cancelled.
* Running processes receive SIGINT → SIGTERM → SIGKILL via the worker.
*/
export type CancellationPolicy = "allow_finish" | "cancel_running";
/** Human-readable labels for each cancellation policy */
export const CANCELLATION_POLICY_LABELS: Record<CancellationPolicy, string> = {
allow_finish: "Allow running tasks to finish",
cancel_running: "Cancel running tasks",
};
/** Complete workflow builder state */
export interface WorkflowBuilderState {
/** Workflow name (used to derive ref and filename) */
@@ -209,6 +226,8 @@ export interface WorkflowBuilderState {
tags: string[];
/** Whether the workflow is enabled */
enabled: boolean;
/** Cancellation policy (default: allow_finish) */
cancellationPolicy: CancellationPolicy;
}
/** Parameter definition in flat schema format */
@@ -238,6 +257,7 @@ export interface WorkflowYamlDefinition {
tasks: WorkflowYamlTask[];
output_map?: Record<string, string>;
tags?: string[];
cancellation_policy?: CancellationPolicy;
}
/**
@@ -252,6 +272,7 @@ export interface WorkflowGraphDefinition {
vars?: Record<string, unknown>;
tasks: WorkflowYamlTask[];
output_map?: Record<string, string>;
cancellation_policy?: CancellationPolicy;
}
/**
@@ -450,6 +471,10 @@ export function builderStateToDefinition(
definition.tags = state.tags;
}
if (state.cancellationPolicy !== "allow_finish") {
definition.cancellation_policy = state.cancellationPolicy;
}
return definition;
}
@@ -537,6 +562,10 @@ export function builderStateToGraph(
graph.vars = state.vars;
}
if (state.cancellationPolicy !== "allow_finish") {
graph.cancellation_policy = state.cancellationPolicy;
}
return graph;
}
@@ -723,6 +752,7 @@ export function definitionToBuilderState(
tasks,
tags: definition.tags || [],
enabled: true,
cancellationPolicy: definition.cancellation_policy || "allow_finish",
};
}