Files
attune/work-summary/2026-03-09-workflow-cancellation-policy.md
David Culbreth 9e7e35cbe3
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
[wip] workflow cancellation policy
2026-03-09 14:08:01 -05:00

3.1 KiB

Work Summary: Workflow Cancellation Policy

Date: 2026-03-09

Overview

Added configurable cancellation behavior for workflows. When a workflow is cancelled, the policy controls whether currently running tasks are allowed to finish (default) or are forcefully terminated via SIGTERM.

Changes

Backend (Rust)

crates/common/src/workflow/parser.rs

  • Added CancellationPolicy enum with two variants: AllowFinish (default) and CancelRunning
  • Added cancellation_policy field to WorkflowDefinition with #[serde(default, skip_serializing_if)] for backward compatibility
  • Added 5 unit tests covering default behavior, explicit values, JSON round-trip, and deserialization of legacy definitions without the field

crates/common/src/workflow/mod.rs

  • Re-exported CancellationPolicy from the workflow module

crates/api/src/routes/executions.rs

  • Added resolve_cancellation_policy() helper that loads the workflow definition from DB and extracts the policy (falls back to AllowFinish on any failure)
  • Refactored cancel_workflow_children() to resolve the policy and delegate to cancel_workflow_children_with_policy()
  • cancel_workflow_children_with_policy() respects the policy:
    • AllowFinish: Only cancels pre-running children (Requested/Scheduling/Scheduled). Running children are left alone to complete naturally. advance_workflow sees the cancelled workflow_execution and stops dispatching new tasks.
    • CancelRunning: Cancels all children including running ones via SIGINT→SIGTERM→SIGKILL MQ messages to workers (previous hard-coded behavior).
  • Policy is inherited through recursive cancellation of nested workflows

Frontend (TypeScript/React)

web/src/types/workflow.ts

  • Added CancellationPolicy type and CANCELLATION_POLICY_LABELS constant
  • Added cancellationPolicy field to WorkflowBuilderState
  • Added cancellation_policy field to WorkflowYamlDefinition and WorkflowGraphDefinition
  • Updated builderStateToDefinition() and builderStateToGraph() to include the field (omitted when default)
  • Updated definitionToBuilderState() to read the field back

web/src/pages/actions/WorkflowBuilderPage.tsx

  • Added cancellationPolicy to INITIAL_STATE
  • Added a dropdown select in the metadata row for choosing the cancellation policy
  • The setting persists into both the full definition and the raw YAML graph view

Design Decisions

  • Default is AllowFinish: This is the safest behavior — running tasks complete naturally, preventing data corruption from interrupted operations.
  • No migration needed: The field is stored inside the workflow_definition.definition JSONB column. #[serde(default)] handles existing definitions that lack the field.
  • Policy inherited by nested workflows: When a parent workflow is cancelled, its cancellation policy propagates to all descendant workflows rather than each resolving its own.
  • skip_serializing_if: The default value is omitted from serialized JSON to keep stored definitions compact and backward-compatible.

Testing

  • 29/29 workflow parser tests pass (5 new)
  • Zero warnings across entire Rust workspace
  • Zero TypeScript errors