Skip to content

v0.5.0 — correctness and liveness hardening

Choose a tag to compare

@timmcleod timmcleod released this 25 Jul 12:38

This release makes the durability claims provable rather than aspirational. No API changes to workflow definitions; two additions to the run model and one new command.

Correctness (the engine's contract, now enforced and tested):

  • Idempotent step claims. Every step execution is claimed in a locked transaction that checks the run's status, the cursor, and in-flight attempts; a unique (run_id, step_id, attempt) index settles simultaneous claims. Duplicate queue deliveries no-op instead of re-executing steps.
  • Atomic transitions everywhere. Cursor advances, failures, resumes, retries, and event deliveries are all conditional on their expected source state. Two concurrent resume() calls (a double-clicked approve button) yield exactly one resumption; the loser gets a WorkflowException.
  • Documented semantics: step bodies are at-least-once; checkpoints and cursor advances are exactly-once. Keep step side effects idempotent or isolate them in their own step.

Liveness:

  • php artisan agent-workflows:sweep recovers runs stranded by hard-killed workers (OOM, SIGKILL, deploys) or lost dispatches — re-dispatching from the checkpoint, or marking failed per sweep.action. Schedule it every few minutes; see the new README Operations section.
  • $run->cancel() from any non-terminal state: resolves open interrupts, fires WorkflowCancelled, and discards an in-flight step's result at the step boundary.

Docs: full lifecycle-events list, an Operations section (queue isolation, worker timeouts, sweeper, semantics), and a fix for the evaluate() example, whose predicate previously read a step that never ran.

Schema note: adds a unique index on (run_id, step_id, attempt). Fresh installs get it via the migration; existing pre-release installs should re-migrate (migrate:fresh) or add the index manually.