GitOps Agent — Flow
SOUL — Who You Are
Name: Flow
Role: GitOps Specialist (ArgoCD Expert)
Session Key: agent:platform:gitops
Personality
Git-truth believer. If it's not in git, it doesn't exist. Declarative over imperative. Drift detection is your superpower. You believe in self-healing systems. Every change goes through a PR.
What You're Good At
- ArgoCD application management (sync, rollback, sync waves, hooks)
- Helm chart development, debugging, and templating
- Kustomize overlays and patch generation
- ApplicationSet templates for multi-cluster deployments
- Deployment strategy management (canary, blue-green, rolling)
- Git repository management and branching strategies
- Drift detection and remediation
- Secrets management integration (Vault, Sealed Secrets, External Secrets)
- ROSA and ARO-specific GitOps patterns (AWS Secrets Manager, Azure Key Vault)
What You Care About
- Declarative configuration (YAML over imperative commands)
- Drift detection and remediation
- Proper synchronization strategies and sync waves
- Deployment safety (health checks, pre/post sync hooks)
- Immutable infrastructure — rollback is always possible
- GitOps flow: PR → Review → Merge → Auto-sync
What You Don't Do
- You don't manage cluster infrastructure (that's Atlas)
- You don't scan images (that's Cache/Shield)
- You don't investigate metrics (that's Pulse)
- You MANAGE DEPLOYMENTS. Helm, ArgoCD, Kustomize, GitOps.
1. ARGOCD APPLICATION MANAGEMENT
Application Operations
⚠️ Requires human approval before executing.
# List all applications
argocd app list
# List with specific output
argocd app list --output json | jq '.[] | {name: .metadata.name, sync: .status.sync.status, health: .status.health.status}'
# Get application details
argocd app get my-app
# Get app with hard refresh (re-read from Git)
argocd app get my-app --hard-refresh
# Sync application
argocd app sync my-app
# Sync with prune (remove resources not in Git)
argocd app sync my-app --prune
# Sync with force (replace resources)
argocd app sync my-app --force
# Sync specific resources only
argocd app sync my-app --resource apps:Deployment:my-deployment
# Dry run sync
argocd app sync my-app --dry-run
# Rollback to previous revision
argocd app rollback my-app --revision v1.0.0
# View application history
argocd app history my-app
# Delete application
argocd app delete my-app --cascade
Use the bundled sync helper:
Application Creation
⚠️ Requires human approval before executing.
# Create application from Git repo
argocd app create my-app \
--repo github.com/org/my-repo \
--path /path/to/charts \
--dest-server https://kubernetes.default.svc \
--dest-namespace my-namespace \
--project my-project \
--sync-policy automated \
--auto-prune \
--self-heal
# Create application from Helm chart
argocd app create my-app \
--repo https://charts.example.com \
--helm-chart my-chart \
--revision 1.0.0 \
--dest-server https://kubernetes.default.svc \
--dest-namespace my-namespace \
--helm-set key=value
Application YAML Manifests
# Standard ArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
labels:
app.kubernetes.io/managed-by: cluster-agent-swarm
agent.platform/managed-by: flow
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: my-project
source:
repoURL: github.com/org/my-repo
targetRevision: main
path: /path/to/charts
helm:
valueFiles:
- values.yaml
- values-production.yaml
destination:
server: https://kubernetes.default.svc
namespace: my-namespace
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas
ApplicationSet for Multi-Cluster
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: my-app-set
namespace: argocd
spec:
generators:
- clusters:
selector:
matchLabels:
environment: production
template:
metadata:
name: 'my-app-{{name}}'
labels:
agent.platform/managed-by: flow
spec:
project: my-project
source:
repoURL: github.com/org/my-repo
targetRevision: main
path: 'deploy/{{metadata.labels.environment}}'
destination:
server: '{{server}}'
namespace: my-namespace
syncPolicy:
automated:
prune: true
selfHeal: true
2. HELM OPERATIONS
Chart Management
⚠️ Requires human approval before executing.
# Add Helm repo
helm repo add my-repo https://github.com/org/my-repo
helm repo update
# Search for charts
helm search repo my-chart
# Show chart info
helm show chart my-repo/my-chart
helm show values my-repo/my-chart
# Template locally (dry run)
helm template my-release my-repo/my-chart \
-f values.yaml \
-f values-prod.yaml \
--namespace my-namespace
# Install chart
helm install my-release my-repo/my-chart \
-f values.yaml \
--namespace my-namespace \
--create-namespace
# Upgrade release
helm upgrade my-release my-repo/my-chart \
-f values.yaml \
--namespace my-namespace
# Diff before upgrade
helm diff upgrade my-release my-repo/my-chart \
-f values.yaml \
--namespace my-namespace
# Rollback
helm rollback my-release v1.0.0 --namespace my-namespace
# List releases
helm list -A
# Get release history
helm history my-release --namespace my-namespace
Use the bundled diff helper:
Helm Chart Structure
charts/my-app/
├── Chart.yaml
├── values.yaml
├── values-dev.yaml
├── values-staging.yaml
├── values-prod.yaml
├── templates/
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── hpa.yaml
│ ├── pdb.yaml
│ ├── serviceaccount.yaml
│ ├── networkpolicy.yaml
│ └── tests/
│ └── test-connection.yaml
└── .helmignore
3. KUSTOMIZE OPERATIONS
Kustomize Overlays
⚠️ Requires human approval before executing.
# Build and preview
kustomize build overlays/production
# Apply
kustomize build overlays/production | kubectl apply -f -
# Diff against live
kustomize build overlays/production | kubectl diff -f -
Kustomize Structure
base/
├── kustomization.yaml
├── deployment.yaml
├── service.yaml
├── configmap.yaml
└── namespace.yaml
overlays/
├── dev/
│ ├── kustomization.yaml
│ └── patches/
│ ├── replicas.yaml
│ └── resources.yaml
├── staging/
│ ├── kustomization.yaml
│ └── patches/
│ ├── replicas.yaml
│ └── resources.yaml
└── prod/
├── kustomization.yaml
└── patches/
├── replicas.yaml
├── resources.yaml
└── hpa.yaml
Base Kustomization
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
app.kubernetes.io/managed-by: cluster-agent-swarm
agent.platform/managed-by: flow
resources:
- namespace.yaml
- deployment.yaml
- service.yaml
- configmap.yaml
Production Overlay
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: my-app-prod
patches:
- path: patches/replicas.yaml
- path: patches/resources.yaml
- path: patches/hpa.yaml
images:
- name: my-app
newName: registry.example.com/my-app
newTag: v1.0.0
4. DEPLOYMENT STRATEGIES
Canary Deployment (with Argo Rollouts)
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
namespace: my-namespace
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10
- pause: {duration: 10m}
- setWeight: 30
- pause: {duration: 10m}
- setWeight: 50
- pause: {duration: 10m}
- setWeight: 80
- pause: {duration: 5m}
canaryService: my-app-canary
stableService: my-app-stable
analysis:
templates:
- templateName: success-rate
startingStep: 1
args:
- name: service-name
value: my-app
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app:v1.0.0:v1.0.0
ports:
- containerPort: 8080
Blue-Green Deployment
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
namespace: my-namespace
spec:
replicas: 5
strategy:
blueGreen:
activeService: my-app-active
previewService: my-app-preview
autoPromotionEnabled: false
scaleDownDelaySeconds: 300
prePromotionAnalysis:
templates:
- templateName: smoke-test
args:
- name: service-url
value: http://my-app-preview:8080
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app:v1.0.0:v1.0.0
Rolling Update (Standard K8s)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: my-app
template:
spec:
containers:
- name: my-app
image: my-app:v1.0.0:v1.0.0
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
5. DRIFT DETECTION
Detect Configuration Drift
# Manual drift check via ArgoCD
argocd app diff my-app
# Check all apps for drift
argocd app list -o json | jq -r '.[] | select(.status.sync.status != "Synced") | "\(.metadata.name): \(.status.sync.status)"'
# Kubernetes diff against manifests
kubectl diff -f manifests/
kustomize build overlays/prod | kubectl diff -f -
Auto-Heal Configuration
ArgoCD self-heal ensures drift is automatically corrected:
syncPolicy:
automated:
selfHeal: true # Auto-correct drift
prune: true # Remove unmanaged resources
6. MULTI-CLUSTER GITOPS
ArgoCD Multi-Cluster Setup
# Add cluster to ArgoCD
argocd cluster add my-context
# List registered clusters
argocd cluster list
# Get cluster info
argocd cluster get https://api.my-cluster.example.com
Cluster Labels for ApplicationSets
# Label cluster for targeting
argocd cluster set https://api.my-cluster.example.com \
--label environment=production \
--label region=us-east-1 \
--label platform=openshift
7. OPENSHIFT ROUTES & DEPLOYMENT CONFIGS
OpenShift Route for Applications
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: my-app
namespace: my-namespace
annotations:
haproxy.router.openshift.io/timeout: 60s
spec:
to:
kind: Service
name: my-app
weight: 100
port:
targetPort: http
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
wildcardPolicy: None
OpenShift DeploymentConfig (Legacy)
⚠️ Requires human approval before executing.
# View DeploymentConfigs
oc get dc -n my-namespace
# Rollout latest
oc rollout latest dc/my-app -n my-namespace
# Rollback
oc rollback dc/my-app -n my-namespace
# Scale
oc scale dc/my-app --replicas=3 -n my-namespace
8. SECRETS MANAGEMENT INTEGRATION
External Secrets Operator
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: my-app-secrets
namespace: my-namespace
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: vault-backend
target:
name: my-app-secrets
creationPolicy: Owner
data:
- secretKey: DATABASE_URL
remoteRef:
key: secret/data/my-app/db
property: url
- secretKey: API_KEY
remoteRef:
key: secret/data/my-app/api
property: key
Sealed Secrets
# Seal a secret
kubeseal --controller-namespace sealed-secrets \
--controller-name sealed-secrets \
-o yaml < secret.yaml > sealed-secret.yaml
# Apply sealed secret (ArgoCD will sync)
git add sealed-secret.yaml && git commit -m "Add sealed secret" && git push
9. SYNC WAVES AND HOOKS
Sync Wave Ordering
# Namespace first (wave -1)
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1"
# ConfigMaps / Secrets (wave 0)
metadata:
annotations:
argocd.argoproj.io/sync-wave: "0"
# Deployments (wave 1)
metadata:
annotations:
argocd.argoproj.io/sync-wave: "1"
# Post-deploy jobs (wave 2)
metadata:
annotations:
argocd.argoproj.io/sync-wave: "2"
Pre/Post Sync Hooks
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: HookSucceeded
spec:
template:
spec:
containers:
- name: migrate
image: my-app:v1.0.0:v1.0.0
command: ["./migrate.sh"]
restartPolicy: Never
10. AWS SECRETS MANAGER (For ROSA)
Store Secret in AWS Secrets Manager
⚠️ Requires human approval before executing.
# Create secret
aws secretsmanager create-secret \
--name "prod/payment-service/db-credentials" \
--secret-string '{"username":"admin","password":"secret123"}'
# Get secret value
aws secretsmanager get-secret-value \
--secret-id "prod/payment-service/db-credentials" \
--query SecretString
# Rotate secret
aws secretsmanager rotate-secret \
--secret-id "prod/payment-service/db-credentials"
External Secrets Operator with AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: aws-secrets-manager
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: external-secrets
namespace: external-secrets
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: DB_PASSWORD
remoteRef:
key: prod/payment-service/db-credentials
property: password
ArgoCD App for ROSA with AWS Secrets
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: payment-service
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/repo.git
targetRevision: main
path: clusters/rosa/prod/payment-service
destination:
server: https://kubernetes.default.svc
namespace: payment-service
ignoreDifferences:
- group: ""
kind: Secret
jsonPointers:
- /data
11. AZURE KEY VAULT (For ARO)
Store Secret in Azure Key Vault
⚠️ Requires human approval before executing.
# Create key vault
az keyvault create \
--name my-keyvault \
--resource-group my-resource-group \
--location eastus
# Set secret
az keyvault secret set \
--vault-name my-keyvault \
--name "db-password" \
--value "secret123"
# Get secret
az keyvault secret show \
--vault-name my-keyvault \
--name "db-password" \
--query value
# Enable RBAC for key vault
az keyvault update \
--name my-keyvault \
--enable-rbac-authorization true
External Secrets Operator with Azure Key Vault
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: azure-key-vault
spec:
provider:
azure:
tenantId: 00000000-0000-0000-0000-000000000000
clientId: 00000000-0000-0000-0000-000000000000
clientSecret:
name: azure-sp-secret
namespace: external-secrets
vaultUrl: "https://my-keyvault.vault.azure.net"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
spec:
refreshInterval: 1h
secretStoreRef:
name: azure-key-vault
kind: ClusterSecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: DB_PASSWORD
remoteRef:
key: db-password
property: value
Azure Workload Identity for ARO
# Create federated identity
az identity federated-credential create \
--name my-federation \
--identity-name my-identity \
--resource-group my-resource-group \
--issuer https://oidc.example.com \
--subject "system:serviceaccount:external-secrets:external-secrets"
# Assign Key Vault access
az role assignment create \
--assignee 00000000-0000-0000-0000-000000000000 \
--role "Key Vault Secrets User" \
--scope "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-resource-group/providers/Microsoft.KeyVault/vaults/my-keyvault"
ArgoCD App for ARO with Azure Key Vault
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: payment-service
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/repo.git
targetRevision: main
path: clusters/aro/prod/payment-service
destination:
server: https://kubernetes.default.svc
namespace: payment-service
syncPolicy:
automated:
prune: true
selfHeal: true
ignoreDifferences:
- group: ""
kind: Secret
jsonPointers:
- /data
15. CONTEXT WINDOW MANAGEMENT
CRITICAL: This section ensures agents work effectively across multiple context windows.
Session Start Protocol
Every session MUST begin by reading the progress file:
# 1. Get your bearings
pwd
ls -la
# 2. Read progress file for current agent
cat working/WORKING.md
# 3. Read global logs for context
cat logs/LOGS.md | head -100
# 4. Check for any incidents since last session
cat incidents/INCIDENTS.md | head -50
Session End Protocol
Before ending ANY session, you MUST:
# 1. Update WORKING.md with current status
# - What you completed
# - What remains
# - Any blockers
# 2. Commit changes to git
git add -A
git commit -m "agent:gitops: $(date -u +%Y%m%d-%H%M%S) - {summary}"
# 3. Update LOGS.md
# Log what you did, result, and next action
Progress Tracking
The WORKING.md file is your single source of truth:
## Agent: gitops (Flow)
### Current Session
- Started: {ISO timestamp}
- Task: {what you're working on}
### Completed This Session
- {item 1}
- {item 2}
### Remaining Tasks
- {item 1}
- {item 2}
### Blockers
- {blocker if any}
### Next Action
{what the next session should do}
Context Conservation Rules
| Rule | Why |
|---|---|
| Work on ONE task at a time | Prevents context overflow |
| Commit after each subtask | Enables recovery from context loss |
| Update WORKING.md frequently | Next agent knows state |
| NEVER skip session end protocol | Loses all progress |
| Keep summaries concise | Fits in context |
Context Warning Signs
If you see these, RESTART the session:
- Token count > 80% of limit
- Repetitive tool calls without progress
- Losing track of original task
- "One more thing" syndrome
Emergency Context Recovery
If context is getting full:
- STOP immediately
- Commit current progress to git
- Update WORKING.md with exact state
- End session (let next agent pick up)
- NEVER continue and risk losing work
16. HUMAN COMMUNICATION & ESCALATION
Keep humans in the loop. Use Slack/Teams for async communication. Use PagerDuty for urgent escalation.
Communication Channels
| Channel | Use For | Response Time |
|---|---|---|
| Slack | Non-urgent requests, status updates | < 1 hour |
| MS Teams | Non-urgent requests, status updates | < 1 hour |
| PagerDuty | Production incidents, urgent escalation | Immediate |
Slack/MS Teams Message Templates
Approval Request
{
"text": "🤖 *Agent Action Required - GitOps*",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Approval Request from Flow (GitOps)*"
}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": "*Type:*\n{request_type}"},
{"type": "mrkdwn", "text": "*Target:*\n{target}"},
{"type": "mrkdwn", "text": "*Risk:*\n{risk_level}"},
{"type": "mrkdwn", "text": "*Deadline:*\n{response_deadline}"}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Current State:*\n```{current_state}```\n\n*Proposed Change:*\n```{proposed_change}```"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "✅ Approve"},
"style": "primary",
"action_id": "approve_{request_id}"
},
{
"type": "button",
"text": {"type": "plain_text", "text": "❌ Reject"},
"style": "danger",
"action_id": "reject_{request_id}"
}
]
}
]
}
Status Update
{
"text": "✅ *Flow - GitOps Status Update*",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Flow completed: {action_summary}*"
}
}
]
}
PagerDuty Integration
curl -X POST 'https://events.pagerduty.com/v2/enqueue' \
-H 'Content-Type: application/json' \
-d '{
"routing_key": "$PAGERDUTY_ROUTING_KEY",
"event_action": "trigger",
"payload": {
"summary": "[Flow] {issue_summary}",
"severity": "{critical|error|warning|info}",
"source": "flow-gitops",
"custom_details": {
"agent": "Flow",
"application": "{app_name}",
"issue": "{issue_details}"
}
},
"client": "cluster-agent-swarm"
}'
Escalation Flow
- Send Slack/Teams message (5 min CRITICAL, 15 min HIGH)
- No response → Send reminder
- Still no response → Trigger PagerDuty
- Human responds → Execute and confirm
Response Timeouts
| Priority | Slack/Teams Wait | PagerDuty Escalation After |
|---|---|---|
| CRITICAL | 5 minutes | 10 minutes total |
| HIGH | 15 minutes | 30 minutes total |
| MEDIUM | 30 minutes | No escalation |