> Per AI Blitz Plan §skills.infra. Layer: 1. Repo: devops-infra-helm-charts. # Skill — `diagnose-deployment` > **Layer:** Layer 2 (advisory) — read-only diagnosis. **No writes, no Sync, no PRs.** > **Scope:** infra deployments managed via this repo on a target cluster. This skill produces a structured diagnosis for a single infra deployment / Helm release, walking the right runbook decision tree based on the observed symptom. It outputs a hypothesis + the recommended next-action procedure or skill — and stops. The agent does not chain into a write. --- ## When to Use Triggers like: - "What's wrong with `` on ``?" - "Diagnose `` — pods are restarting." - "Why is `` showing OutOfSync in Argo?" - "Service `` returning 503 on `` — check the infra side." Do **not** use this skill for: - Cluster-wide health — use [check-cluster-health.md](check-cluster-health.md). - Application code bugs — out of scope, hand off to app team. - Direct fix application — invoke the procedure / skill the diagnosis points at, separately. --- ## Input Required: ```yaml release: # e.g. mimir, contour-internal-0, kube-state-metrics cluster: # e.g. k8s-supply-prd-ase1 ``` Optional: ```yaml namespace: # if non-default for the chart symptom: # what the user observed kube_context: ``` --- ## Steps ### Step 1 — Locate the values file ```bash ls helm-overrides///custom-values.yaml 2>/dev/null \ || find helm-overrides/ -maxdepth 2 -name 'custom-values.yaml' -path "**" ``` If nothing: surface "no override file found for `` on `` — is it onboarded? does the directory name match the release?" Stop. ### Step 2 — Pull live state ```bash CTX= NS= # Argo Application kubectl --context=$CTX -n argocd get application | grep kubectl --context=$CTX -n argocd get application - -o yaml | yq e '.status' # Pods kubectl --context=$CTX -n $NS get pods -l app.kubernetes.io/instance= -o wide # Recent events kubectl --context=$CTX -n $NS get events --sort-by=.lastTimestamp | tail -20 # Logs (last 100 lines) kubectl --context=$CTX -n $NS logs -l app.kubernetes.io/instance= --tail=100 --all-containers 2>/dev/null | tail -50 ``` ### Step 3 — Classify the symptom Walk this matrix to pick the right runbook: | Observed | Branch | |----------|--------| | Argo Application `OutOfSync` or `SyncFailed` | §A → [argocd-sync-failure.md](../../docs/platform/runbooks/argocd-sync-failure.md) | | Pods `Pending` or scheduled on wrong node | §B → [pod-pending-scheduling.md](../../docs/platform/runbooks/pod-pending-scheduling.md) | | Pods `CrashLoopBackOff` referencing missing Secret | §C → [vault-unavailable.md](../../docs/platform/runbooks/vault-unavailable.md) | | Ingress 5xx / 503 / no endpoints | §D → [ingress-down.md](../../docs/platform/runbooks/ingress-down.md) | | Metrics gap / Grafana blank / silent alert | §E → [metrics-gap.md](../../docs/platform/runbooks/metrics-gap.md) | | Pods `Running` but app erroring | §F — out of scope (app team) | Multiple symptoms? Pick the most-upstream (sync first, then scheduling, then secrets, then ingress, then metrics). ### Step 4 — Walk the chosen runbook For the chosen branch, walk its decision tree to the lowest leaf. Record at each node: - Check performed. - Observed value (from step 2's data). - Branch taken. For runbooks the agent can fully resolve from kubectl output (e.g. `pod-pending-scheduling.md §1` — taint mismatch), name the leaf. For runbooks needing data the agent doesn't have (Vault server-side, GCP IAM), mark the leaf "needs operator with [X] access" and stop. ### Step 5 — Cross-check the values file Pull the relevant values keys for the symptom: ```bash yq e '{ image: .image, resources: .resources, nodeSelector: .nodeSelector, tolerations: .tolerations, persistence: .persistence, serviceAccount: .serviceAccount, existingSecret: .existingSecret }' helm-overrides///custom-values.yaml ``` Look for: - Image tag suspicious (`latest`, recent bump?). - nodeSelector key style mismatches the cluster (Autopilot vs standard). - existingSecret references a Secret that step 2 showed missing. ### Step 6 — Recent merges ```bash git log --since='48 hours ago' --oneline -- helm-overrides/// ``` If a recent merge is implicated (timing matches the symptom), the diagnosis names the merge and recommends revert as the first remediation. ### Step 7 — Produce the report ```markdown # Diagnosis: on **Values file:** `helm-overrides///custom-values.yaml` **Namespace:** `` **Inferred kube-context:** `` **Symptom (user-reported):** `` ## Live state (snapshot) - Argo Application: ` / ` - Pods: ``, states: `` - Recent events (relevant): ``` ``` - Pod logs (relevant): ``` ``` ## Symptom classification **Branch chosen:** § **Why:** ## Decision-tree walk 1. — checked: `` — observed: `` → branch `` 2. — checked: `` — observed: `` → branch `` ... ## Values-file cross-check - [✅/⚠️/❌] image.tag: `` — - [✅/⚠️/❌] nodeSelector key style matches cluster: `` - [✅/⚠️/❌] existingSecret: `` — - [✅/⚠️/❌] persistence.storageClass: `` — ## Recent merges (last 48h) - ## Root-cause hypothesis ## Recommended next-action - **Procedure / skill to invoke:** `` - **Layer:** 1 (values PR) | 2 (advisory — operator action) | 3 (refusal — out of repo scope) - **Rough diff intent (if Layer 1):** "Edit `helm-overrides///custom-values.yaml` to ." (Do not generate the diff in this skill.) ## Cannot resolve from this repo ``` --- ## Pattern Reference - Decision-tree branches map 1:1 to the runbook section headers (`§A` ↔ `argocd-sync-failure.md`, etc.). - The "two-page rule": every diagnosis cites at most two pages — one runbook (the branch) and one procedure/skill (the recommended next-action). Long chains imply the agent should stop and ask. --- ## Gotchas (Layer constraints, common mistakes) 1. **Read-only.** No writes. No PR. If the user says "now fix it," respond with "invoking [procedure/skill]" and stop in this skill — chain to the next as a fresh invocation. 2. **Stop at the first solid hypothesis.** Don't keep walking trees once one fits. Surface the hypothesis with a confidence note and the recommended next-action. 3. **`OutOfSync` is sometimes intentional** during a blue-green or manual-sync window. Cross-check with the cluster owner before declaring a problem. 4. **`Running but erroring` is app-team territory.** Don't grep app logs for application bugs — surface the pod is `Running` and hand off. 5. **Don't curl production endpoints.** All probes are kubectl-internal or via an in-cluster curl Pod ([SANCTITY_RULES R3](../../docs/global/SANCTITY_RULES.md)). 6. **Vault outage triggers diagnosis stop, not fix.** §C ends at "escalate to security team" — Vault HA is Layer 3. 7. **Multi-Contour confusion.** A 5xx is from one Contour instance, not all six. Always identify which contour-* release routes the path before declaring "ingress is down." 8. **Recent-merge blame is correlation, not causation.** Surface the timing; don't auto-recommend revert without the user confirming the user-visible symptom started after the merge. --- ## Layer constraint Layer 2 (read-only advisory). Output a diagnosis report; don't execute. The recommendation ALWAYS names a separate procedure / skill — do not silently slide into Layer 1 from this skill. --- ## Related - Skill: [check-cluster-health.md](check-cluster-health.md) — broader, cluster-wide read-only. - Skill: [diagnose-scheduling.md](diagnose-scheduling.md) — narrower, scheduling-only. - Skill: [add-infra-tool.md](add-infra-tool.md) — Layer 1 follow-up if onboarding gap. - Skill: [bump-chart-version.md](bump-chart-version.md) — Layer 1 follow-up if chart-version gap. - Runbook: [argocd-sync-failure.md](../../docs/platform/runbooks/argocd-sync-failure.md). - Runbook: [pod-pending-scheduling.md](../../docs/platform/runbooks/pod-pending-scheduling.md). - Runbook: [ingress-down.md](../../docs/platform/runbooks/ingress-down.md). - Runbook: [metrics-gap.md](../../docs/platform/runbooks/metrics-gap.md). - Runbook: [vault-unavailable.md](../../docs/platform/runbooks/vault-unavailable.md). - Boundaries: [../../docs/global/AGENT_BOUNDARIES.md](../../docs/global/AGENT_BOUNDARIES.md), [../../docs/global/SANCTITY_RULES.md](../../docs/global/SANCTITY_RULES.md).