Skip to Content
DocsAuthoring flowsYAML flows (escape hatch)

YAML flows (escape hatch)

YAML is the power-user authoring path. It runs unchanged on the same executor, matcher, drivers, and reports as prose flows — the only difference is you write the IR yourself instead of letting the planner do it.

# flows/login.flow.yaml # yaml-language-server: $schema=../.klera/flow.schema.json name: Login smoke defaultTimeoutMs: 8000 steps: - assert: { visible: { testID: login-screen } } - type: into: { testID: login-email } value: pm@example.com - tap: Sign In - wait: for: { testID: welcome-greeting } timeoutSeconds: 5

Run it: klera run flows/login.flow.yaml. No LLM in the loop.

When to choose YAML

Reach for YAML when:

  • Procurement won’t approve LLM spend. Hand-authored YAML never touches a planner. You can still run prose flows by compiling them out-of-band (manual transport), but YAML lets you skip planning entirely.
  • You want a hand-tuned regression suite. A 200-line YAML flow with precise testIDs and tight timeouts iterates faster than recompiling prose every time you tweak a selector.
  • You’re importing from Maestro. klera reads Maestro YAML directly via the compatibility loader. See Migrating from Maestro.
  • The prose surface is too loose. Some flows want explicit coordinate gestures, raw assertJS expressions, or mock-network arrays that read more naturally as YAML than as prose.

Default to prose for new flows. Reach for YAML when one of the above applies. Both run on the same executor; the choice is about authoring ergonomics, not capability.

The schema

The IR is defined as a Zod schema in @klera/protocol/ir.ts. Every flow parses through it on load — invalid YAML fails fast with a clear path into the offending step.

A flow is an object with a name and a steps array. Three top-level fields are tunable:

name: Login smoke description: Asserts a fresh user can sign in. # optional defaultTimeoutMs: 10000 # default per-step timeout (ms); default 10s selfHealing: true # opt out by setting false steps: - tap: Sign In - assert: { visible: Welcome }

Two fields gate cross-platform divergence detection — see iOS and Android for the full picture:

parallel: ['ios', 'android'] divergenceTolerance: visualDiffPercent: 1.5 textNormalize: true

Every step is a single-key object. The key names the step kind (tap, type, assert, …); the value is the payload. See the IR reference for the full vocabulary.

Long form vs short form

Most steps accept a short-form sugar that normalises to the long form at parse time. The two forms are interchangeable; pick whichever reads better.

steps: - tap: Sign In - tap: { testID: login-submit } - swipe: up - screenshot: home.png - setBiometric: success - waitForIdle: true - relaunch: true - visualSnapshot: home-after-login

Short forms are not shortcuts the executor handles separately — they normalise to long-form IR at parse time, and the cached IR (or report payload) only contains the long form. Matcher behaviour, telemetry attributes, and report shape are identical regardless of which form you authored.

Editor support via JSON Schema

Add the language-server directive at the top of every YAML flow and your editor delivers autocomplete, hover docs, and inline validation:

# yaml-language-server: $schema=../.klera/flow.schema.json name: Login smoke steps: - tap: # ← autocomplete suggests targets

Materialise the schema once after klera init:

klera schema --out .klera/flow.schema.json klera schema --out .klera/flow.schema.json --pretty # readable

The schema is derived from the same Zod schema the runtime parses, so the editor’s view of “what’s valid” never drifts from what the executor accepts. Works with VS Code, JetBrains, Neovim, Helix, Sublime — anything that runs yaml-language-server.

See editor support for the end-to-end setup.

Opting into YAML scaffolding

klera init scaffolds a .flow.md + paired .flow.json cache by default. To opt into a YAML-only scaffold:

npx @klera/cli init --yaml

This writes flows/welcome.flow.yaml with a hand-authored body, skips the planner entirely, and configures .klera/config.yaml to expect YAML files. You can mix YAML and prose flows in the same flows/ directory — the loader detects the file extension and dispatches to the right pipeline.

klera init --yaml is for adopters who want to skip the prose loop entirely. If you only want a YAML flow alongside your prose flows, just drop a *.flow.yaml file into flows/ — no special init flag needed.

A realistic flow side by side

The same login scenario, both surfaces, both run on the same executor.

--- fixtures: email: pm@example.com password: hunter2 --- # Login smoke Type the email into the email field and the password into the password field, then tap "Sign in". Wait up to five seconds for the welcome greeting to appear. If a "What's New" modal appears between login and the welcome screen, dismiss it before the assertion.

Both files run identically:

klera run flows/login.flow.md # prose path — loads paired .flow.json klera run flows/login.flow.yaml # YAML path — parses straight to IR

The matcher, the strategy ladder, the bounded self-healing, the failure-evidence flush, the auto-triage, the OTel emission — all identical. You’re choosing how the IR is produced, not how it runs.

Validation

klera validate flows/login.flow.yaml # one file klera validate flows/ # whole directory

Validation parses the YAML through the Zod schema and exits non-zero on any error. Useful as a pre-commit hook or a fast CI gate before the heavier klera run step.

klera validate flows/login.flow.yaml flows/login.flow.yaml steps[3].type: Required expected object, got undefined

Sentinels in YAML values

YAML flows reference fixtures and secrets the same way prose flows do — through the ${env:KEY} and ${secret:KEY} sentinels. The resolver fires before each step’s value reaches the runtime, and the per-run Redactor scrubs ${secret:...} values from every artefact.

- type: into: { testID: login-email } value: ${env:E2E_LOGIN_EMAIL} - type: into: { testID: login-password } value: ${secret:E2E_LOGIN_PASSWORD} - tap: { testID: login-submit }

See fixtures and secrets for the full treatment, including .env resolution order, fixture files, and what the Redactor scrubs.

Next steps

  • IR reference — every step kind, parameter shape, and short-form sugar.
  • Migrating from Maestro — Maestro YAML runs unchanged via the compatibility loader.
  • Editor support — autocomplete, hover docs, inline validation across every major editor.
Last updated on