Skip to main content

The Graduation Path

Agent Workflow separates workflow definition from execution. The StepRunner interface is the seam โ€” swap the bean, not the workflow:
LevelRunnerWhat it adds
0LocalStepRunnerIn-process, zero overhead. Default.
1CheckpointingStepRunnerJDBC crash recovery โ€” resume from last completed step
2TemporalStepRunnerDistributed durable execution via Temporal activities
Same workflow code at every level:

CheckpointingStepRunner

Persists step outputs to a JDBC database. On restart with the same runId, completed steps are skipped โ€” their cached output is returned directly.

How it works

  1. Before executing a step, queries by (runId, stepName) โ€” the checkpoint key
  2. If a COMPLETED record exists, returns the cached outputPayload (skip)
  3. Otherwise, creates a STARTED record, executes the step, upgrades to COMPLETED with the serialized output
  4. On exception, records FAILED with the error message

Maven coordinates

Requires Spring Data JPA and a JDBC DataSource on the classpath. H2 works for development; Postgres or MySQL for production.

Restart semantics

runId is the stable identity for a workflow instance. COMPLETED steps are skipped permanently for that runId. FAILED steps are not automatically retried โ€” the system leaves them in place until an operator explicitly decides to retry. This is intentional. Not all failures are transient: a bad prompt, a schema mismatch, or a programming error will fail again without a fix. Automatic retry would mask the real problem.

Crash-and-resume with CheckpointManager

When a step fails, call CheckpointManager.getRunState() to inspect what happened, then resetFailedSteps() only after confirming the failure was transient:
resetFailedSteps deletes FAILED records; COMPLETED records are untouched. The next execution creates a fresh STARTED record for each reset step and re-runs it.

Basic crash-and-resume example

A 4-step workflow crashes at step 3. After operator reset, steps 1-2 are skipped (cached), step 3 retried:
A complete runnable example is in workflow-dsl-examples/CrashRecoveryIT โ€” @DataJpaTest + H2, no LLM needed.

JPA entities

Two JPA entities back the checkpoint system:
EntityTablePurpose
AgentStepExecutionagent_step_executionsPer-step checkpoint. Key: (runId, stepName) unique constraint. Tracks status, output, tokens, cost.
AgentFlowExecutionagent_flow_executionsPer-run envelope. Tracks workflow name, steps total/completed, total cost.
Both use BatchStatus (severity-ordered enum) and ExitStatus (embeddable record with severity-based composition via and()).

Typed output deserialization

Each checkpoint stores the stepโ€™s output type alongside its serialized payload. On restore, CheckpointingStepRunner uses Class.forName(outputType) to deserialize back to the original type rather than raw Object. This means that when a step is skipped and its cached output is returned to the next step, the type is preserved:
Steps that declare outputType() participate fully. Step.named() lambdas return Object.class by default โ€” deserialization falls back to Jacksonโ€™s type inference for those.

JdbcTraceRecorder

Records every step transition to a step_transitions table. Auto-creates the table on first use.
Each StepTransition record includes: run_id, workflow_name, from_step, to_step, timestamp, duration_ms, tokens_used, cost_usd, node_type, label, trace_path. The trace_path column stores the absolute path to the stepโ€™s JSONL trace file when using a trace-aware AgentClientStep โ€” see Trace Capture. Query traces for a run:

TemporalStepRunner

Dispatches each step as a Temporal Activity. Steps must be registered with StepActivityImpl on the worker side.

Maven coordinates

Activity dispatch

Worker-side step registration

Steps are resolved by name from a ConcurrentHashMap registry. The activity creates a fresh AgentContext with the runId for each execution.
Steps dispatched via Temporal must be idempotent โ€” Temporal may retry activities on timeout or failure.
Sub-workflows run inline, not as activities. A Workflow used as a step inside another Workflow bypasses the TemporalStepRunner and executes in-process. Only leaf steps are dispatched as Temporal activities. This is required for correct context propagation โ€” the activity worker receives only the runId, not the full parent context.

API Reference

StepRunner interface, TraceRecorder, WorkflowExecutor

DSL Primitives

Sequential, parallel, gate, loop, branch, and more