v0.5.0 — correctness and liveness hardening
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 aWorkflowException. - 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:sweeprecovers runs stranded by hard-killed workers (OOM, SIGKILL, deploys) or lost dispatches — re-dispatching from the checkpoint, or marking failed persweep.action. Schedule it every few minutes; see the new README Operations section.$run->cancel()from any non-terminal state: resolves open interrupts, firesWorkflowCancelled, 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.