Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5acc081
feat: adds the `preemptible` task hint
claymcleod Oct 15, 2025
b6017eb
revise: further revisions
claymcleod Oct 15, 2025
f0d9675
revise: adds table of contents entry
claymcleod Oct 15, 2025
46176ec
chore: adds `CHANGELOG.md` entry
claymcleod Oct 15, 2025
d0b5c5f
updates based on @geoffjentry suggestions
claymcleod Jan 12, 2026
9b32cc0
Merge branch 'wdl-1.3' into preemptible-hint
claymcleod Jan 12, 2026
496b813
chore: fixes the merge conflict committed to `CHANGELOG.md`
claymcleod Jan 12, 2026
28bd9e7
chore: updates MiniWDL shield badge
github-actions[bot] Jan 12, 2026
759673a
chore: updates Toil shield badge
github-actions[bot] Jan 12, 2026
7827da0
Merge branch 'wdl-1.3' into preemptible-hint
claymcleod Jan 12, 2026
8b33f29
chore: updates Cromwell shield badge
github-actions[bot] Jan 12, 2026
62a05ed
revise: removes example sentence before example section
claymcleod Jan 12, 2026
94af11d
chore: updates MiniWDL shield badge
github-actions[bot] Jan 12, 2026
fab3d45
chore: updates Toil shield badge
github-actions[bot] Jan 12, 2026
e8f896d
chore: updates Cromwell shield badge
github-actions[bot] Jan 12, 2026
2a98496
docs: redesign `preemptible` execution semantics
claymcleod Jul 15, 2026
128301d
chore: merge WDL 1.4 into `preemptible-hint`
claymcleod Jul 15, 2026
479d182
chore: updates Toil shield badge
github-actions[bot] Jul 15, 2026
cb368c7
docs: clarify `max_retries` preemption accounting
claymcleod Jul 15, 2026
cdb38d0
chore: updates Toil shield badge
github-actions[bot] Jul 15, 2026
a90f419
Merge branch `wdl-1.4` into `preemptible-hint`
claymcleod Jul 16, 2026
7d60213
chore: updates Cromwell shield badge
github-actions[bot] Jul 16, 2026
0e8a458
docs: decouple `preemptible_only` from hint enforcement
claymcleod Jul 16, 2026
dde7316
chore: updates Toil shield badge
github-actions[bot] Jul 16, 2026
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ Keep the changelog pleasant to read in the text editor:
+ Properly indent blocks.
-->

version 1.4.0
---------------------------

