# YamlCommandReader in Maestro: The Execution Pipeline's YAML Parser

> Discover how the YamlCommandReader transforms YAML test flows into executable MaestroCommand objects and parses configuration metadata for the Maestro execution pipeline.

- Repository: [Maestro/Maestro](https://github.com/mobile-dev-inc/Maestro)
- Tags: internals
- Published: 2026-03-20

---

**The YamlCommandReader is the entry point that transforms declarative YAML test flows into executable `MaestroCommand` objects and extracts configuration metadata for the Maestro execution engine.**

The **YamlCommandReader** serves as the critical bridge between static YAML flow definitions and Maestro's dynamic execution engine. Located in the `mobile-dev-inc/Maestro` repository, this component parses `.yaml` files into concrete command lists that the **Orchestra** engine can execute on mobile devices. Without this parser, the declarative test descriptions would remain inert text files rather than actionable device commands.

## Core Responsibilities in the Execution Pipeline

### Converting YAML Files to Command Lists

At the heart of the pipeline is the `readCommands` method in [`YamlCommandReader.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/YamlCommandReader.kt). This method accepts a `Path` to a YAML flow file and returns a `List<MaestroCommand>` that represents the sequence of actions to perform.

According to the source code in [`maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlCommandReader.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlCommandReader.kt) (lines 38–41), the method enables the transformation from declarative YAML to imperative command objects:

```kotlin
val commands = YamlCommandReader.readCommands(flowPath)

```

This list is what the `Orchestra` engine iterates over during test execution.

### Extracting Flow Configuration

The reader handles two distinct configuration extraction scenarios. First, `readConfig(Path)` parses only the optional `config` block—containing properties like `name`, `appId`, and environment variables—without processing the entire command list (lines 47–50).

Second, after parsing the full command list, `Orchestra.runFlow` retrieves the configuration via `YamlCommandReader.getConfig(commands)` to initialize the JavaScript engine and AI components (as seen in [`Orchestra.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/Orchestra.kt), lines 65–68).

```kotlin
val config = YamlCommandReader.readConfig(flowPath)      // Config-only parsing
val config = YamlCommandReader.getConfig(commands)       // From parsed commands

```

### Enabling Hot Reload with File Watching

For development workflows, the `getWatchFiles` method (lines 58–61) returns every file referenced by a flow, including the main flow file itself, `initFlow` dependencies, and media assets. This allows Maestro's file-watchers to trigger re-runs when any dependency changes.

```kotlin
val watchFiles = YamlCommandReader.getWatchFiles(flowPath)

```

### Syntax Validation and Debugging Utilities

The reader provides `checkSyntax` (lines 74–76) to validate YAML snippets without executing them, surfacing parsing errors early in the development cycle. For debugging, `formatCommands` generates a nicely formatted string representation of the command list.

```kotlin
// Throws SyntaxError with visual cues if invalid
YamlCommandReader.checkSyntax(yamlSnippet)

```

### Converting Exceptions to User-Friendly Errors

All parsing logic is wrapped in `mapParsingErrors` (lines 78–88), which transforms low-level `FlowParseException` instances into user-friendly `SyntaxError` objects. The `errorMessage` helper (lines 90–103) generates visual cues indicating the offending line in the YAML source.

## Integration with the Orchestra Execution Engine

The **YamlCommandReader** does not operate in isolation—it feeds directly into the `Orchestra` class, the core execution engine. As implemented in [`maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt), the `runFlow` method accepts the pre-parsed command list and configuration retrieved from the reader.

The CLI entry point in [`maestro-cli/src/main/java/maestro/cli/runner/TestRunner.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-cli/src/main/java/maestro/cli/runner/TestRunner.kt) demonstrates this pipeline: it loads the YAML flow using `YamlCommandReader.readCommands`, then hands the resulting list to `Orchestra` for execution on the connected device.

## Practical Code Examples

### Loading and Executing a Complete Flow

This example demonstrates the full path from file to execution:

```kotlin
import maestro.orchestra.yaml.YamlCommandReader
import maestro.orchestra.Orchestra
import maestro.Driver
import java.nio.file.Paths

// Resolve the YAML flow file
val flowPath = Paths.get("flows/login_test.yaml")

// Parse into executable commands (YamlCommandReader.kt:38-41)
val commands = YamlCommandReader.readCommands(flowPath)

// Extract configuration for environment setup
val config = YamlCommandReader.getConfig(commands)

// Execute via Orchestra (Orchestra.kt:65-68)
val orchestra = Orchestra(maestro = Driver())
val success = orchestra.runFlow(commands)

```

### Validating YAML Syntax Without Execution

Use `checkSyntax` to catch errors before running tests:

```kotlin
import maestro.orchestra.yaml.YamlCommandReader

val snippet = """
  - tapOn:
      text: "Submit"
  - assertVisible: "Success"
""".trimIndent()

// Validates and throws SyntaxError with line indicators if malformed
YamlCommandReader.checkSyntax(snippet)

```

### Collecting Dependencies for File Watching

Identify all files that should trigger re-runs during development:

```kotlin
import maestro.orchestra.yaml.YamlCommandReader
import java.nio.file.Paths

val flowPath = Paths.get("flows/checkout.yaml")
val dependencies = YamlCommandReader.getWatchFiles(flowPath)

// Returns list including the flow and referenced sub-flows/media
println(dependencies.joinToString("\n"))

```

## Key Source Files in the Architecture

- **[`maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlCommandReader.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlCommandReader.kt)**: Core parser implementing `readCommands`, `readConfig`, `getWatchFiles`, `checkSyntax`, and error mapping logic.
- **[`maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt)**: Execution engine that consumes the command list and configuration provided by the reader.
- **[`maestro-cli/src/main/java/maestro/cli/runner/TestRunner.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-cli/src/main/java/maestro/cli/runner/TestRunner.kt)**: CLI integration point that orchestrates the YAML loading and execution phases.
- **[`maestro-test/src/test/kotlin/maestro/orchestra/yaml/YamlCommandReaderTest.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-test/src/test/kotlin/maestro/orchestra/yaml/YamlCommandReaderTest.kt)**: Unit tests validating parsing accuracy, configuration extraction, and error handling.

## Summary

- The **YamlCommandReader** transforms static YAML files into executable `MaestroCommand` lists via `readCommands`.
- It extracts configuration metadata through both `readConfig` (standalone) and `getConfig` (from parsed commands) to initialize execution contexts.
- File watching capabilities rely on `getWatchFiles` to detect changes in flows and their dependencies.
- Developer tooling benefits from `checkSyntax` for early validation and `mapParsingErrors` for clear, actionable error messages.
- It serves as the entry point for the execution pipeline, bridging declarative test definitions and the imperative `Orchestra` engine.

## Frequently Asked Questions

### What does YamlCommandReader return when parsing a flow?

The `readCommands` method returns a `List<MaestroCommand>` representing the sequence of actions defined in the YAML. The `readConfig` method returns configuration metadata such as the flow name, target app ID, and environment variables without processing the command list.

### How does YamlCommandReader handle malformed YAML?

All parsing operations wrap exceptions in `mapParsingErrors` (lines 78–88), which converts technical `FlowParseException` errors into user-friendly `SyntaxError` instances. These include the offending file path, line number, and a visual pointer to the exact error location in the source.

### Where is YamlCommandReader invoked in the Maestro CLI?

The CLI runner in [`maestro-cli/src/main/java/maestro/cli/runner/TestRunner.kt`](https://github.com/mobile-dev-inc/Maestro/blob/main/maestro-cli/src/main/java/maestro/cli/runner/TestRunner.kt) invokes `YamlCommandReader.readCommands` to load flow files before passing them to the `Orchestra` execution engine. This occurs during both single-flow executions and continuous testing modes.

### Can YamlCommandReader validate YAML without running the test?

Yes. The `checkSyntax` method (lines 74–76) accepts a YAML string and validates it against Maestro's command schema without executing any device actions. This is useful for CI/CD pipelines and IDE integrations that need to verify flow validity before deployment.