Saga / Compensating Rollback
Some automations span several irreversible-looking steps — create a record, send
an email, move a file. If a later step fails, the already-completed steps should
be undone, but the executor’s AC_try only does try/catch/finally for one
block; nothing tracked “what to undo” across N completed steps. A Saga
records a compensating action per step and, on any failure, runs the
compensations for the completed steps in LIFO order.
Forward actions and compensations are plain callables (or, via the executor, JSON
action lists), so the orchestration is fully unit-testable with no real side
effects. Compensation is best-effort: a failing compensation is logged and the
rollback continues. Imports no PySide6.
Headless API
from je_auto_control import Saga
result = (Saga()
.step("create", create_record, delete_record)
.step("notify", send_email, None) # nothing to undo
.step("move", move_file, restore_file)
.run())
if not result.ok:
result.failed_step # which step raised
result.completed # steps that ran forward
result.compensated # steps undone (LIFO over completed)
run() returns a SagaResult (ok / completed / compensated /
failed_step / error). A step “fails” when its action raises; steps with no
compensation are simply skipped during rollback.
Executor command
AC_run_saga takes steps — a list (or JSON string) of {name, action:
[...], compensation: [...]} where each action / compensation is an
AutoControl action list. It returns {ok, completed, compensated, failed_step,
error}. The same operation is exposed as the MCP tool ac_run_saga and as a
Script Builder command under Flow.