Lifecycle hooks

A workflow has at most one <lifecycle>. Hook source order does not matter; WOML normalizes semantic order.

HookOptional steps filterRuns when
<on-start>NoRun is durably admitted, before the business DAG.
<on-step-start>YesLogical step begins before its first attempt.
<on-step-success>YesLogical step eventually succeeds.
<on-step-failure>YesLogical step exhausts attempts.
<on-step-complete>YesLogical step settles as success or failure.
<on-success>NoBusiness outcome is success.
<on-error>NoBusiness outcome is failure, including workflow timeout.
<on-cancel>NoDurable cancellation wins.
<on-complete>NoOutcome hook settles and the run is finalizing.

Each hook may occur once and contains one or more source-ordered <script> or <notify> actions. Step filters are whitespace-separated step IDs; omission means every executable step, including nested steps.

Lifecycle scripts receive normal script bindings plus:

JavaScript
lifecycle.event
lifecycle.workflow.id
lifecycle.workflow.outcome
lifecycle.step?.id
lifecycle.step?.outcome
lifecycle.step?.attempts
lifecycle.failure?.code
lifecycle.failure?.message

They cannot create context.steps values, choose a branch, recover a step, or rewrite the workflow result. Failures become durable lifecycle warnings. The run finalizes with lifecycle status completed or completed_with_warnings.

Reusable step/provider definitions accept only <on-success>, <on-error>, and <on-complete>. Their hooks are observational and script-only.