Skip to content

Versioning & GitOps

How to version control your agents, track prompt changes, and implement GitOps workflows.

Why Version Control Agents?

Agent .md files are code. Treat them like source code:

  1. Track changes - See who changed what and when
  2. Review changes - Code review for prompts before deployment
  3. Rollback - Revert to previous working version
  4. Collaboration - Multiple people can work on agents
  5. Audit trail - Compliance and debugging
  6. Environment promotion - Dev → Staging → Prod

Git Setup

Initialize Repository

cd /Users/zfab/repos/agentmd

# Initialize git (if not already)
git init

# Add .gitignore
cat > .gitignore << 'EOF'
# Environment files (NEVER commit these)
.env
.env.local
.env.*.local

# Secrets
secrets/
*.key
*.pem

# Output (generated by agents)
output/

# Database
data/
*.db
*.db-journal

# Python
__pycache__/
*.pyc
.venv/
venv/

# OS
.DS_Store
Thumbs.db
EOF

git add .gitignore
git commit -m "Add .gitignore"

Directory Structure

agentmd/
├── .git/
├── .gitignore
├── .env                  # Gitignored
├── .env.example          # Committed (template)
├── workspace/
│   ├── production/       # Production agents
│   │   ├── daily-report.md
│   │   └── log-monitor.md
│   ├── staging/          # Staging agents
│   │   └── new-feature.md
│   └── dev/              # Development agents
│       └── experiment.md
├── output/               # Gitignored
└── data/                 # Gitignored

Commit Agent Files

# Add all agent files
git add workspace/

# Commit
git commit -m "Add daily report and log monitor agents"

Best practices: - Commit .md agent files ✓ - Commit .env.example template ✓ - Never commit .env with secrets ✗ - Never commit output/ generated files ✗ - Never commit data/ database files ✗

Git Diff for Prompts

Track Prompt Changes

# View changes before committing
git diff workspace/daily-report.md

Example diff:

diff --git a/workspace/daily-report.md b/workspace/daily-report.md
index abc123..def456 100644
--- a/workspace/daily-report.md
+++ b/workspace/daily-report.md
@@ -5,7 +5,7 @@ model:
   name: gpt-4
 triggers:
   - type: schedule
-    cron: "0 9 * * *"  # 9 AM daily
+    cron: "0 8 * * *"  # 8 AM daily
 ---

-Generate a summary of yesterday's activities.
+Generate a detailed summary of yesterday's activities, including:
+1. Key metrics
+2. Error count
+3. Recommendations

Review Config Changes

# See only frontmatter changes
git diff workspace/daily-report.md | grep -A 10 "^---"

Compare Versions

# Compare current with last commit
git diff HEAD workspace/daily-report.md

# Compare with specific commit
git diff abc123 workspace/daily-report.md

# Compare two branches
git diff main staging -- workspace/daily-report.md

Rollback

Revert to Previous Version

Scenario: Agent started producing bad output after a prompt change.

# View history
git log --oneline workspace/daily-report.md

# Output:
# def456 Update daily report schedule
# abc123 Add error count to report
# 789xyz Initial daily report agent

# Revert to before bad change
git checkout abc123 -- workspace/daily-report.md

# Commit the revert
git commit -m "Revert daily report to working version (abc123)"

Undo Last Commit

# Undo commit but keep changes
git reset --soft HEAD~1

# Undo commit and discard changes (CAREFUL!)
git reset --hard HEAD~1

Revert Specific Change

# Revert a specific commit (creates new commit)
git revert def456

# This creates an inverse commit that undoes def456

View File at Specific Commit

# View without checking out
git show abc123:workspace/daily-report.md

# Save to file for comparison
git show abc123:workspace/daily-report.md > /tmp/old-version.md
diff /tmp/old-version.md workspace/daily-report.md

Pull Request Workflow

Branching Strategy

# Main branches
main         # Production
staging      # Staging environment
dev          # Development

# Feature branches
feature/add-new-agent
feature/improve-daily-report
fix/log-monitor-timeout

Create Feature Branch

# Create and switch to feature branch
git checkout -b feature/add-email-digest

