erg.

Workflow configuration

A workflow is a directed flowchart that routes each issue from the first commit to a merged PR. You define states (nodes), the actions or events that drive each state, and the transitions between them. Run erg configure inside your repo to scaffold a starting point, or omit the file entirely to use the built-in defaults.

The settings block controls daemon-level behaviour: concurrency limits, branch naming, merge strategy, and session constraints. The source block selects the issue provider and filter. The states block is the flowchart itself.

Pipeline overview

The recommended workflow begins with a planning step — Claude posts an implementation plan for human approval before writing any code.

planning
ai.plan
await_plan
plan.user_replied
coding
ai.code
open_pr
github.create_pr
await_merge
pr.mergeable
done
succeed

plan.user_replied loops back for re-planning on feedback · retry with exponential backoff · timeout enforced on wait states

Example config

.erg/workflow.yaml
source:
  provider: github            # github | asana | linear
  filter:
    label: queued              # GitHub/Linear: issue label; Asana: tag name (optional when section is set)
    section: Todo             # Asana only: poll tasks in this board section instead of by tag

states:
  coding:
    type: task
    action: ai.code
    params:
      max_turns: 50
      max_duration: 30m
      system_prompt: file:.erg/prompt.md
    before:                        # runs before step; failure blocks
      - run: "make deps"
    after:                         # runs after; fire-and-forget
      - run: "make lint"
    retry:                         # retry with exponential backoff
      - max_attempts: 3
        interval: 30s
        backoff_rate: 2.0
    next: open_pr
    error: failed

  await_review:
    type: wait
    event: pr.reviewed
    timeout: 48h                  # enforced at runtime
    timeout_next: nudge_reviewers  # dedicated timeout edge
    params:
      auto_address: true
      max_feedback_rounds: 3

  merge:
    type: task
    action: github.merge
    params:
      method: squash            # rebase | squash | merge

settings block reference

The optional settings block at the top level of workflow.yaml controls daemon-level behaviour for the repo. All fields are optional — omit any field to use the default.

Field Type Default Description
max_concurrent int 3 Maximum number of sessions running simultaneously for this repo.
branch_prefix string none Prefix applied to auto-generated branch names (e.g. bot/).
cleanup_merged bool false Delete the branch and worktree automatically after a PR is merged or closed.
max_turns int 50 Maximum autonomous turns per AI session before the session is stopped.
max_duration int (minutes) 30 Maximum wall-clock time in minutes for a single AI session.
auto_merge bool false Automatically merge the PR once CI passes and all required approvals are met.
merge_method string rebase Merge strategy when auto-merging: rebase, squash, or merge.
container_image string built-in Custom Docker image to use for containerized Claude sessions.
settings example
settings:
  max_concurrent: 3          # max parallel sessions
  branch_prefix: bot/        # all branches start with bot/
  cleanup_merged: true       # delete branch/worktree after merge
  max_turns: 50              # stop session after 50 turns
  max_duration: 30           # stop session after 30 minutes
  auto_merge: true           # merge automatically when CI passes
  merge_method: squash       # rebase | squash | merge

source.filter keys

The filter block under source controls which issues or tasks erg picks up. Available keys depend on the provider.

Key Provider Description
label GitHub, Asana, Linear GitHub and Linear: issue label to poll. Asana: tag name to filter by. Optional for Asana when section is set — use it to narrow within a section if needed.
project Asana Asana project GID. Required for all Asana workflows. Found in the project URL: app.asana.com/0/{gid}/list.
section Asana Board section name to poll. When set, erg fetches tasks from that specific section rather than the whole project, enabling kanban-style workflows where section position — not tags — drives the lifecycle. Matched case-insensitively. If label is also set, it is applied as an additional tag filter on top of the section results.
team Linear Linear team ID. Required for Linear workflows.

State types

Each state in the workflow has a type that determines its behavior.

Type Description
task Execute an action (e.g. ai.code, github.merge)
wait Poll for an event with an enforced timeout (e.g. pr.reviewed, ci.complete)
choice Evaluate rules against step data for conditional branching
pass Inject data for downstream states to read
succeed Terminal state — marks the work item as complete
fail Terminal state — marks the work item as failed

task

A task state executes a registered action and then transitions to next on success or error on failure. Every action that calls an external API (GitHub, Asana, Linear, git remotes) should define a retry block. Network failures are transient — without retry, a momentary outage permanently fails the work item.

retry example
open_pr:
  type: task
  action: github.create_pr
  retry:
    - max_attempts: 3
      interval: 15s
      backoff_rate: 2.0   # waits 15s, 30s, 60s
  next: await_review
  error: failed

