Writing Workflows

This page explains how to create and structure Kuristo workflow files using YAML syntax.

Basic Structure

A Kuristo workflow defines a set of jobs, each identified by a unique job ID. Jobs contain one or more steps to execute.

Example of a workflow file

jobs:
  job1:
    name: simulation
    steps:
      - run: ./prepare.sh
      - run: ./simulate --input data.in
      - run: ./postprocess.sh
jobs.<id> (string, required)

Job ID that is unique within the workflow file

jobs.<id>.name (string, optional)

Descriptive job name shown in logs and reports

jobs.<id>.steps (list, required)

Commands or structured actions to run

Step Fields

Each step represents a unit of work (e.g., a script or an action).

Example of running a shell command

jobs:
  mesh:
    name: Generate mesh
    steps:
      - run: ./mesh.sh
        working-directory: scripts/
jobs.<id>.steps[*].run (string)

Shell command to execute

jobs.<id>.steps[*].working-directory (string, optional)

Directory to run the command in

Example of running an action

jobs:
  mesh:
    name: Generate mesh
    steps:
      - uses: my-action/execute
        with:
          input: input_file.txt
jobs.<id>.steps[*].uses (string)

The name of the action to execute

jobs.<id>.steps[*].with:

Specify parameters that are used by the action

Job Dependencies

Use the needs field to create dependencies between jobs. This controls execution order.

Example of setting dependencies

jobs:
  prep:
    name: Prepare
    steps:
      - run: ./prepare_inputs.sh

  sim:
    name: Run Simulation
    needs: [prep]
    steps:
      - run: ./simulate
jobs.<id>.needs (list of job IDs, optional)

Name of the job that must finish before this job starts

Jobs without dependencies may run in parallel, depending on available system resources.

Strategy Matrix

Use jobs.<id>.strategy.matrix to create multiple job variations automatically. Each combination runs as a separate job. This is useful for running the same test or simulation with different parameters, configurations, or mesh sizes.

Cartesian Product

Create jobs from all combinations of matrix variables:

jobs:
  convergence-study:
    strategy:
      matrix:
        mesh_size: [coarse, medium, fine]
        solver: [direct, iterative]
    steps:
      - run: ./simulate --mesh ${{ matrix.mesh_size }} --solver ${{ matrix.solver }}
      - uses: checks/exodiff
        with:
          input: results.e
          gold: gold/results_${{ matrix.mesh_size }}_${{ matrix.solver }}.e

This creates 6 job variations (3 mesh sizes × 2 solvers), each running independently.

Explicit Combinations

Use include to specify exact combinations when not all are valid or needed:

jobs:
  verification:
    strategy:
      matrix:
        include:
          - model: steady_state
            preconditioner: ilu
          - model: transient
            preconditioner: ml
          - model: transient
            preconditioner: ilu
            slow: true
    steps:
      - run: ./verify --model ${{ matrix.model }} --prec ${{ matrix.preconditioner }}

This creates only the specified 3 combinations rather than all possible pairs.

Job Labels

Use the labels field to tag jobs for selective execution. This allows you to run subsets of jobs without creating separate workflow files.

Example of using labels

jobs:
  smoke-test:
    name: Smoke Test
    labels: [smoke, quick]
    steps:
      - run: ./test_smoke.sh

  integration-test:
    name: Integration Test
    labels: [integration, slow]
    steps:
      - run: ./test_integration.sh

  unit-test:
    name: Unit Tests
    steps:
      - run: ./test_unit.sh
jobs.<id>.labels (list of strings, optional)

Tags for filtering jobs during execution. Jobs can have multiple labels. See the User Interface documentation for information on filtering jobs with the --label option.