Skip to content
← all posts
6 min readtechnical

GitHub Actions with Mentiko: CI/CD for Agent Chains

github-actionsci-cddeploymentautomation

Mentiko Team

Your agent chain definitions are JSON files. They live in a git repo. They get reviewed in PRs. But if you're merging chain changes without validation, you're one typo away from a broken pipeline in production.

GitHub Actions lets you treat chain definitions like application code: lint them, test them, and deploy them automatically. Here's how to set that up with Mentiko.

What to validate

A chain definition can fail in several ways that aren't obvious until runtime:

  • Malformed JSON (missing comma, unclosed bracket)
  • Invalid event references (agent triggers on an event nobody emits)
  • Circular dependencies (agent A triggers B, B triggers A)
  • Missing variables (prompt references {API_KEY} but no variable is defined)
  • Workspace configuration errors (referencing a Docker image that doesn't exist)

Catching these at PR time instead of runtime saves you from deploying chains that will fail on their first run.

Basic chain validation workflow

Create .github/workflows/chain-validation.yml:

name: Validate Chain Definitions

on:
  pull_request:
    paths:
      - 'chains/**/*.json'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Mentiko CLI
        run: |
          curl -fsSL https://mentiko.com/install.sh | bash
          echo "$HOME/.mentiko/bin" >> $GITHUB_PATH

      - name: Validate JSON syntax
        run: |
          for file in chains/**/*.json; do
            echo "Validating $file..."
            python3 -m json.tool "$file" > /dev/null
          done

      - name: Validate chain definitions
        run: |
          for file in chains/**/*.json; do
            mentiko validate "$file"
          done

      - name: Check event graph
        run: mentiko graph --check chains/

The workflow runs on every PR that touches files in the chains/ directory. Three validation stages:

  1. JSON syntax check -- catches structural errors before Mentiko even parses the file.
  2. mentiko validate -- checks the chain definition against the schema. Validates agent names, trigger/emit declarations, retry configs, workspace references.
  3. mentiko graph --check -- builds the event dependency graph across all chains and checks for orphaned events, circular dependencies, and unreachable agents.

If any step fails, the PR gets a red check. The author fixes the issue before merging.

Dry-run testing

Validation catches structural problems. Dry runs catch logic problems. A dry run walks through the chain execution without actually calling any LLM providers -- it simulates the event flow and verifies that agents trigger in the expected order.

Add a dry-run job to your workflow:

  dry-run:
    runs-on: ubuntu-latest
    needs: validate
    steps:
      - uses: actions/checkout@v4

      - name: Install Mentiko CLI
        run: |
          curl -fsSL https://mentiko.com/install.sh | bash
          echo "$HOME/.mentiko/bin" >> $GITHUB_PATH

      - name: Dry run changed chains
        run: |
          # Get list of changed chain files
          CHANGED=$(git diff --name-only origin/main -- 'chains/**/*.json')
          for file in $CHANGED; do
            echo "Dry running $file..."
            mentiko run --dry-run "$file" \
              --var TOPIC="test-input" \
              --timeout 30s
          done

The dry run validates the event flow without spending tokens. It confirms that:

  • The chain starts correctly from chain:start
  • Each agent's triggers resolve to events that actually get emitted
  • The chain reaches chain:complete (or a defined terminal state)
  • Timeout settings are respected
  • Variable interpolation works with test values

The needs: validate dependency ensures dry runs only execute after basic validation passes.

Auto-deploy on merge

Once a chain change is validated and reviewed, deploy it automatically when the PR merges to main.

Create .github/workflows/deploy-chains.yml:

name: Deploy Chain Definitions

on:
  push:
    branches: [main]
    paths:
      - 'chains/**/*.json'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Deploy to Mentiko instance
        env:
          MENTIKO_HOST: ${{ secrets.MENTIKO_HOST }}
          MENTIKO_API_KEY: ${{ secrets.MENTIKO_API_KEY }}
        run: |
          # Get changed files in this push
          CHANGED=$(git diff --name-only HEAD~1 -- 'chains/**/*.json')

          for file in $CHANGED; do
            chain_name=$(basename "$file" .json)
            echo "Deploying $chain_name..."
            mentiko deploy "$file" \
              --host "$MENTIKO_HOST" \
              --api-key "$MENTIKO_API_KEY"
          done

      - name: Verify deployment
        env:
          MENTIKO_HOST: ${{ secrets.MENTIKO_HOST }}
          MENTIKO_API_KEY: ${{ secrets.MENTIKO_API_KEY }}
        run: |
          CHANGED=$(git diff --name-only HEAD~1 -- 'chains/**/*.json')
          for file in $CHANGED; do
            chain_name=$(basename "$file" .json)
            mentiko status "$chain_name" \
              --host "$MENTIKO_HOST" \
              --api-key "$MENTIKO_API_KEY"
          done

Two GitHub secrets are required:

  • MENTIKO_HOST -- your Mentiko instance URL (e.g., https://mentiko.yourcompany.com or a LAN IP like 192.168.68.172:8082)
  • MENTIKO_API_KEY -- an API key generated from your Mentiko dashboard with deploy permissions

The deploy step pushes updated chain definitions to your running instance. The verify step confirms each deployed chain is active and healthy.

Environment-specific chains

Production and staging often need different configurations -- different models, different retry thresholds, different variable values. Handle this with environment-specific overrides:

chains/
  content-pipeline.json          # base definition
  overrides/
    staging.content-pipeline.json  # staging overrides
    prod.content-pipeline.json     # production overrides

The override files contain only the fields that differ:

{
  "overrides": {
    "agents.researcher.workspace": "docker-staging",
    "agents.drafter.retry.max_attempts": 1,
    "variables.MODEL": "gpt-4o-mini"
  }
}

Your deploy workflow applies the right overrides per environment:

      - name: Deploy with environment overrides
        run: |
          ENV="${{ github.ref == 'refs/heads/main' && 'prod' || 'staging' }}"
          for file in chains/*.json; do
            chain_name=$(basename "$file" .json)
            override="chains/overrides/${ENV}.${chain_name}.json"
            if [ -f "$override" ]; then
              mentiko deploy "$file" --override "$override" \
                --host "$MENTIKO_HOST" --api-key "$MENTIKO_API_KEY"
            else
              mentiko deploy "$file" \
                --host "$MENTIKO_HOST" --api-key "$MENTIKO_API_KEY"
            fi
          done

The base chain definition stays identical across environments. Only the overrides change. This means your staging chain is structurally identical to production -- just with safer parameters for testing.

Scheduled chain health checks

Beyond deployment, use GitHub Actions to periodically verify your chains are healthy:

name: Chain Health Check

on:
  schedule:
    - cron: '0 */6 * * *'  # Every 6 hours

jobs:
  health-check:
    runs-on: ubuntu-latest
    steps:
      - name: Check chain status
        env:
          MENTIKO_HOST: ${{ secrets.MENTIKO_HOST }}
          MENTIKO_API_KEY: ${{ secrets.MENTIKO_API_KEY }}
        run: |
          mentiko health --host "$MENTIKO_HOST" \
            --api-key "$MENTIKO_API_KEY" \
            --format json > health.json

          # Fail if any chain has error status
          if jq -e '.chains[] | select(.status == "error")' health.json > /dev/null 2>&1; then
            echo "Unhealthy chains detected:"
            jq '.chains[] | select(.status == "error")' health.json
            exit 1
          fi

          echo "All chains healthy."

A cron-scheduled workflow checks your Mentiko instance every 6 hours. If any chain is in an error state, the workflow fails and GitHub sends a notification. It's a lightweight monitoring layer that doesn't require any additional infrastructure.

PR comments with chain diff

For extra visibility during code review, add a step that posts a human-readable chain diff as a PR comment:

      - name: Post chain diff
        if: github.event_name == 'pull_request'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          DIFF=$(mentiko diff --base origin/main --head HEAD chains/)
          if [ -n "$DIFF" ]; then
            gh pr comment ${{ github.event.pull_request.number }} \
              --body "### Chain Changes
          $DIFF"
          fi

mentiko diff produces a structured comparison: added agents, removed events, changed prompts, modified retry configs. Reviewers see exactly what changed in the chain without parsing raw JSON diffs.

What this gives you

With validation, dry runs, auto-deploy, and health checks in place, your chain management workflow looks like this:

  1. Developer creates a branch and modifies a chain definition
  2. PR triggers validation and dry-run tests
  3. Team reviews the PR with chain diff context
  4. PR merges, deploy workflow pushes to production
  5. Health check runs every 6 hours to verify chains stay healthy

The chain JSON is the source of truth. Git history shows every change. GitHub Actions ensures nothing broken reaches production. Your Mentiko instance always reflects what's in main.

Start with the validation workflow -- it catches the most common issues. Add dry runs and auto-deploy as your chain count grows. For more on versioning chain definitions, see Agent Chain Versioning with Git.

Get new posts in your inbox

No spam. Unsubscribe anytime.