# Create new agent
cat > workspace/staging/email-digest.md << 'EOF'
---
name: email-digest
model:
  provider: openai
  name: gpt-3.5-turbo
triggers:
  - type: schedule
    cron: "0 8 * * *"
---

Generate a daily email digest from /workspace/data/emails/.
EOF

# Commit
git add workspace/staging/email-digest.md
git commit -m "Add email digest agent"

Push and Create PR

# Push feature branch
git push origin feature/add-email-digest

# Create PR (using GitHub CLI)
gh pr create \
  --title "Add email digest agent" \
  --body "New agent to generate daily email digest. Runs at 8 AM daily." \
  --base main \
  --head feature/add-email-digest

PR Review Checklist

Reviewer checks:

  • Agent config is valid (agentmd validate)
  • No secrets in frontmatter
  • Appropriate paths.read and paths.write restrictions
  • Schedule frequency is reasonable
  • max_tokens and timeout are set
  • Prompt is clear and concise
  • Temperature appropriate for task
  • Model choice is cost-effective
  • Tested in staging environment

Review command:

# Checkout PR branch
gh pr checkout 123

# Validate agent
agentmd validate workspace/staging/email-digest.md

# Test run
agentmd run email-digest --dry-run

# Review diff
git diff main...feature/add-email-digest

Merge and Deploy

# Merge PR (via GitHub UI or CLI)
gh pr merge 123 --squash

# Pull latest main
git checkout main
git pull

# Deploy to production
# (See deployment section below)

Configuration History

Track Config Changes Over Time

# View all changes to a specific field
git log -p workspace/daily-report.md | grep -A 5 "cron:"

# Output:
# commit def456
#     cron: "0 8 * * *"
#
# commit abc123
#     cron: "0 9 * * *"

Annotate Lines

# See who last changed each line
git blame workspace/daily-report.md

# Output:
# abc123 (Alice 2026-03-01) ---
# abc123 (Alice 2026-03-01) name: daily-report
# def456 (Bob   2026-03-10) model:
# def456 (Bob   2026-03-10)   provider: openai

Compare Configurations Across Environments

# Compare dev vs staging
git diff dev staging -- workspace/daily-report.md

# Compare staging vs production
git diff staging main -- workspace/daily-report.md

GitOps Deployment

Environment Branches

main      → Production
staging   → Staging
dev       → Development

Promotion flow:

Dev branch → Test → Merge to staging branch
Staging branch → Test → Merge to main branch
Main branch → Auto-deploy to production

Automatic Deployment

GitHub Actions (.github/workflows/deploy.yml):

name: Deploy Agents

