dmx
Configuration

Loop Configuration

Loop configs are YAML files that define how a loop runs. They live in .dmx/loops/{name}.yaml in your repository.

Where configs live

dmx resolves loop configs in this order:

  1. App repo: .dmx/loops/{name}.yaml in your repository
  2. Shared sources: .dmx/vendor/{source}/loops/{name}.yaml for each source declared in .dmx/shared-sources.yaml, in declared order
  3. Bundled defaults: the five default loops shipped with dmx

The repo always wins. A file at .dmx/loops/dev.yaml overrides the bundled Dev Loop entirely, and any shared-source version of it. Shared sources are optional; see Shared Sources to maintain loops once for many repositories.

To customize a loop, copy the bundled default into .dmx/loops/ and modify it. To create a new loop, create a new file. The filename stem becomes the loop name.

Full YAML schema

# Required — must match the filename stem
name: dev

# Required — ordered list of skill names to execute
skills:
  - implement-next-phase
  - commit

# Trigger — how the loop starts
trigger:
  type: manual          # manual | on_complete

# Goal state — plain-English description of success
goal_state: "All phases implemented and committed, no unchecked tasks remaining"

# Repeat until — condition to evaluate before running validators
# Currently supported: all_phases_complete
# Omit if the loop should run once only
repeat_until: all_phases_complete

# Validators — run after all skills complete (and repeat_until is met)
validators:
  - tool: run_tests               # validator script name
    timeout_seconds: 630          # optional; default 630, minimum 60
    checks:
      - name: tests_pass
        required: true            # required: true | false

  - tool: security_scan           # a custom validator in validators/
    checks:
      - name: no_high_findings
        required: true
      - name: no_medium_findings
        required: false

# What to do when an optional check fails
on_optional_failure: warn         # warn | ignore

# What to do when a required check fails
failure_handling: pause           # pause | fail

# Whether to pause at each skill boundary for human review
human_gate: true

# What to do when the loop completes
on_complete:
  on_success:
    trigger_loop: validate        # loop name to start, or null
  on_failure:
    trigger_loop: null
  on_warning:
    trigger_loop: null

# Restrict which branch this loop can start from. Only meaningful for loops
# that establish a new ticket/branch identity. Omit otherwise.
require_branch: base

Field reference

name

Required. The loop name. Must match the filename stem. Used in trigger_loop references and in /dmx/run-loop name.

skills

Required. Ordered list of skill names to execute. Skills run in order. The loop pauses between skills when human_gate: true.

Skill names correspond to the name field in skill .md files. Available skills are listed in Command Reference.

The loop runtime looks each skill up in this order:

  1. App repo: .dmx/skills/ in your repository
  2. Shared sources: .dmx/vendor/{source}/skills/ for each declared source, in order
  3. Bundled: the skills shipped with dmx

In each location, a skill can be a flat {name}.md file or a {name}/SKILL.md folder (the agentskills.io / Claude Code convention). If both exist, the flat file wins. A file at .dmx/skills/commit.md replaces the bundled commit skill whenever a loop runs it. Skill names must be plain slugs: letters, digits, _, and -.

trigger

How the loop starts.

TypeWhen to use
manualThe developer invokes /dmx/run-loop explicitly
on_completeThe loop fires when the previous loop's on_success.trigger_loop names this loop

goal_state

Plain-English description of what the loop is trying to achieve. Passed to validators as context (goal_state in the input contract).

repeat_until

If set, the skill sequence repeats until the condition is met. Currently supported:

  • all_phases_complete: repeats until all - [ ] items in tasks.md are checked off

Omit for loops that should run once.

validators

List of validators to run after all skills complete (and after repeat_until is satisfied). Each validator:

  • tool: the validator script to run (looked up in validators/{tool}.py: repo first, then shared sources, then bundled)
  • checks: named checks, each with required: true | false
  • timeout_seconds: optional; how long the validator may run. Defaults to 630 seconds, minimum 60. See timeout_seconds.

Multiple validators run in order. All required checks across all validators must pass.

on_optional_failure

What happens when an optional check fails:

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

Default: warn.

failure_handling

What happens when a required check fails:

  • pause: loop pauses, you fix and continue
  • fail: loop terminates

Default: pause.

human_gate

When true, the loop pauses after each skill completes and waits for /dmx/loop-continue before proceeding. When false, skills run back-to-back automatically.

Default: true.

on_complete

What to do when the loop finishes:

  • on_success.trigger_loop: loop name to start automatically when all validators pass
  • on_failure.trigger_loop: loop name to start when required validators fail (uncommon)
  • on_warning.trigger_loop: loop name to start when optional validators fail (uncommon)

Set to null to terminate without starting another loop.

require_branch

Restricts the loop to starting from branch_base (the integration branch set in .dmx/config.md). The only supported value is base.

Use this for loops that create a brand-new ticket/branch — like the bundled Spec Loop — where any pre-existing spec.md or current branch would be stale. Attempting to start such a loop from anywhere else is rejected with a message telling you to switch branches first. Leave it unset for loops that operate on an already-identified ticket.

The same check applies when another loop chains into this one through on_complete. If the chain is blocked (for example, a custom loop on a feature branch chains into spec), the finished loop reports its own outcome along with the reason the next loop couldn't start.

Omit for loops that should be startable from any branch.

Example: custom documentation loop

name: docs
skills:
  - docs
trigger:
  type: manual
goal_state: "Public API documented, docstrings complete, README updated"
validators:
  - tool: lint_check
    checks:
      - name: no_lint_errors
        required: false
on_optional_failure: warn
failure_handling: pause
human_gate: true
on_complete:
  on_success:
    trigger_loop: null

Example: hotfix loop (faster path)

name: hotfix
skills:
  - implement-next-phase
  - commit
  - validate
  - create-pr
trigger:
  type: manual
goal_state: "Fix implemented, validated, PR opened against production branch"
validators:
  - tool: run_tests
    checks:
      - name: tests_pass
        required: true
on_optional_failure: warn
failure_handling: pause
human_gate: true
on_complete:
  on_success:
    trigger_loop: null

Tips

Keep loop configs in version control. .dmx/loops/ should be committed to your repo. Loop configs are reviewed in PRs like any other code change.

Start from the bundled defaults. Copy the relevant YAML from The Default Loops and modify it. The defaults are designed to be good starting points, not constraints.

One loop, one responsibility. Avoid creating loops that try to do too many things. A loop that's too long is hard to review and hard to debug when validators fail.

On this page