Automating ArgoCD GitOps with an AI Agent: From OutOfSync Noise to Safe Reconciliation
Someone ran a kubectl patch at 2 a.m. to stop the bleeding during an incident. It worked. The pods stabilized, the pager went quiet, and nobody wrote it down. At 2:03, ArgoCD noticed live state no longer matched Git, flipped the app to OutOfSync, and, because selfHeal was on for that project, reverted the hotfix to the broken manifest. The incident reopened. That's ArgoCD automation working exactly as configured and doing exactly the wrong thing. Auto-sync reconciles state. It has no idea whether the drift it just erased was an accident or the only thing keeping production up.
Part one mapped the failure patterns: apps parked OutOfSync until the status lost its meaning, drift from manual hotfixes, Degraded health nobody drills into, sync waves failing halfway, orphans after chart refactors. Part two built the native toolkit: sync policies chosen deliberately, sync windows, Lua health checks, argocd app diff, history and rollback, hooks, and ignoreDifferences.
Part two ended on its honest ceiling. selfHeal reverts every drift or keeps every drift. It can't tell a hotfix worth committing from an accident worth reverting. That judgment (the diff, the classification, the safe action) is still human work, and it comes back with every drift event. This part covers the layer that does it: CloudThinker triaging drift and app health continuously, proposing the safe reconciliation, and tracking deployments across sync waves. ArgoCD stays exactly where it is.
Why rules stop where judgment starts
Every native feature from part two is a rule written in advance. selfHeal is a rule. ignoreDifferences is a rule. A notification trigger is a rule. Rules work for the drift you predicted.
Real drift doesn't announce its category. An OutOfSync on orders-worker could be a 2 a.m. fix, an HPA doing its job, or a Helm values change that rendered something nobody intended. Each one needs a different action, and you only find out which by looking at evidence: the diff, who wrote the field, what Git did recently. An engineer does that naturally. A rule can only take the branch someone drew for it.
Connecting ArgoCD: an API token, about five minutes
01GitOps
ArgoCD
Applications and projectsSync status, health, history, diffs
Managed resourcesResource tree, events, rollout state
Source repositoriesCommits behind each synced revision
Read-only RBAC role
02Triage
CloudThinker
- Watch the fleetEvery OutOfSync and Degraded, every app
- Classify the driftHotfix, controller mutation or chart bug
- Propose the actionCommit, sync, ignore rule or rollback
No controller changes
03Where the team works
Chat and review
Slack or TeamsFindings and approvals in the thread
Pull requestsHotfixes codified for normal review
- JiraFollow-ups with the evidence linked
Git stays the source of truth
CloudThinker connects the way your security team would want any external reader to: a dedicated local account (or an SSO-mapped one) with a scoped API token, granted a read-only RBAC policy on the ArgoCD API server. That means get on applications, projects and repositories, nothing more. No write access at connection time, no controller changes, no admission webhook.
A minimal grant looks like this:
# argocd-cm
data:
accounts.cloudthinker: apiKey
---
# argocd-rbac-cm
data:
policy.csv: |
p, role:cloudthinker-readonly, applications, get, */*, allow
p, role:cloudthinker-readonly, projects, get, *, allow
p, role:cloudthinker-readonly, repositories, get, *, allow
g, cloudthinker, role:cloudthinker-readonly
Then generate the token and paste it, with the API server URL, into CloudThinker:
argocd account generate-token --account cloudthinker
The first scan runs against the same API the argocd CLI uses in part two, so the evidence you get back is the evidence you'd have gathered by hand with argocd app get and argocd app diff. The ArgoCD connection guide lists the exact RBAC lines for your review record.
What happens when an app drifts
01Signal
OutOfSync
An app drifts or goes Degraded. CloudThinker picks it up on the next poll.
02Evidence
Diff and history
The rendered diff, field managers, recent commits and sync operations.
03Judgment
Classify
Hotfix, controller mutation or chart bug, with the evidence for the call.
04Output
Proposal
Commit the hotfix, sync, add an ignore rule, or roll back. Never a silent revert.
Auto-sync answers "does live match Git?" with yes or no. CloudThinker answers the question that decides what to do: why don't they match? For each OutOfSync app it pulls the same rendered diff the CLI produces, and classifies it.
- Manual hotfix. A live-only change with no matching commit: an image tag bumped by hand, a replica count patched, an env var added during an incident. Reverting it silently is the wrong move. The finding is "this is a hotfix, here's the diff, decide whether to commit it or roll it back", not a sync.
- Controller or mutation drift. A field an HPA, mutating webhook or operator owns:
spec.replicasunder an HPA, an injected sidecar, a defaultedimagePullPolicy. It isn't a deviation from intent. The long-term fix is a narrowignoreDifferencesrule, not a sync and not an alert on every reconcile loop. - Chart or template bug. The rendered manifest changed because a Helm chart bump or Kustomize edit produced something unintended: an orphan after a refactor, a renamed selector, a dropped label. Here the drift is real and Git is wrong. Syncing would spread the mistake.
Each classification arrives with its evidence: the diff hunk, the last synced commit, the field manager that wrote the value, and which side, Git or live, holds the intended state.
One drift, investigated
Here's a single case end to end. The details are illustrative.
At 02:31, orders-worker in production goes OutOfSync. The project runs automated sync with selfHeal: false, so nothing reverts it. CloudThinker picks it up and starts with the three readings above.
The diff. One hunk: the container image is orders-worker:1.8.3 live and 1.8.2 in Git. Image tags aren't controller-owned, so reading two is unlikely.
Who wrote it. The Deployment's managedFields show a kubectl-patch manager writing the image at 02:07. A person, not a pipeline.
What Git did. No commit to the orders-worker values in four days, so reading three is out. The incident channel shows INC-4412 opened at 01:50: connection leak in orders-worker. Release notes for 1.8.3 mention a connection-pool fix.
The finding, posted in the incident thread:
orders-worker (prod) is OutOfSync: manual hotfix, keep it. Live runs
1.8.3, patched by hand at 02:07 during INC-4412. Git still pins1.8.2, the version with the connection leak. Syncing now would reintroduce the incident. Proposed: a pull request settingimage.tag: 1.8.3invalues-prod.yaml. After merge the app returns to Synced with no rollout. Not proposed: a sync, or a rollback.
The on-call approves. The pull request goes through normal review. The next person to enable selfHeal on that project doesn't undo last night.
Drill into Degraded health, not around it
An app can be Synced and still Degraded, and a Degraded nobody opens is a core waste pattern from part one. When health drops, CloudThinker inspects the Kubernetes state behind the rollup: availableReplicas against desired, CrashLoopBackOff reasons and last exit codes, failing readiness probes, a rollout stuck past its progressDeadlineSeconds, a PVC pending on a missing StorageClass. The finding is the resource-level cause, not the color of the tile.
Track deployments across sync waves
When a sync stalls, CloudThinker identifies the wave it stopped in and why: a PreSync hook that failed, a wave whose resources never went Healthy so the next never started, an app-of-apps child left behind. "Which commit is live in prod, and where did last night's sync stall?" becomes a question you ask in chat instead of a walk through argocd app history across a dozen apps.
Nothing syncs outside the controls you set
Findings imply actions. CloudThinker prepares the specific action with evidence (sync this app because the drift is a chart bug and Git is right; commit this hotfix; add this ignoreDifferences block for the HPA-owned field; roll back to the last healthy revision). It acts within Auto Mode, so you decide what runs on its own and what waits for approval.
One behavior always holds: CloudThinker never reverts a manual hotfix on its own. A live-only change is escalated as a keep-or-revert decision with the diff attached. Everything lands in an audit trail: what drifted, how it was classified, what was proposed, who approved, what synced, and when.
What a first pass typically finds
These numbers are illustrative, for a mid-market setup of roughly 90 Applications across 3 clusters, with app-of-apps in a couple of projects. Yours will differ.
| Finding | Detail | Typical impact |
|---|---|---|
| Stale OutOfSync | 12 apps OutOfSync for 30+ days, status long ignored | Real drift invisible in the noise |
| Manual hotfix drift | 4 live-only patches with no matching commit, 1 in prod | selfHeal would silently revert an incident fix |
| Controller-owned noise | HPA and sidecar-injected fields flagged on every loop | Constant false drift, candidates for ignoreDifferences |
| Degraded, undrilled | 3 Synced-but-Degraded apps, 1 stuck past progressDeadlineSeconds | Broken workload behind a green sync status |
| Stalled sync waves | 2 syncs halted on a failed PreSync hook, later waves never ran | Partial deploys, no rollback triggered |
| Orphaned resources | 9 resources left after a chart refactor, no longer in Git | Drift, cost and confusing diffs |
Almost none of it is exotic. It's the part-one inventory, surfaced automatically, and each row arrives with a classification and a proposed action that waits for approval.
Why this beats the traditional setup
selfHeal and notifications | With CloudThinker on top | |
|---|---|---|
| A 2 a.m. hotfix | Reverted, or kept with no record | Classified as a hotfix, committed through review |
| Controller noise | Permanent yellow until someone writes a rule | Flagged with the exact ignoreDifferences block |
| Degraded app | A Slack message with a color | The failing resource and its cause |
| Stalled sync | Failed phase, dig through history | The wave, the hook and what's half-applied |
| Actions | Whatever the policy flag says, every time | Proposed with evidence, gated by Auto Mode, audited |
The deeper difference is where judgment lives. Native automation encodes a decision once and applies it to every drift. CloudThinker makes the decision per drift, from evidence, and writes the reasoning down so you can check it.
Prompts to try in your first session
Ask in plain language. Each answer cites the ArgoCD data behind it (the same app get, app diff and app history evidence from part two), so you can verify it rather than trust it.
"Why is payments-api OutOfSync? Diff Git against live, tell me whether it's a manual hotfix, controller drift or a chart bug, and show me the diff. Propose only, change nothing."
"Which apps are Synced but Degraded right now? For each one, drill into the Kubernetes resources and tell me the actual cause, not just the health status."
"Walk me through last night's sync of the platform app-of-apps: which wave stalled, which hook failed, and which commit is live in prod versus what's in Git."
"List every OutOfSync app whose only drift is a field an HPA or webhook owns, and draft the narrowest ignoreDifferences rule for each."
What the agents won't do
"AI with access to your deploy system" should raise these questions. Here are the answers.
- Read-only by default. The API token grants no sync or write permission. Triggering a sync, opening a pull request or changing an app spec needs scoped write access you grant explicitly, and still goes through Auto Mode.
- No silent reverts of hotfixes. CloudThinker never reconciles away a live-only change the way
selfHealdoes. A hotfix is escalated as a keep-or-revert decision with the diff attached. - No invisible changes. Every proposal and execution is in the audit trail with its evidence and approver.
- No architectural decisions. It will show you that a chart refactor orphaned nine resources. Whether to delete them or restructure the app-of-apps is an engineering call, and it stays yours.
Keep the part-two setup. Sync policies, windows, health checks and ignore rules are the right tool for the paths that are already deterministic. Put the agent on the part that never was.
Closing the loop
Part one showed where GitOps drifts. Part two showed how far sync policies, health checks and ignoreDifferences go, and that they stop at intent. What's left is the per-app decision of whether a drift is a hotfix to keep or an accident to revert. Teams that move from ignored OutOfSync columns to continuous drift triage typically cut time-to-answer on a drifted or Degraded app from a terminal session to under a minute, and stop letting selfHeal make decisions it was never qualified to make.
Related reading
- Auto Mode: decide what runs on its own
- CloudThinker Connections: how CloudThinker reaches your stack
- AI agent for Kubernetes operations
- CloudThinker × Terraform: the full lifecycle
Try CloudThinker free with 100 premium credits and no card required, and follow the ArgoCD connection guide to see your own first findings within the hour.
