dmx
Reference

Loop YAML Schema

Full reference for loop configuration files (.dmx/loops/{name}.yaml).

Top-level fields

FieldTypeRequiredDefault
namestringYes—
skillslist[string]Yes—
triggerobjectNo{type: manual}
goal_statestringNo""
repeat_untilstring | nullNonull
validatorslist[object]No[]
on_optional_failurewarn | ignoreNowarn
failure_handlingpause | failNopause
human_gatebooleanNotrue
on_completeobjectNo(see below)
require_branchbase | nullNonull

name

name: dev

The loop name. Must be non-empty and match the filename stem exactly — a file named dev.yaml must have name: dev, or loading fails.

skills

skills:
  - implement-next-phase
  - commit

Ordered list of skill names to execute. Must contain at least one skill. Skill names correspond to the name field in skill .md files. See Command Reference.

Each skill is resolved from .dmx/skills/ in the repo, then any shared sources, then the bundled skills. In each location it can be a flat {name}.md file or a {name}/SKILL.md folder.

Skills execute in order. When human_gate: true, the loop pauses after each skill.

trigger

trigger:
  type: manual        # or: on_complete

manual: the loop must be started explicitly with /dmx/run-loop name.

on_complete: the loop fires automatically when another loop's on_complete.on_success.trigger_loop references it.

goal_state

goal_state: "All phases implemented and committed, no unchecked tasks remaining"

Plain-English description of what the loop is trying to achieve. Passed to validators in the loop context (goal_state in the input contract). Not persisted to the task state file — it's read fresh from the loop config on each run.

repeat_until

repeat_until: all_phases_complete

If set, the skill sequence repeats until the condition evaluates to true. After each iteration, the condition is checked before validators run.

all_phases_complete: checks .dmx/tasks.md for unchecked - [ ] items. Condition is met when all items are - [x].

Omit (or set to null) for loops that should run once.

validators

validators:
  - tool: run_tests
    timeout_seconds: 630
    checks:
      - name: tests_pass
        required: true
  - tool: spec_adherence
    checks:
      - name: scope_matches_spec
        required: true

List of validator tools to run after all skills complete. Each validator:

tool: validator script name. Resolved to validators/{tool}.py in the repo root, then to validators/{tool}.py in each declared shared source; falls back to the bundled validator with that name.

checks: named checks to verify in this validator's output. Each check:

  • name: must match a check name in the validator's JSON output
  • required: true (must pass) or false (warning if fails)

timeout_seconds: optional. See below.

timeout_seconds

validators:
  - tool: run_tests
    timeout_seconds: 1200

How long this validator may run, in seconds. Must be a whole number of at least 60. When omitted, the runner allows 630 seconds.

The limit is passed to the validator as loop_context.timeout_seconds. The bundled run_tests stops the test command 30 seconds before it, so a slow suite is reported as a test timeout. That's why the minimum is 60: there's always time left to stop the test command cleanly.

To give a validator more time, set timeout_seconds in the loop's YAML: your .dmx/loops/{name}.yaml if you have one, otherwise the shared source that provides the loop. To change a bundled loop, copy it to .dmx/loops/{name}.yaml first.

on_optional_failure

on_optional_failure: warn   # or: ignore

What happens when an optional check (required: false) fails:

  • warn: warning is emitted, loop advances
  • ignore: failure is silently ignored

failure_handling

failure_handling: pause   # or: fail

What happens when a required check (required: true) fails:

  • pause: loop pauses. You fix the issue and run /dmx/loop-continue to retry.
  • fail: loop terminates. Job is marked failed.

human_gate

human_gate: true   # or: false

When true, the loop pauses after each skill completes and waits for /dmx/loop-continue before proceeding to the next skill (or to validators, if it was the last skill).

When false, skills run back-to-back automatically.

on_complete

on_complete:
  on_success:
    trigger_loop: validate    # or: null
  on_failure:
    trigger_loop: null
  on_warning:
    trigger_loop: null

Three sub-keys, each with a trigger_loop value:

  • on_success: all required validators pass
  • on_failure: one or more required validators fail
  • on_warning: required validators pass but optional validators fail

trigger_loop is either a loop name (starts that loop immediately) or null (loop chain ends).

Default values if on_complete is omitted:

on_complete:
  on_success:
    trigger_loop: null
  on_failure:
    trigger_loop: null
  on_warning:
    trigger_loop: null

require_branch

require_branch: base

Restricts which branch the loop can be started from. The only supported value is base, meaning the loop must be started from branch_base (the integration branch configured in .dmx/config.md). Omit for loops that operate on an already-identified ticket — this is only meaningful for loops that establish a new ticket/branch identity, like the bundled Spec Loop.

When set, run_loop also skips resolving a job id from any existing spec.md/branch and starts the run under a temporary id instead, promoted to the real job id once the ticket is created.

The check also runs when another loop chains into this one through on_complete. If it blocks the chain, the finished loop reports its own outcome plainly, along with the reason the next loop couldn't start.

In a repository with no commits yet, a require_branch: base loop can't start: it would branch from branch_base on GitHub, which needs at least one commit. Commit and push first.

Complete example

name: dev
skills:
  - implement-next-phase
  - commit
trigger:
  type: on_complete
goal_state: "All phases implemented and committed, no unchecked tasks remaining"
repeat_until: all_phases_complete
validators:
  - tool: run_tests
    timeout_seconds: 630
    checks:
      - name: tests_pass
        required: true
on_optional_failure: warn
failure_handling: pause
human_gate: true
on_complete:
  on_success:
    trigger_loop: validate
  on_failure:
    trigger_loop: null
  on_warning:
    trigger_loop: null

On this page