Skip to content

Writing Custom Workflows

Animus workflows are defined in YAML and live in .animus/workflows.yaml and .animus/workflows/*.yaml. These files describe project-local workflows, overrides, and pack composition. They usually wrap canonical pack-qualified refs such as animus.task/standard instead of copying task or requirement semantics into the repository.

For the target design of universal phase verdicts and YAML-defined phase-local fields, see Phase Contracts.

File Location

Place workflow files in either:

  • .animus/workflows.yaml
  • .animus/workflows/*.yaml

Example:

.animus/
  workflows.yaml
  workflows/
    custom.yaml
    premium.yaml

Animus loads the single-file form and the directory form together. You can keep everything in one file or split workflows across multiple files.

YAML Structure Overview

A workflow file can contain the following top-level sections:

yaml
mcp_servers:    # External tool integrations
agents:         # Agent definitions (model, tool, system_prompt/system_prompt_file)
models:         # Named model+tool registry (agents reference by name)
phase_catalog:  # Phase metadata (label, description, category, tags)
phases:         # Phase execution config (mode, directive, command)
workflows:      # Workflow definitions (sequence of phases)

Agents

Agents define which model and tool to use, plus an optional system prompt. For long prompts, you can keep the text in a separate UTF-8 file with system_prompt_file instead of embedding it inline:

yaml
agents:
  default:
    model: claude-sonnet-4-6
    tool: claude
    tool_profile: main

  po-reviewer:
    system_prompt: |
      You are a Product Owner reviewing completed development work.
      Your job is to verify that ALL acceptance criteria and requirements
      from the original task are fully met by the implementation.
    model: claude-sonnet-4-6
    tool: claude

  research-agent:
    model: gemini-3.1-pro-preview
    tool: gemini

  reviewer:
    system_prompt_file: prompts/reviewer.md
    model: claude-sonnet-4-6
    tool: claude

The tool field specifies which CLI tool runs the agent: claude, codex, gemini, opencode, or oai-runner.

system_prompt and system_prompt_file are mutually exclusive on the same agent. Relative prompt-file paths resolve from the YAML file that declares the agent.

For Claude-only account routing, set tool_profile on a Claude agent or in a phase runtime: block. The profile name resolves against the user's global Animus config and typically injects CLAUDE_CONFIG_DIR. Initial login for each profile still happens interactively in Claude Code.

MCP Servers

Declare MCP servers that agents can use as external tools:

yaml
mcp_servers:
  animus:
    command: animus
    args: ["mcp", "serve"]

  hubspot:
    command: npx
    args: ["-y", "@hubspot/mcp-server"]
    env:
      HUBSPOT_ACCESS_TOKEN: "${HUBSPOT_ACCESS_TOKEN}"
    tools:
      - contacts.search
      - contacts.get

Environment variables use ${VAR} interpolation syntax. The tools field is optional and restricts which tools from the server are exposed to agents.

Agents reference MCP servers by name:

yaml
agents:
  sales-agent:
    model: claude-sonnet-4-6
    tool: claude
    mcp_servers:
      - animus
      - hubspot

Phase Catalog

The phase catalog provides metadata for phases. This is separate from the phase execution config:

yaml
phase_catalog:
  implementation:
    label: "Implementation"
    description: "Implement production-quality code changes"
    category: development
    tags: [coding, implementation]

  code-review:
    label: "Code Review"
    description: "Review implementation for defects and maintainability"
    category: review
    tags: [review, quality]

Categories include: planning, development, review, verification.

Phases

Phases define how work is executed. There are two modes: agent-driven and command-driven.

Longer term, every phase should also participate in the same universal verdict-driven output model, with YAML declaring any additional phase-specific fields that the phase emits.

Agent-Driven Phases

Agent phases dispatch work to an AI agent:

yaml
phases:
  implementation:
    mode: agent
    agent: default
    directive: "Implement the task requirements"

  code-review:
    mode: agent
    agent: po-reviewer
    directive: "Review the implementation"

mode is required on every phase definition (agent, command, or manual). max_rework_attempts is not a phase-definition field — it belongs on a rich phase entry inside a workflow's phases: list (see Phase Configuration Within Workflows).

Command-Driven Phases

Command phases run a CLI command directly:

yaml
phases:
  unit-test:
    mode: command
    directive: "Run the workspace test suite"
    command:
      program: cargo
      args: ["test", "--workspace"]
      timeout_secs: 600

  lint:
    mode: command
    directive: "Run clippy with deny warnings"
    command:
      program: cargo
      args: ["clippy", "--workspace", "--", "-D", "warnings"]
      timeout_secs: 120

Workflows with Shared Sequences

Workflows are the top-level unit. A workflow that embeds another workflow (via workflow_ref) achieves the same reuse as a "pipeline" concept — the referenced workflow's phases are expanded inline at dispatch time:

yaml
workflows:
  - id: review-cycle
    name: "Review Cycle"
    description: "Reusable code review and testing sequence"
    phases:
      - code-review:
          on_verdict:
            rework:
              target: code-review
      - testing

  - id: quick-fix
    name: "Quick Fix"
    description: "Fast pipeline for small bug fixes"
    phases:
      - implementation
      - testing

Phase Configuration Within Workflows

Phases in a pipeline support these options:

on_verdict routing -- When a review phase returns a rework verdict, route back to a target phase:

yaml
phases:
  - code-review:
      on_verdict:
        rework:
          target: implementation

skip_if conditions -- Skip a phase based on task properties:

yaml
phases:
  - implementation:
      skip_if:
        - "task.type == 'docs'"

Sub-workflow references -- Embed another workflow definition by ID:

yaml
phases:
  - requirements
  - implementation
  - workflow_ref: review-cycle

Workflows

Workflows tie everything together. They are the top-level units that get dispatched against subjects. A project-local workflow can delegate to bundled or installed pack refs:

yaml
workflows:
  - id: standard-workflow
    name: Standard Workflow
    phases:
      - workflow_ref: animus.task/standard
yaml
workflows:
  - id: requirement-task-generation
    name: "Requirement Task Generation"
    description: "Create or refine requirement-linked Animus tasks"
    phases:
      - requirement-task-generation

Git Operations Are Command Phases

Animus does not perform git operations (commit, push, PR, merge) as runner automation. The post_success.merge config block was removed in v0.5.x — a workflow that still sets post_success.merge (or integrations.git.auto_merge) is rejected at parse time.

Express commit, push, PR creation, and merge as command phases: a phase with a command: block that runs git or gh. They run in the task worktree and sequence in phases: like any other phase, so they pick up verdict routing, retries, and approval gates for free.

yaml
phases:
  create-pr:
    mode: command
    directive: "Open a GitHub PR from the current branch"
    command:
      program: gh
      args: ["pr", "create", "--fill", "--base", "main"]
      cwd_mode: task_root
      timeout_secs: 60
      success_exit_codes: [0]

workflows:
  - id: full
    name: "Full Lifecycle"
    phases:
      - triage
      - refine-requirements
      - implementation
      - unit-test:
          on_verdict:
            rework:
              target: implementation
      - code-review:
          on_verdict:
            rework:
              target: implementation
      - po-review:
          on_verdict:
            rework:
              target: implementation
      - create-pr        # command phase running `gh pr create`

A human still performs the final merge of the PR (or you add an explicit gh pr merge command phase, gated by an approval if desired).

Variables

Variables live inside a workflow definition (not at the file's top level) and can be overridden at runtime via --input-json:

yaml
workflows:
  - id: my-workflow
    name: My Workflow
    phases: [implementation]
    variables:
      - name: target_branch
        default: main
      - name: review_depth
        default: standard

Complete Example: Code Review Workflow

yaml
agents:
  reviewer:
    system_prompt: |
      You are a senior code reviewer. Focus on correctness, performance,
      and adherence to the project's coding conventions. Flag any security
      concerns. Use Animus MCP tools to update task checklists with findings.
    model: claude-sonnet-4-6
    tool: claude

  implementer:
    model: claude-sonnet-4-6
    tool: claude

phase_catalog:
  implement:
    label: "Implementation"
    category: development
    tags: [coding]
  review:
    label: "Code Review"
    category: review
    tags: [review, quality]
  test:
    label: "Testing"
    category: verification
    tags: [testing]

phases:
  run-tests:
    mode: command
    directive: "Run tests"
    command:
      program: cargo
      args: ["test", "--workspace"]
      timeout_secs: 600

workflows:
  - id: reviewed-implementation
    name: "Reviewed Implementation"
    description: "Implement, test, review with rework loop"
    phases:
      - implement
      - run-tests:
          on_verdict:
            rework:
              target: implement
      - review:
          on_verdict:
            rework:
              target: implement
      - create-pr        # command phase running `gh pr create`

Tips

  • Use ${VAR} syntax for environment variable interpolation in env fields.
  • Reuse agents across phases by referencing the same agent name.
  • Order phases so that fast-failing checks (lint, tests) run before expensive reviews.
  • Keep max_rework_attempts reasonable (2-3) to avoid infinite loops.
  • Validate your workflow config with animus workflow config validate.

Released under the Elastic License 2.0 (ELv2).