on:
  push:
    branches: [main]
    paths: ['workspace/**/*.md']

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

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.13'

      - name: Install Agent.md
        run: |
          pip install -e .
          pip install -e ".[all]"

      - name: Validate agents
        run: |
          for agent in workspace/production/*.md; do
            agentmd validate "$agent"
          done

      - name: Deploy to production server
        env:
          SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
        run: |
          # Copy agents to production server
          scp workspace/production/*.md user@prod-server:/opt/agentmd/workspace/

          # Restart agent runtime
          ssh user@prod-server 'systemctl restart agentmd'

Manual Deployment

# 1. Validate all agents
for agent in workspace/production/*.md; do
  agentmd validate "$agent" || exit 1
done

# 2. Copy to production server
rsync -avz workspace/production/ user@prod-server:/opt/agentmd/workspace/

# 3. Restart runtime on server
ssh user@prod-server 'systemctl restart agentmd'

Blue-Green Deployment

Run two environments side-by-side:

# Blue environment (current production)
/opt/agentmd-blue/
  workspace/production/

# Green environment (new version)
/opt/agentmd-green/
  workspace/production/

# Switch: Update symlink
ln -sfn /opt/agentmd-green /opt/agentmd-current

# Rollback: Switch back
ln -sfn /opt/agentmd-blue /opt/agentmd-current

Tagging Releases

Create Release Tag

# Tag current version
git tag -a v1.0.0 -m "Release v1.0.0: Add email digest agent"

# Push tag
git push origin v1.0.0

View Releases

# List all tags
git tag

# Show tag details
git show v1.0.0

Deploy Specific Version

# Checkout specific version
git checkout v1.0.0

# Deploy
rsync -avz workspace/production/ user@prod-server:/opt/agentmd/workspace/

Collaboration Workflow

Multiple Contributors

Workflow: 1. Create feature branch from main 2. Make changes 3. Create PR 4. Review by team member 5. Merge to staging for testing 6. Merge to main for production

Example:

# Alice: Add new agent
git checkout -b feature/alice-new-agent
# ... work ...
git push origin feature/alice-new-agent
gh pr create

# Bob: Review
gh pr checkout 123
agentmd validate workspace/staging/new-agent.md
gh pr review 123 --approve

# Alice: Merge to staging
gh pr merge 123 --squash

Prevent Conflicts

Use separate files:

workspace/
  alice/
    agent-a.md
  bob/
    agent-b.md

Or use feature branches:

# Alice works on feature/alice-agent
# Bob works on feature/bob-agent
# No conflicts because different branches

Audit Trail

Track All Changes

# Full history of an agent
git log --follow --patch workspace/production/daily-report.md

Who Changed What When

# See all commits by author
git log --author="Alice" workspace/

# See changes in date range
git log --since="2026-03-01" --until="2026-03-10" workspace/

Generate Changelog

# Changes since last tag
git log v1.0.0..HEAD --oneline workspace/

# Detailed changelog
git log v1.0.0..HEAD --pretty=format:"- %s (%an, %ar)" workspace/

Example output:

- Add email digest agent (Alice, 2 days ago)
- Update daily report schedule (Bob, 1 week ago)
- Fix log monitor timeout (Charlie, 2 weeks ago)

Best Practices

Commit Messages

Good:

Add email digest agent

Creates daily email digest at 8 AM.
Reads from /workspace/data/emails/.
Outputs to /output/digest.html.

Bad:

Update file

Conventional Commits:

feat: add email digest agent
fix: resolve log monitor timeout
docs: update daily report description
refactor: simplify prompt for better performance

Branching Convention

main               # Production (protected)
staging            # Staging (protected)
dev                # Development

feature/<name>     # New features
fix/<name>         # Bug fixes
refactor/<name>    # Refactoring
docs/<name>        # Documentation

PR Description Template

## Description
Brief description of changes.

## Agent Changes
- Added: `email-digest.md`
- Modified: `daily-report.md` (changed schedule from 9 AM to 8 AM)

## Testing
- [ ] Validated with `agentmd validate`
- [ ] Tested in dev environment
- [ ] Tested in staging environment
- [ ] Reviewed token usage
- [ ] Reviewed permissions

## Deployment Plan
Deploy to production after 2 days in staging with no issues.

Protected Branches

GitHub settings:

Branch: main
- Require pull request before merging
- Require approvals: 1
- Require status checks to pass: ✓
  - validate-agents
  - test-run
- Include administrators: ✓

Recovery Scenarios

Accidentally Committed Secret

# Remove secret from file
vim workspace/daily-report.md  # Remove secret

# Amend last commit
git add workspace/daily-report.md
git commit --amend

# Force push (if already pushed - CAREFUL!)
git push --force origin feature/daily-report

# Better: Use git-secrets or pre-commit hooks to prevent

Lost Work

# View reflog (all commits, even deleted)
git reflog

# Output:
# abc123 HEAD@{0}: reset: moving to HEAD~1
# def456 HEAD@{1}: commit: Add new agent

# Recover deleted commit
git checkout def456

Diverged Branches

# Pull with rebase to keep linear history
git pull --rebase origin main

# Or merge
git pull origin main

Monitoring and Alerts

Track Deployment Success

# After deployment, check logs
ssh user@prod-server 'agentmd logs --since "5 minutes ago"'

Alert on Failed Validation

# Pre-commit hook (.git/hooks/pre-commit)
#!/bin/bash
for agent in workspace/**/*.md; do
  agentmd validate "$agent" || exit 1
done

Integration Tests

# Test agents before merge
for agent in workspace/staging/*.md; do
  agentmd run $(basename $agent .md) --dry-run || exit 1
done