Workflows
A workflow combines a versioned document with executable catalog definitions. The document owns topology; TypeScript step definitions own runtime behavior.
Define a workflow
import { workflow } from "@dromio/workflow";
import { z } from "zod";
// Excerpt: these step definitions are created with step() or step.model().
export const responseReview = workflow({
catalog: [generateResponse, saveReview],
document: new URL("./response-review.workflow.json", import.meta.url),
input: {prompt: z.string().min(1)},
output: {reviewPath: z.string().min(1)},
});
document accepts a WorkflowDocument object, a filesystem path, or a URL.
The returned workflow exposes start(), hydrate(), graph(), configure(),
its parsed document and catalog, contracts, child sources, and workspace.
workflow is callable and also owns bundled workflow builders:
workflow.judge()runs one assessor and one deterministic gate.workflow.judgeUntil()adds bounded revision and repetition.workflow.clarifyUntil()adds focused durable human questions and accepted-answer provenance.
Each builder returns an independently runnable workflow. Place it in a parent
with step.workflow() only when the parent needs that child boundary.
Complete workflow document
This is the minimum useful JSON shape:
{
"id": "response-review",
"label": "Review a response",
"description": "Generate and save one reviewed response.",
"version": 1,
"trigger": {
"id": "request",
"type": "manual",
"label": "Review request",
"input": {
"prompt": {"jsonSchema": {"type": "string", "minLength": 1}}
}
},
"nodes": [
{
"id": "generate",
"catalogItemId": "responses.generate",
"label": "Generate response"
},
{
"id": "save",
"catalogItemId": "responses.save-review",
"label": "Save review"
}
],
"edges": [
{"id": "request-to-generate", "source": "request", "target": "generate"},
{"id": "generate-to-save", "source": "generate", "target": "save"},
{"id": "save-to-result", "source": "save", "target": "result"}
],
"end": {
"id": "result",
"type": "result",
"label": "Review ready",
"output": {
"reviewPath": {"jsonSchema": {"type": "string", "minLength": 1}}
}
}
}
Every node catalogItemId must exist in the supplied catalog. Trigger and end
contract keys must agree with the TypeScript input and output when both are
declared. Every visible node must be reachable from the trigger and able to
reach the end. Validation errors are reported during workflow() compilation.
end is the document field, type: "result" declares the boundary kind, and
the boundary's id is user-defined ("result" here). They need not share a
name.
Bind placement keys
By default a step reads and writes its declared keys. Use node bindings when a placement needs different workflow-state names:
{
"id": "summarize-source",
"catalogItemId": "notes.summarize",
"bindings": {
"input": {"note": "sourceText"},
"output": {"summary": "sourceSummary"}
}
}
Input bindings map the step key to an existing state key. Output bindings map the step key to the new state key.
Loops and explicit control flow
Documents may declare loop regions with id, start, end, and backTo.
Steps return goto(stepId) for a visible non-linear transition. Keep ordinary
dependencies as edges and use router, fork, and iteration primitives for typed
child-workflow composition.
Child workflows
Composite step definitions carry their child sources. You may also pass
workflows explicitly when compilation needs nested documents:
// Excerpt: these workflows are compiled with workflow().
workflow({
catalog: [evaluateResponse, reviewResponse],
document,
workflows: [
evaluateResponse.workflow,
assessResponseWorkflow,
analyzeNoteWorkflow,
],
});
Register actual workflow objects rather than rebuilding factories or copying child documents into the parent.
Configuration
Provide a typed config default when authoring, then compile another placement
configuration with configure():
const configured = responseReview.configure({prefix: "Reviewed"});
const session = await configured.start({prompt: "Explain traces"});
configure() returns a compiled executable workflow but does not create a new
authored definition, document, or catalog identity.
Run directly
const session = await responseReview.start({prompt: "Explain traces"});
if (session.status === "completed") {
console.log(session.state.reviewPath);
}
See Execution and durability before deploying waits or external side effects.