Recipe YAML Format

PyPTS accepts recipe language 2.0.0. Exact fields, types, required status, defaults, aliases, and discriminator values are generated from the production models in the Recipe Language 2.0 Reference. The downloadable JSON Schema describes the same contract.

A complete maintained example is included below. This file is parsed by the test suite with the production parser.

Complete recipe-language 2 example
# SPDX-FileCopyrightText: 2026 CERN <home.cern>
# SPDX-License-Identifier: CC-BY-SA-4.0
---
name: Recipe language 2 documentation example
version: "1.0"
recipe_version: 2.0.0
description: Demonstrate canonical version 2 syntax and typed mappings.
main_sequence: Main
continue_on_error: false
report: overwrite
report_name_include_serial: true
test_package: acceptance.tests
globals:
  target: 12
  saved_result: null
---
sequence_name: Main
description: Run a measurement and then the calibration sequence.
parameters: {}
outputs: {}
locals:
  expected: 12
  measured: null
setup_steps: []
steps:
  - steptype: PythonModuleStep
    step_name: Measure channels
    description: Exercise every input mapping and representative outputs.
    id: measure-channels
    skip: false
    critical: true
    continue_on_error: false
    action_type: method
    module: measurements.py
    method_name: measure
    input_mapping:
      channels: {type: direct, value: [0, 1], indexed: true}
      expected: {type: local, local_name: expected}
      target: {type: global, global_name: target}
      transform: {type: method, value: normalize}
    output_mapping:
      passed: {type: passfail}
      exact: {type: equals, value: 12}
      bounded: {type: range, min: 10, max: 14}
      measured: {type: local, local_name: measured}
      saved: {type: global, global_name: saved_result}
      chart: {type: image}
  - steptype: SequenceStep
    step_name: Calibrate
    description: Run a nested sequence and use its aggregate verdict.
    sequence: {type: internal, name: Calibration}
    input_mapping: {}
    output_mapping:
      result: {type: passthrough}
      stored: {type: local, local_name: measured}
teardown_steps: []
---
sequence_name: Calibration
description: Wait for the equipment to stabilize.
parameters: {}
outputs: {}
locals: {}
setup_steps: []
steps:
  - steptype: WaitStep
    step_name: Stabilize
    description: Wait before returning to the caller.
    input_mapping:
      wait_time: {type: direct, value: 1}
    output_mapping: {}
teardown_steps: []

Multi-document structure

A recipe is one YAML stream containing multiple documents, separated by ---:

  • The first document is a RecipeHeader. Its required recipe_version is 2.0.0 and main_sequence names an existing sequence.

  • Every later document is a Sequence. Sequence names must be unique, and at least one sequence is required.

  • Each sequence owns setup_steps, steps, and teardown_steps lists. Step definitions use one of the exact, case-sensitive canonical discriminators listed in the generated reference.

Validation is strict. Unknown fields, wrong scalar types, duplicate YAML keys, unsafe YAML tags, missing required fields, invalid sequence references, and unsupported versions produce diagnostics. PyPTS does not repair or silently normalize legacy syntax.

Variables and mappings

globals belong to the recipe header and are available throughout the run. locals belong to one active sequence. parameters and outputs are currently reserved metadata dictionaries; the runtime does not automatically bind nested-sequence inputs or outputs from them.

Every input mapping has an explicit type:

input_mapping:
  literal: {type: direct, value: 12}
  repeated: {type: direct, value: [1, 2], indexed: true}
  from_local: {type: local, local_name: expected}
  from_global: {type: global, global_name: target}
  callback: {type: method, value: normalize}

The exact input structures are DirectInput, LocalInput, GlobalInput, and MethodInput. Indexed direct inputs must contain lists, and all indexed inputs on one step must have equal lengths.

Output mappings also require an explicit type. passfail, equals, and range contribute verdicts; all configured verdict checks must pass. passthrough consumes an already-computed result and must be the only verdict mapping on a step. local and global store values, while image publishes report images. See the generated PassFailOutput through RangeOutput definitions for exact fields.

Runtime-specific behavior

PythonModuleStep resolves module relative to test_package when the header supplies that package. Without test_package, the runtime uses its file-based module lookup. A method action requires method_name.

SequenceStep references another sequence in the same YAML stream. WaitStep requires a wait_time input. SSH steps require the connection globals documented by the parser and must follow connect, upload, close order. Exact step-specific fields are linked from the generated reference, beginning with PythonModuleStep.

Error handling applies to execution errors, not failed verdicts. A header-level continue_on_error overrides step-level values when present; critical errors still stop execution. Teardown steps run during sequence cleanup.

Serialization and readable examples

recipe_to_yaml() accepts a validated aggregate recipe definition and returns deterministic multi-document YAML text without performing file I/O. It emits aliases and model defaults, omits None, and always writes explicit document separators. Parsing that output recreates an equal aggregate definition.

Serialization is formatting-destructive: comments, quoting choices, key layout, and other source formatting are not retained. Maintained examples are therefore kept readable by hand and may include comments. YamVIEW and programmatic serialization produce normalized YAML instead.

Migrating version 1 recipes

Version 1 is rejected; there is no automatic migration command or runtime compatibility path. To migrate a file:

  1. Set the required header field to recipe_version: 2.0.0.

  2. Use exact canonical step names such as PythonModuleStep, WaitStep, and UserInteractionStep. Lowercase spellings are invalid.

  3. Add type: direct to every literal input mapping that previously relied on the implicit default. All input and output mappings are discriminated explicitly.

  4. Remove sequence-level serial_number. Use SerialNumberStep and a mapped global or local value when the run needs a serial number.

  5. Add every now-required field, including header, sequence, and step descriptions, and remove unknown fields.

  6. Validate the complete multi-document file. Strict validation reports all detected migration issues with paths and source positions; PyPTS never changes legacy spelling or meaning silently.

After migration, parse the source, serialize with recipe_to_yaml(), and parse again. Equal aggregate definitions establish semantic stability; byte equality and preservation of source comments are intentionally not required.