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:
| Icon | Status |
|---|---|
| ✓ | 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:
| Variable | Value |
|---|---|
$DAGR_RUN_ID | ID of the current run |
$DAGR_STAGE_ID | ID of the stage |
$DAGR_STAGE_NAME | Name of the stage |
$DAGR_DAG_ID | ID 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