+ Introduced the `preemptible` task hint and `preemptible_only` task requirement ([#728](https://github.com/openwdl/wdl/pull/728)).

version 1.3.0
---------------------------

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
|-----------|------------------|
| MiniWDL | [![MiniWDL Tests](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/openwdl/wdl/wdl-1.4/shields/miniwdl.json)](https://github.com/openwdl/wdl/actions/runs/29465188571) |
| Sprocket | [![Sprocket Tests](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/openwdl/wdl/wdl-1.4/shields/sprocket.json)](https://github.com/openwdl/wdl/actions/runs/29465188597) |
| Toil | [![Toil Tests](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/openwdl/wdl/wdl-1.4/shields/toil.json)](https://github.com/openwdl/wdl/actions/runs/29465188570) |
| Cromwell | [![Cromwell Tests](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/openwdl/wdl/wdl-1.4/shields/cromwell.json)](https://github.com/openwdl/wdl/actions/runs/28450565217) |
| Toil | [![Toil Tests](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/openwdl/wdl/preemptible-hint/shields/toil.json)](https://github.com/openwdl/wdl/actions/runs/29525409350) |
| Cromwell | [![Cromwell Tests](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/openwdl/wdl/preemptible-hint/shields/cromwell.json)](https://github.com/openwdl/wdl/actions/runs/29465517910) |


The **Workflow Description Language (WDL)** (pronounced as _/hwɪdl/_ or "whittle" with a 'd') is an open standard for describing data processing workflows using a human-readable/writeable syntax.
Expand Down
69 changes: 67 additions & 2 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ Revisions to this specification are made periodically in order to correct errors
- [Hardware Accelerators (`gpu` and `fpga`)](#hardware-accelerators-gpu-and--fpga)
- [`disks`](#disks)
- [`max_retries`](#max_retries)
- [✨ `preemptible_only`](#-preemptible_only)
- [`return_codes`](#return_codes)
- [Hints Section](#-hints-section)
- [Hints-scoped types](#hints-scoped-types)
Expand All @@ -124,6 +125,7 @@ Revisions to this specification are made periodically in order to correct errors
- [`localization_optional`](#localization_optional)
- [`inputs`](#inputs)
- [`outputs`](#outputs)
- [✨ `preemptible`](#-preemptible)
- [Compute Environments](#compute-environments)
- [Conventions and Best Practices](#conventions-and-best-practices)
- [🗑 Runtime Section](#-runtime-section)
Expand Down Expand Up @@ -5785,11 +5787,13 @@ Test config:
* Default value: `0`
* Alias: `maxRetries`

The `max_retries` attribute specifies the maximum number of times a task should be retried in the event of failure. The execution engine must retry the task at least once and up to (but not exceeding) the specified number of attempts.
The `max_retries` attribute specifies the maximum number of times a task should be retried following a failure other than preemption. The execution engine must retry the task at least once and up to (but not exceeding) the specified number of attempts.

A retry following preemption does not count against `max_retries`. If the engine honors the [`preemptible`](#-preemptible) hint, that hint governs the number of preemptible attempts. Starting a non-preemptible attempt after exhausting those attempts does count as one retry.

The execution engine may choose to define an upper bound (>= 1) on the number of retry attempts that it permits.

A value of `0` means that the task as not retryable, and therefore any failure in the task should never result in a retry by the execution engine, and the final status of the task should remain the same.
A value of `0` means that a failure other than preemption must not result in a retry. It does not prevent retries on preemptible instances following preemption, but it prevents the engine from starting a non-preemptible attempt after the `preemptible` attempts are exhausted.

```wdl
task max_retries_test {
Expand All @@ -5800,6 +5804,32 @@ task max_retries_test {
}
```

##### ✨ `preemptible_only`

* Accepted type: `Boolean`
* Default value: `false`

A preemptible instance is a compute instance that the execution environment may reclaim before the task completes. Reclamation of an instance in this way is a *preemption*, which is distinct from a failure of the task's command.

When `preemptible_only` is `false`, this requirement has no effect. When `preemptible_only` is `true`, the execution engine must run every attempt on a preemptible instance. If the engine cannot provide a preemptible instance, the task must fail before the command is instantiated.

This requirement does not require the engine to honor the [`preemptible`](#-preemptible) hint. If the engine honors that hint and exhausts the suggested number of preemptible attempts, the task fails because this requirement prohibits a non-preemptible attempt. If the hint is absent or ignored, the engine determines whether to retry after preemption. A retry following preemption does not count against [`max_retries`](#max_retries), while a retry following any other failure does.

```wdl
version 1.4

task preemptible_only_example {
command <<< echo "hello" >>>

requirements {
preemptible_only: true
max_retries: 2
}
}
```

Every attempt must run on a preemptible instance. The engine determines whether to retry after preemption, and such retries do not count against `max_retries`. Independently, up to two attempts that fail for reasons other than preemption may be retried on preemptible instances.

##### `return_codes`

* Accepted types:
Expand Down Expand Up @@ -6152,6 +6182,39 @@ Reserved input-specific attributes:

Provides output-specific hints. Each key must refer to a parameter defined in the task's [`output`](#task-outputs) section. A key may also use dotted notation to refer to a specific member of a struct output.

##### ✨ `preemptible`

* Accepted type: `Int`
* Default value: `0`
Comment thread
claymcleod marked this conversation as resolved.

Suggests the maximum number of attempts that may use a [preemptible instance](#-preemptible_only). An execution engine may ignore this hint. When [`preemptible_only`](#-preemptible_only) is `false`, an engine that ignores the hint may run the initial attempt on a non-preemptible instance. This does not consume a retry permitted by [`max_retries`](#max_retries).

When an engine honors this hint, an attempt that ends in preemption counts against `preemptible` and does not count against `max_retries`. An attempt that ends in any other failure counts against `max_retries` and does not count against `preemptible`, regardless of the instance type on which it ran. The two limits are independent.

When the engine honors this hint and `preemptible_only` is `false`, the engine must not start another preemptible attempt once `preemptible` attempts have ended in preemption. If at least one retry remains under `max_retries`, the engine must use one retry to continue on a non-preemptible instance. If no retry remains, the task fails. In particular, `max_retries: 0` never permits a non-preemptible attempt after an attempt ends in preemption, but an engine may still ignore the hint and run the initial attempt on a non-preemptible instance.

A value of `0` suggests that the task should not run on a preemptible instance.

When [`preemptible_only`](#-preemptible_only) is `true`, the engine must run every attempt on a preemptible instance even if this hint is absent, ignored, or has a value of `0`. If the engine honors a positive value for this hint and exhausts the suggested number of attempts, the task fails because `preemptible_only` prohibits a non-preemptible attempt.

```wdl
version 1.4

task preemptible_example {
command <<< echo "hello" >>>

requirements {
max_retries: 2
}

hints {
preemptible: 3
Comment thread
claymcleod marked this conversation as resolved.
}
}
```

An engine may ignore the hint and run the initial attempt on a non-preemptible instance. An engine that honors the hint may run up to three attempts on a preemptible instance. If all three are preempted, none of those preemptions count against `max_retries`. The engine then uses one of the two retries to continue on a non-preemptible instance. One retry remains if that attempt fails for a reason other than preemption.

#### Compute Environments

The `hints` section should be used to provide hints that are specific to different compute environments such as HPC systems or cloud platforms. Attributes for a compute environment should be specified in a `hints` value, in which any of the [reserved hints](#reserved-task-hints) are allowed to override the values specified at the task level (if any), and other attributes are platform-specific.
Expand Down Expand Up @@ -6349,6 +6412,8 @@ This information is provided by the `task` variable, which is implicitly defined
* `fpga`: An `Array[String]` with one specification per allocated FPGA. The specification is execution engine-specific. If no FPGAs were allocated, then the value must be an empty array.
* `disks`: A `Map[String, Int]` with one entry for each disk mount point. The key is the mount point and the value is the initial amount of disk space allocated, in bytes. The execution engine must, at a minimum, provide one entry for each disk mount point requested, but may provide more. The amount of disk space available for a given mount point may increase during the lifetime of the task (e.g., autoscaling volumes provided by some cloud services).
* `max_retries`: An `Int` with the maximum number of retry attempts.
* `preemptible` ✨: An `Int` with the maximum number of attempts that may use a preemptible instance.
* `preemptible_only` ✨: A `Boolean` indicating whether the task may run only on preemptible instances.
* `attempt`: An `Int` with the current task attempt. The value must be `0` the first time the task is executed, and incremented by `1` each time the task is retried (if any).
* `previous`: A [hidden type](#taskprevious-hidden-scoped-type) containing the computed requirements from the previous task attempt. All fields are `None` on the first try.
* `end_time`: An `Int?` whose value is the time by which the task must be completed, as a [Unix time stamp](https://en.wikipedia.org/wiki/Unix_time). A value of `0` means that the execution engine does not impose a time limit. A value of `None` means that the execution engine cannot determine whether the runtime of the task is limited. A positive value is a guarantee that the task will be preempted at the specified time, but is *not* a guarantee that the task won't be preempted earlier.
Expand Down
2 changes: 1 addition & 1 deletion shields/cromwell.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"label": "Cromwell / WDL 1.4",
"message": "63/176 passed",
"message": "59/172 passed",
"color": "red"
}
2 changes: 1 addition & 1 deletion shields/toil.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"label": "Toil / WDL 1.4",
"message": "137/172 passed",
"message": "136/172 passed",
"color": "yellow"
}