Concepts · Sub-workflows

Sub-workflows

A sub-workflow is a workflow invoked by another workflow. A Workflow Template can declare a step that runs a second template as a child Run, producing its own Artifact and rolling its cost up into the parent. This is how Esy keeps artifacts reusable instead of re-deriving the same work inside every workflow.

Why composition

Some artifacts are inputs to others. A research report is valuable on its own, and it is also the substrate for a research infographic, a brief, or a slide. Rather than re-implement research inside each of those workflows, the report is authored once as generate-research-report and composed by the others. The child run is a first-class Run: it has its own steps, telemetry, cost, artifact, and review state.

The subWorkflow step

Composition is declared as a runtime step with kind: "subWorkflow". The step pins a child template id and version and supplies an intakeMapping — a map from parent context paths to the child template’s intake field names.

runtime stepjson
{
  "id": "step-1",
  "name": "Research report",
  "kind": "subWorkflow",
  "subWorkflow": {
    "templateId": "generate-research-report",
    "templateVersion": "2026.05.29",
    "intakeMapping": {
      "intake.topic": "topic",
      "intake.audience": "audience"
    }
  }
}
FieldPurpose
templateIdThe child Workflow Template to invoke.
templateVersionPinned child version. Composition is reproducible against the exact child definition used.
intakeMappingMaps a parent context path (for example intake.topic) to a child intake field name (for example topic). The engine resolves each path against the parent run context and passes the result as the child’s intake.

Parent and child runs

When the engine reaches a subWorkflow step, it creates a child run linked to its parent by parentRunId and parentStepId. The child executes to completion and persists its own artifact; the parent step records the childRunId so the two are navigable in both directions in the app.

run shapetree
Parent run  (generate-research-infographic)
   ├── step-1  subWorkflow ─────────────┐
   │                                    ▼
   │                            Child run  (generate-research-report)
   │                               └── artifact: research-report
   ├── step-2  llm   (compose infographic prompt)
   └── step-3  image (render infographic)
          └── artifact: infographic
                 └── sourceReportArtifactId → research-report

The downstream artifact links back to the artifact the sub-workflow produced. An infographic composed from a report carries sourceReportArtifactId, so a reader can open the underlying research artifact from the visual.

Cost rollup

A child run captures its own provider cost the same way any run does. The parent run’s total is the sum of its own step costs plus the totals of every child it spawned, so a composed workflow reports the full cost of producing its artifact — not just the parent’s steps.

rolluptree
parent run total
   = parent's own step costs (llm + image)
   + Σ child run totals (each child's own step costs)

Cost estimation mirrors this at planning time: estimating a template that contains a subWorkflow step resolves the pinned child template and adds its estimate to the parent’s, so a budget pre-check sees the true per-run cost before launch.

Depth and cycles are bounded

Sub-workflow composition is guarded against runaway depth and cycles. A template cannot compose itself, directly or transitively, and nesting is capped. This keeps a single operator action from fanning out into an unbounded tree of runs and cost.

Related concepts

  • Workflow templates — declare the subWorkflow step.
  • Runs — child runs link to their parent via parentRunId.
  • Artifacts — composed artifacts back-reference their source.
  • Costs and Budgets — child cost rolls up into the parent and into pre-run estimates.