Skip to main content
Templates are the building blocks of every Aether workflow. A spec.templates array holds all the templates that a workflow can reference, and each template is a union type: exactly one of dag, task, or loop must be set. This design keeps templates composable — a DAG can reference a Loop template as one of its tasks, which in turn can run an inner DAG as its body, up to the configured nesting depth.

Template as a union type

The helper methods GetName(), GetInputs(), GetOutputs(), GetTimeout(), and GetExecutor() delegate to whichever sub-type is set, so the engine can inspect templates uniformly.
The validator rejects any template where zero or more than one of dag/task/loop is set. Every template must have exactly one.

DAG template

A DAG template ("dag") defines a directed acyclic graph of tasks. It is the primary composition mechanism — the entrypoint of a workflow is almost always a DAG.

Tasks within a DAG

Each entry in dag.tasks is a Task struct operating as a call site — it references a template by name (or declares an inline executor) and passes arguments:
Each DAG task must have either a template reference or an inline executor. Both at the same time, or neither, is a validation error.

How dependencies create the DAG graph

The dependencies field on each task lists the names of other tasks in the same DAG that must reach a terminal phase before this task is eligible to run. A task with no dependencies is a root node and starts immediately.

DAG outputs

A DAG template can declare its own outputs that lift individual child task outputs into the parent scope:

when conditions

The when field on a DAG task is a boolean expression evaluated just before the task would be dispatched. If it evaluates to false, the task transitions to PhaseSkipped and any downstream tasks that depend on it are unblocked (the dependency is treated as satisfied):

continueOn

The ContinueOn policy allows a DAG to proceed even when a task fails, errors, or times out:

entrypoints

By default the engine starts all tasks that have no dependencies. entrypoints (a single name or a list) restricts the initial dispatch to a named subset, useful when a DAG has multiple independent roots but you want to control which ones start first.

Task template

A Task template ("task") is a leaf node — it delegates execution to a named executor plugin and cannot contain other templates.

The executor field

executor.type is a string that the engine’s broker uses to look up the registered executor.Plugin implementation. The engine itself never executes task logic — that is the sole responsibility of the matched plugin.

Retry policy

limit sets the maximum retry count. When expression is omitted, only Error and Timeout phases trigger a retry. With a custom expression, the retry fires only when it evaluates to true; the expression can reference tasks.<name>.phase, tasks.<name>.code, tasks.<name>.msg, and output parameters.
Only leaf Task templates support retry. DAG and Loop containers do not.

Task template example


Loop template

A Loop template ("loop") iterates over a set of items, running a body template for each one. It supports three mutually exclusive iteration modes and parallel execution control.
body is required and must reference an existing template by name — this is the template executed for each iteration.

Three iteration modes

items is a static JSON array. Each element is one iteration’s item value. Scalar elements are available as loop_iter.item; object elements are expanded into loop_iter.<field>:

Aggregate strategies

When a loop completes, its body template outputs from all iterations are merged into the loop’s own outputs using an Aggregate policy:
Parameters narrows which output parameter names are included in the aggregate. Omit it to include all declared outputs.
With strategy: "list", each parameter’s value across all iterations is collected into a JSON array ordered by iteration index.

Template composability

Templates compose by name reference. A DAG task sets "template": "<name>" to invoke any template in the flat spec.templates list. This means:
  • A DAG can contain tasks that run other DAGs (nested DAGs)
  • A DAG can contain tasks that run Loop templates
  • A Loop’s body can reference a DAG template, which itself can contain Loops
The maximum static nesting depth is controlled by spec.maxNestedDepth (default 3, absolute ceiling 10). The validator walks the entire template reference tree from the entrypoint and rejects workflows that exceed this limit before they are submitted.
Outputs from a nested DAG propagate upward: the inner DAG declares its own outputs.parameters using valueFrom references to its child task outputs. The parent scope then reads those via tasks.<node-name>.outputs.parameters.<param>, exactly as it would for a leaf task. See Parameter Binding for the complete data-flow model.