Skip to main content

Two Entry Points

// Named shortcuts — intent obvious from the first word
Workflow.sequential("pipeline").step(a).then(b).then(c).run(input);
Workflow.loop("refine").step(refineStep).times(5).run(draft);
Workflow.parallel("gather").step(review, audit, ciCheck).run(event);

// Full DSL — for gates, branches, decisions, error paths
Workflow.define("complex-flow")
    .step(fetch)
    .then(analyze)
    .gate(judgeGate).onPass(approve).onFail(revise).end()
    .run(event);

// Supervisor — LLM autonomously delegates to sub-agents
Workflow.supervisor("delegate", routingClient)
    .agents(codeReview, securityAudit, docUpdate)
    .until(result -> result.score() >= 0.9)
    .run(event);
Both surfaces produce a WorkflowGraph and support .run(input).

The Primitives

Sequential

Chain steps — output flows forward:
Workflow.define("pipeline")
    .step(write)
    .then(editForAudience)
    .then(editForStyle)
    .run(input);

Branch (predicate routing)

Route based on output:
Workflow.define("router")
    .step(classify)
    .branch(output -> "medical".equals(output))
        .then(medicalExpert)
        .otherwise(legalExpert)
    .run("I broke my leg");

Loop (while-do)

Exit condition checked before body:
Workflow.loop("refine")
    .step(refineStep)
    .until(result -> result.score() >= 0.8)
    .run(draft);

Loop (do-while / repeatUntilOutput)

Body runs first, exit condition reads actual output:
Workflow.define("score-loop")
    .repeatUntilOutput(score -> score instanceof Double d && d >= 0.8)
        .step(editor)
        .step(scorer)
    .end()
    .run(initialInput);

Parallel (static fan-out)

Run steps concurrently, collect results:
List<Object> results = (List<Object>) Workflow.define("gather")
    .parallel(findMeals, findMovies)
    .run("romantic");

Parallel (dynamic fan-out)

Fan-out determined at runtime:
Workflow.define("batch")
    .parallel(
        ctx -> loadItems(),                    // items supplier
        item -> Step.named("process", (c, i) -> processItem(i))  // step factory
    )
    .run(null);

Decision (LLM-routed)

LLM picks which step to run:
Workflow.define("decision-router")
    .decision(chat)
        .option("summarize", summarizeStep)
        .option("translate", translateStep)
    .end()
    .run("The quick brown fox...");

Gate (quality checkpoint)

Approve, reject, or retry with feedback:
Workflow.define("gated-pipeline")
    .step(generate)
    .gate(new JudgeGate(jury, 0.8))
        .onPass(approve)
        .onFail(revise)
        .withReflector(reflectorStep)   // transforms verdict → feedback
        .maxRetries(3)
    .end()
    .run(input);
On failure, the full Verdict (score, reasoning, per-judge judgments) is written to AgentContext under JUDGE_VERDICT for the retry step to consume.

Supervisor (autonomous delegation)

LLM picks sub-agents each iteration:
Workflow.supervisor("text-improver", chat)
    .agents(review, edit, format)
    .until(ctx -> ctx.get(AgentContext.ITERATION_COUNT).orElse(0) >= 3)
    .run(roughDraft);

Error Recovery

Route exceptions to recovery steps:
Workflow.define("resilient")
    .step(riskyStep)
        .onError(TimeoutException.class, retryWithBackoff)
    .then(finalStep)
    .run(input);

BackTo (cyclic back-edge)

Jump back to an earlier step when a condition is met — a lightweight escape hatch for retry patterns:
Workflow.define("rebase-loop")
    .step(rebase)
    .step(runTests)
    .backTo("rebase", result -> !"PASS".equals(result))
    .step(merge)
    .run(branch);
backTo creates an EdgeCondition.BackEdge in the graph IR — a real edge, visible in traces. The next .step() chains from the same node (not the back-edge target), so merge runs when the predicate returns false. Use RunOptions.maxIterations() as a circuit breaker to prevent infinite loops:
.run(input, RunOptions.maxIterations(10));
When to use backTo vs repeatUntil: repeatUntil creates structured loop nodes (entry → body → check → exit). backTo is a single edge — no extra nodes, no loop wrapper. Use it when you need a quick “retry this section” without the overhead of a full loop construct.

Terminate

Exit early from anywhere:
Steps.terminate(WorkflowStatus.FAILED, "Quality below threshold")

Step Types

Steps are the atomic unit. Each wraps a different kind of execution:
Step TypeWhat It WrapsExecution Time
Step.named("n", lambda)Any lambdaDepends
ChatClientStep.of(chat, template)Single Spring AI callSeconds
ClaudeStep.of(template)Full Claude CLI agent sessionMinutes
AgentClientStep.of(client, template)AgentClient abstractionMinutes
A2AStep.of(url)Remote A2A agentMinutes
Steps.of(fn)Pure deterministic functionMilliseconds
Steps.retrying(n, step)Retry wrapperPer attempt
Steps.outputOf("step-name")Read prior step’s outputInstant
The key differentiator: ClaudeStep runs a full agentic loop internally — many turns, many tool calls, minutes of execution. The workflow sees it as one step. This is the opposite of frameworks where each LLM API call is a separate workflow activity.

Composability

Workflows implement Step<I, O>. Nest them freely:
var inner = Workflow.<String, String>define("inner")
    .step(analyze).then(classify).build();

Workflow.define("outer")
    .step(inner)        // workflow-as-step
    .then(report)
    .run(input);

RunOptions

Runtime constraints — not DSL verbs, hints to the executor:
Workflow.define("bounded")
    .step(expensiveStep)
    .run(input, RunOptions.maxCost(5.0)
        .withMaxIterations(50)
        .withMaxDuration(Duration.ofMinutes(10)));

The Graph IR

Every primitive compiles to real nodes and typed edges — not opaque lambdas:
  • A branch is 4 nodes (gateway → then-step, else-step → join) + 4 edges
  • A loop has a back-edge from body to entry
  • A parallel has fork and join nodes with BranchIndex edges
This makes workflows inspectable, traceable, and replayable. The TraceRecorder sees every transition; Markov analysis works on the edge data.
WorkflowGraph<String, String> graph = Workflow.define("my-flow")
    .step(a).then(b).compile();

graph.nodes()     // List<WorkflowNode> — sealed interface
graph.edges()     // List<WorkflowEdge> — typed conditions

Getting Started

Install, first workflow, four-layer architecture

Complete Examples

8 runnable integration tests validated against GPT-4.1