ablative docs
↑↓ move↵ openesc close
Stack Notebook ablative.com.au

AWL children, waits and failure

Child and subflow contracts, durable waits, sleeps and failure handlers.

Open .md

Children and subflows

child awl 5

Declares a separate durable workflow this one may call and await.

  • form declaration
  • positions top-level
//! Call a separate durable workflow and await its result.
workflow grade_essay
  input essay: String
  outcome done: type Grade, route success

type Grade { score: Int }

child score_essay(essay: String) -> Grade

step grade
  score_essay(essay: essay) -> grade
  route done(grade)
spawn awl 5

Starts a child workflow without waiting for it, so it can bind no result.

  • form statement
  • positions step-body
//! Start a child without waiting; it can bind no result.
workflow archive_report
  input report_id: String
  outcome done: type String, route success

child notify_archived(report_id: String) -> Nil

step archive
  spawn notify_archived(report_id: report_id)
  route done(report_id)
subflow awl 5

Declares an inline reusable graph with typed parameters and exactly one outcome, compiled into this workflow.

  • form declaration
  • positions top-level
//! An inline reusable graph with typed parameters and one outcome.
workflow review_repo
  input repo: String
  outcome done: type Report, route success

type Report { summary: String }

worker reviewers
  action inspect(target: String) -> Report

subflow review_once(target: String)
  outcome out: type Report

  step look
    inspect(target: target) -> report
    route out(report)

step review
  review_once(target: repo) -> report

step publish
  report |> route done

An ordinary child call waits and binds the result. Spawn starts without waiting and cannot bind a result. The child must be deployed with a matching contract. A subflow’s invocation is the sole statement of a top-level step and may bind its outcome. It cannot sit inside a fork, loop, failure handler or substep. Subflows can nest but cannot recurse.

Wait and sleep

wait awl 10

Waits for a declared signal and binds it; a `timeout` makes that binding optional, because expiry is absence. Without one, the wait also guards a flow loop it sits on: no `max N visits` is needed.

  • form statement
  • positions step-body
//! Take delivery of the declared signal, binding its payload.
workflow hold_for_go
  input build_id: String
  signal go: Approval
  outcome done: type String, route success

type Approval { approver: String }

step hold
  wait go -> approval
  route done(approval.approver + " released " + build_id)
sleep awl 10

Sleeps for a duration on a durable timer.

  • form statement
  • positions step-body
//! Sleep on a durable timer; the run survives a restart mid-sleep.
workflow cool_down
  input build_id: String
  outcome done: type String, route success

step pause
  sleep 30s
  route done(build_id)

A signal wait uses a declared receive contract. A timed wait handles its timeout path. Sleep creates a durable timer and releases execution capacity while waiting.

Failure handling

on awl 11

Opens a step failure handler, or names a signal that arms a workloop in its header.

  • form statement
  • positions workloop-header, step-body
//! `on failure` runs when the step faults, and must end in a route.
workflow publish_package
  input name: String
  outcome done:   type String, route success
  outcome failed: type String, route failure

worker registry
  action publish(name: String) -> Nil
  action yank(name: String) -> Nil

step publish
  publish(name: name)
  route done(name)

  on failure
    yank(name: name)
    route failed("publish failed; yanked")

An on-failure block receives errors from the fallible operations it guards. Keep the handler inside the compiler’s supported statement set: an untimed wait is accepted, while a loop, fork, timed wait or child spawn in that block is refused during bytecode compilation.

The reference overview lists every additional bytecode compilation restriction.