Use catch to route specific error types to recovery states instead of the generic error edge. Use before hooks to run setup scripts (blocking) and after hooks for teardown (fire-and-forget).

wait

A wait state polls for an external event on each daemon tick. It does not advance until the event fires. Always set a timeout to prevent indefinite waits — use timeout_next to route the timeout to a dedicated state (e.g. post a comment) rather than the generic error edge.

wait example
await_review:
  type: wait
  event: pr.reviewed
  timeout: 72h
  timeout_next: nudge_reviewers
  params:
    auto_address: true          # Claude addresses feedback while waiting
    max_feedback_rounds: 3
  next: merge
  error: failed

The pr.mergeable event is a shorthand that combines pr.reviewed and ci.complete into a single wait state. Use it for simple pipelines where you want both conditions satisfied before proceeding.

choice

A choice state reads values from the accumulated step data and branches to different states based on rules. Each rule specifies a variable, an equals value, and a next state. The default route fires when no rule matches.

choice example
check_ci:
  type: choice
  choices:
    - variable: ci_passed
      equals: true
      next: request_reviewer
    - variable: ci_failed
      equals: true
      next: fix_ci
  default: failed

Choice states are the branching points of your flowchart. Combine them with pass states to make decisions on injected configuration flags, or with CI/review wait states to route on outcomes.

pass

A pass state injects static data into the workflow context and immediately transitions to next. Use it at the start of a workflow to set configuration flags that downstream choice states can inspect.

pass example
configure:
  type: pass
  data:
    close_issue_on_merge: true
    max_ci_fix_rounds: 3
  next: coding

template

A template state embeds a reusable sub-workflow inline. At load time, template states are expanded: each template's internal states are copied into the calling workflow with a unique namespace prefix, and exit ports are wired to the caller's mapped local states.

template state (using a built-in)
ci:
  type: template
  use: builtin:ci
  exits:
    success: review
    failure: notify_failed

The use field accepts either a built-in reference (e.g. builtin:ci, builtin:review) or a relative file path to a template YAML file (e.g. .erg/templates/my-template.yaml). The exits field maps the template's declared exit ports to local states in the calling workflow.

Template file structure

A template file defines a self-contained workflow fragment with a single entry point and named exit ports.

.erg/templates/code-review-merge.yaml
template: code-review-merge
entry: coding
exits:
  success: done      # internal terminal state
  failure: failed    # internal terminal state
params:
  - name: max_rounds
    default: 3
states:
  coding:
    type: task
    action: ai.code
    next: push
  # ... more states ...
  done:
    type: succeed
  failed:
    type: fail

Parameters

Templates can declare parameters with defaults. Callers override them via the params field. Inside template states, {{param_name}} placeholders in string values are substituted with the resolved parameter value.

caller with params
do_work:
  type: template
  use: .erg/templates/code-review-merge.yaml
  params:
    max_rounds: 5
  exits:
    success: close_issue
    failure: notify_slack

Built-in templates

Erg ships with modular built-in templates that can be composed to build any workflow. Each has success and failure exits.

builtin:plan

Planning phase: AI generates a plan → waits for user feedback → re-plans on rejection (loop until approved). Includes 72h timeout with expiry notification.

builtin:code

AI coding session with preset turn and duration limits. Runs containerized by default.

builtin:pr

Creates a pull request linked to the source issue. Includes retry on transient failures.

builtin:ci

Waits for CI → fixes failures (bounded loop) → resolves merge conflicts via rebase or AI. Includes timeout and unfixable notifications.

builtin:review

Waits for PR review → addresses feedback (bounded loop). Handles approval, changes-requested, and external merge. Includes 48h timeout notification.

builtin:merge

Merges the PR (rebase by default) and cleans up the branch. Includes retry on transient failures.

Templates can be nested (a template can reference another template) and circular references are automatically detected. Template file paths must be relative to the repo root — absolute paths are rejected.

Error handling

retry

Task states support retry with configurable max_attempts, interval, and exponential backoff_rate.

catch

Route specific errors to recovery states with catch blocks. Match by error type or message pattern.

timeout

Wait states enforce timeout durations at runtime. timeout_next provides a dedicated transition edge.

error state

Every task state can define an error transition for unrecoverable failures. Defaults to the failed terminal state.

Hooks

before hooks run before step execution — if they fail, the step is blocked. after hooks run after completion and are fire-and-forget.

Hooks receive the following environment variables: $ERG_BRANCH, $ERG_PR_URL, $ERG_ISSUE_URL, $ERG_REPO_PATH, $ERG_SESSION_ID, $ERG_ISSUE_ID, $ERG_ISSUE_TITLE, $ERG_WORKTREE, and $ERG_PROVIDER.

made by zack · mit license