03 — Dependencies & parallelism
The dependencies list on each Task defines
the DAG edges. Tasks with no dependency between them run in parallel.
fetch → (transform_a, transform_b in parallel) → combine.
#[workflow(name = "diamond", description = "Fan out then fan in")]
pub mod diamond {
use super::*;
#[task]
pub async fn fetch(ctx: &mut Context<serde_json::Value>) -> Result<(), TaskError> { Ok(()) }
#[task(dependencies = ["fetch"])]
pub async fn transform_a(ctx: &mut Context<serde_json::Value>) -> Result<(), TaskError> { Ok(()) }
#[task(dependencies = ["fetch"])]
pub async fn transform_b(ctx: &mut Context<serde_json::Value>) -> Result<(), TaskError> { Ok(()) }
#[task(dependencies = ["transform_a", "transform_b"])]
pub async fn combine(ctx: &mut Context<serde_json::Value>) -> Result<(), TaskError> { Ok(()) }
}
with cloaca.WorkflowBuilder("diamond") as builder:
builder.description("Fan out then fan in")
@cloaca.task()
def fetch(context): return context
@cloaca.task(dependencies=["fetch"])
def transform_a(context): return context
@cloaca.task(dependencies=["fetch"])
def transform_b(context): return context
@cloaca.task(dependencies=["transform_a", "transform_b"])
def combine(context): return context
transform_a and transform_b both depend only on fetch, so the engine runs
them concurrently; combine waits for both. The DAG is validated when the
workflow is built — cycles and missing tasks are rejected.
- 04 — Error handling & retries
- Explanation: Trigger Rules