Skip to content

Human-in-the-Loop ​

A workflow can stop at a step until a person acts on it. Two step forms do this: a human task waits for input before work continues, and an approval gate waits for a review of the step it is attached to.

Choosing a Step Form ​

Human taskApproval gate
YAMLStandalone step with action: human.taskapproval field on an executable step
Before waitingRuns nothingRuns the step's command or action
Reviewer actionsComplete; push back when with.push_back is setApprove, push back, or reject
Collected valuesTyped form values, published as step outputsString inputs, exposed as environment variables to downstream steps
PlacementRoot DAGs, locally or on a distributed workerRoot DAGs and sub-DAGs; the DAG cannot set worker_selector
Act fromWeb UI, REST API, local CLIWeb UI, REST API

Use a human task when later steps need a decision or typed values from a person, or when a person reviews results that earlier steps produced. Use an approval gate when a person reviews the output of the step the gate is attached to, or must be able to reject the run.

Lifecycle ​

text
step becomes ready
        │
        ▼
runs its command                  (approval gate only)
        │
        ▼
     Waiting ── push back ──────▶ restart point and its dependents
        │                         run again, then the step waits again
        │
        ├── complete / approve ──▶ Succeeded; dependents can run
        │
        └── reject ─────────────▶ Rejected; the run ends   (approval gate only)

While a step waits:

  • The step and the DAG run have status Waiting. Steps that depend on the waiting step stay Not Started.
  • Branches that do not depend on the waiting step keep running. When nothing else can run, the run stops in Waiting and releases its process or worker slot.
  • The waiting step has no timeout. It stays open until someone acts on it or the run is stopped.

Resuming ​

Each decision is stored with the run before the run resumes. The run then continues as a new attempt of the same DAG run. Completed steps keep their results and do not run again. The resumed attempt uses the DAG definition stored with the run, so edits to the YAML file after the run started do not apply to it.

A decision resumes the run as soon as a step can run, even while other steps keep waiting. The resumed attempt runs the ready branch and returns to Waiting for the remaining steps. The run instead stays Waiting until no manual step remains when:

  • any step in the run has failed, been aborted, been rejected, or is waiting for a retry, so those steps run again only once, or
  • the step that became ready declares build inputs.

Each resumed attempt checks DAG-level preconditions again and runs handler_on.init again. Each return to Waiting runs the wait handler and sends wait notifications again.

Human tasks always resume through the queue. Approval gates resume directly unless the DAG belongs to an enabled global queue. Queued resumes need a running scheduler.

Notifying Reviewers ​

SurfaceHow
CockpitThe Review column lists root runs waiting for approval or human-task input.
Notification rulesInclude the Waiting event in a rule.
Wait handlerhandler_on.wait runs a step. DAG_WAITING_STEPS holds the comma-separated names of the waiting steps.
Wait emailSet mail_on.wait: true and configure wait_mail.
yaml
mail_on:
  wait: true

wait_mail:
  from: dagu@example.com
  to:
    - approvers@example.com
  prefix: "[REVIEW REQUIRED]"

handler_on:
  wait:
    run: './notify-slack.sh "Review required: $DAG_WAITING_STEPS"'

steps:
  - id: deploy_staging
    run: ./deploy.sh staging
    approval:
      prompt: Verify staging before production

  - id: deploy_prod
    depends: [deploy_staging]
    run: ./deploy.sh production

Push-back ​

Push-back sends work back instead of accepting it. Dagu resets a restart point and every step downstream of it to Not Started, runs them again with the reviewer's feedback, and opens the checkpoint again. The reviewer can then accept the new result or push back again. Each push-back increments the iteration count.

Human taskApproval gate
Enabled bywith.push_backAlways available
Restart pointwith.push_back.rewind_to, an upstream stepThe step itself, or approval.rewind_to
FeedbackFields declared in with.push_back.formAny keys; approval.input limits the keys the step sees
Web UI actionRequest changesRetry

Push-back Context ​

Every step that runs again because of a push-back receives:

  • each feedback value as an environment variable named after its key, such as FEEDBACK
  • DAG_PUSHBACK, a JSON payload with the latest feedback and the push-back history, kept within 30 KiB
  • DAG_PUSHBACK_ITERATION and ${context.pushback.iteration}, the current iteration
  • DAG_PUSHBACK_PREVIOUS_STDOUT_FILE, the path to the step's previous stdout log, when it had one

AI steps also receive the feedback without any wiring. A chat.completion step continues its previous conversation with the feedback as the next user message. A harness.run step gets a push-back block appended to its prompt, with the previous stdout as a file path only, because harness output can be large.

The feedback of one push-back, encoded as a JSON object, is limited to 16 KiB because it is passed as environment variables. A larger push-back is rejected before the run changes.

See Push-back Context for the DAG_PUSHBACK payload format.

Acting on a Waiting Step ​

Human taskApproval gate
Web UIHuman tasks tab: Complete task, Request changesApproval tab: Approve, Retry; Reject in the run's action bar
REST APIHuman Task EndpointsApproval Endpoints
CLIdagu human-task complete, dagu human-task push-backNot available
MCPNot availableNot available

Acting on a waiting step requires permission to run DAGs in its workspace. The run records who acted: the authenticated user for Web UI and REST API actions, and the OS user for the local CLI.

Dagu is open source under the GNU General Public License v3.0.