dagr

July 29, 2026 · View on GitHub

dagr is a SQLite-backed DAG (directed acyclic graph) execution engine for agent workflow orchestration. It lets you define named pipelines of stages, track runs, fire shell hooks when stages become ready, and mark steps done as work completes.

What it does

  • Stores DAGs, stages, and runs in a local SQLite database
  • Advances run state automatically: when all dependencies of a stage are done, the stage becomes ready
  • Fires optional shell hooks when a stage becomes ready, capturing an optional dispatch ID from stdout
  • Detects cycles in stage dependency graphs before a run starts
  • Renders run progress as an ASCII tree in the terminal

Install

Prerequisites: Go 1.21+

go install github.com/callmeradical/dagr/cmd/dagr@latest

Or build from source:

git clone https://github.com/callmeradical/dagr
cd dagr
make install

Quick-start

1. Create a DAG

dagr dag create my-pipeline --description "nightly data pipeline"

2. Add stages with hooks

# Root stage — no dependencies
dagr stage add my-pipeline fetch \
  --brief "download raw data from source" \
  --hook 'echo "fetching..."; my-fetch-script $DAGR_RUN_ID $DAGR_STAGE_ID'

# Dependent stage — runs after fetch is done
dagr stage add my-pipeline process \
  --brief "transform and load data" \
  --depends-on fetch \
  --hook 'my-process-script $DAGR_RUN_ID $DAGR_STAGE_ID'

# Final stage — depends on process
dagr stage add my-pipeline report \
  --brief "generate report" \
  --depends-on process

3. Start a run

dagr run start my-pipeline
# started run 01934a7b-... for dag "my-pipeline"

The engine immediately advances the run: any stage with no unmet dependencies becomes ready and its hook fires.

4. Watch the run

dagr run watch 01934a7b-...

Output:

┌─ my-pipeline──────────────────────── run:01934a7b ─┐
│                                                    │
│    fetch                          ○ ready          │
│    └──▶ process                   ◌ pending        │
│         └──▶ report               ◌ pending        │
│                                                    │
│  0/3 complete · 1 ready · 2 blocked                │
└────────────────────────────────────────────────────┘

Status icons:

IconStatus
done
running
ready
pending / blocked
failed

5. Mark a step done

When an agent or hook finishes, call step-done to advance the run:

dagr run step-done <run-id> <stage-id> --result "200 ok"

This marks the step done and re-evaluates all pending steps. Stages whose dependencies are now satisfied become ready and their hooks fire.

List commands

dagr dag list
dagr stage list my-pipeline
dagr run list my-pipeline

Hook protocol

When a stage becomes ready, dagr spawns the hook command via sh -c:

sh -c "$HOOK"

The following environment variables are substituted in the hook string before execution:

VariableValue
$DAGR_RUN_IDID of the current run
$DAGR_STAGE_IDID of the stage
$DAGR_STAGE_NAMEName of the stage
$DAGR_DAG_IDID of the DAG

Dispatch ID: If the hook writes a non-empty first line to stdout, dagr records it as the step's dispatch_id (e.g. a fleet task ID or job ID). Subsequent lines are ignored. The step status is set to running after the hook fires.

Errors: Hook errors are printed to stderr but do not fail the run. The step must be explicitly marked done or failed by the caller.

Database location

By default dagr stores its database at ~/.local/share/dagr/dagr.db. Override with:

dagr --db /path/to/custom.db <command>

Shipping with no-mistakes

dagr stages that involve shipping code should use no-mistakes as the final validation gate — not as an implementation loop.

What no-mistakes owns

no-mistakes owns the single final shipping pipeline: rebase, review, targeted tests, documentation, lint, push, PR creation, and CI. dagr stages should not invoke it repeatedly or during development.

When to invoke it

Start no-mistakes once, after the implementation branch is committed and repository-native tests, lint, and bounded independent review are complete:

no-mistakes axi run --intent "<the user objective and approved tradeoffs>"

Never use --yes. Never abort and rerun to escape a gate.

Driving gates

At each gate, inspect every finding:

  • auto-fix — authorize selectively with --action fix --findings <ids>. Review the exact finding first.
  • ask-user — relay to the user and wait for their decision. Never approve autonomously.
  • no-op — informational; approve when no action is required.

Check progress without blocking: no-mistakes axi status

Hook pattern for a shipping stage

dagr stage add my-dag ship \
  --brief "validate and ship the implementation" \
  --depends-on implement \
  --hook 'cd $WORKTREE && no-mistakes axi run --intent "$INTENT" && echo $FLEET_TASK_ID'

Mark the step done from your completion callback, not from within the hook itself — no-mistakes is async. Call dagr run step-done only after no-mistakes axi status reports checks-passed.

After checks-passed

Stop driving at checks-passed. The PR is ready for merge; do not poll or rerun. Call dagr run step-done for the stage so the DAG advances.

Diagnosing inefficiency

no-mistakes stats --agents
no-mistakes stats --run <run-id>

Repeated full runs, excessive fix rounds, and intent inference are the main cost drivers. One final run with explicit --intent is the target.

Development

make build    # build ./dagr
make test     # go test ./...
make vet      # go vet ./...
make install  # install to $GOPATH/bin
make clean    # remove binary