# Fanout Cve Task Guardian

> End-to-end maintenance skill for the fanout-cve-rollout task (<TASK_ID>). Owns pre-push safety checks, local quality/anatomy gates, push, Oracle validation, and rollout monitoring. This is a REWARD-SHAPED task (5 independent subscores, 0.2 each) — the goal is a dense, correct partial-credit gradient that guides agents through the full remediation, NOT the avg < <TARGET_MEAN> variance target of the sibling tasks. Calls fanout-cve-validation-debugger when Oracle fails and fanout-cve-score-tuner when the gradient is wrong.

- Skill: `omonuj/fanout-cve-task-guardian` (Agent Skill)
- Install (CLI): `npx skillmds@latest add omonuj/fanout-cve-task-guardian`
- Raw SKILL.md: https://api.skillmd.com/api/skills/omonuj/fanout-cve-task-guardian/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: omonuj (https://skillmd.com/u/omonuj)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/omonuj/fanout-cve-task-guardian

---


# fanout-cve-task-guardian

## Step 0 — QC Spec design gate (READ FIRST, every iteration)

Before editing ANY task file and again before every push, read the QC Spec — `.claude/QC-SPEC.md`. It is the priority-ordered rubric (Critical → Error → Legitimacy) `horizon check-quality` and reviewers grade against.

Confirm the intended/current design does not violate any Critical or Error item. The ones to hold for this reward-shaped task:
- **No Binary Bundling** — keep the 5 subscores independent; never AND heterogeneous checks into one.
- **Functional Tests as Subscores** — `pods_rolled_live_env` exercises live behavior (`kubectl exec` of pod env); keep it functional, not a spec read.
- **Incremental Rewards / No Dead Weights / Functional Subscore Variance** — every 0.2 subscore represents real progress and takes ≥2 distinct values across a batch.
- **Equal Weights** — all five subscores equal (0.2).
- **No Answer Leakage** — the grader judges CVE truth off root-only `/opt/grader-cve.json`, never an agent-editable artefact.

If a change would breach a Critical/Error item, stop and rework it before pushing.

## Execution Mode — FULLY AUTONOMOUS
**NEVER ask the user for confirmation** on any command, edit, push, validation, or live-probe. The ONLY user-facing handoff is Step 4c (trigger the rollout batch in the Horizon UI). Everything else runs without asking. Two valid paths → pick the safer.

## Goal
Keep fanout-cve-rollout shippable every push: setup.sh runs cleanly, Oracle scores **1.0**, and the 5 subscores form a dense, correct partial-credit gradient (each independently achievable; partial fixes score partially; the full remediation sequence scores 1.0). The front door for the other fanout-cve-* skills.

## What kind of task this is (read first)
This is a **reward-shaping** task (per the issue's Training Context), NOT a variance task. There are **no drift controllers**, no cross-namespace discovery, no avg < <TARGET_MEAN> target. Five independent subscores give the agent signal at each fix stage so it's guided toward the complete two-phase fix (image upgrade + ConfigMap revert + rolling restart + GitOps commit + ArgoCD sync) instead of stopping at the first plausible fix. Do not try to "add variance" — that would fight the design.

## Task Identity
| Field | Value |
|---|---|
| **Task UUID** | `<TASK_ID>` |
| **Task slug / id** | `fanout-cve-rollout` |
| **Local path** | `tasks/fanout-cve-rollout/` |
| **Horizon root** | `/Users/mac/Documents/tasks` |
| **Venv** | `source /Users/mac/Documents/tasks/horizon_env/bin/activate` |
| **Scoring** | 5 subscores × 0.2 each (binary) — `score = 0.2·Σ` |
| **Mini-batch** | `<TASK_ID>` |

**Status:** built, locally Oracle-traced (k8s-native, real cluster-state + live `kubectl exec`). NOT yet hosted-Oracle / rollout-measured.

## How scoring works (derive from grader.py)
The scenario: `fanout-service` runs a CVE-2023-49092 image; pods snapshotted `AMQP_TLS_VALIDATION_MODE=full` at start; the ConfigMap was later patched to the `peer` workaround **without a restart**, so the live pod env and the ConfigMap have drifted. Full fix = upgrade image + revert ConfigMap to `full` + roll pods + commit to Gitea + ArgoCD Synced.

Five subscores (0.2 each):

| Subscore | Passes when |
|---|---|
| `cve_image_patched` | `fanout-service` Deployment template label `bleater.io/fanout-version` is a version NOT in the root-only vulnerable list (`/opt/grader-cve.json`) — i.e. the patched `1.4.3` |
| `configmap_reverted` | `fanout-tls-config` `AMQP_TLS_VALIDATION_MODE == "full"` |
| `pods_rolled_live_env` | Deployment rollout converged AND every Running pod carries a clean version label AND its **live env** (`kubectl exec -- echo $AMQP_TLS_VALIDATION_MODE`) is `full` |
| `gitops_committed` | `fanout-helm-values` `values.yaml` declares a clean `fanoutVersion` AND `amqpTlsValidationMode: full` |
| `argocd_synced` | `argocd-bleater-app` `status == "Synced"` |

**Anti-cheat:** the grader reads the patched/vulnerable version set from root-only `/opt/grader-cve.json`, NOT the agent-readable `harbor-scan-results` ConfigMap — so an agent can't pass `cve_image_patched` by editing the scan results.

**The partial-fix trap (the heart of the task):** if `configmap_reverted=1` but `pods_rolled_live_env=0`, the grader appends a HINT that the config isn't live until the pods are rolled. The live-env exec is what catches "fixed the ConfigMap but didn't restart" and "upgraded the image but left `peer`."

## Step 0 — Dockerfile invariants
```bash
cat tasks/fanout-cve-rollout/Dockerfile
```
| Invariant | Required | Why |
|---|---|---|
| `ENABLE_ISTIO_BLEATER` | must NOT appear | k3s crash → `score: 0, feedback: null` |
| `COPY data/ubuntu-user-rbac.yaml ...` | present | DO-NOT-CHANGE block needs node access |
| `ALLOWED_NAMESPACES` | present (kube-system fine) | parity; this task is bleater-only |

## Step 1 — Syntax + anatomy
```bash
cd /Users/mac/Documents/tasks
bash -n tasks/fanout-cve-rollout/setup.sh    && echo "setup.sh OK"
bash -n tasks/fanout-cve-rollout/solution.sh && echo "solution.sh OK"
python3 -m py_compile tasks/fanout-cve-rollout/grader.py && echo "grader.py OK"
source horizon_env/bin/activate
horizon check-anatomy tasks/fanout-cve-rollout 2>&1
```

## Step 2 — Quality
```bash
horizon check-quality tasks/fanout-cve-rollout 2>&1 | tail -25
```

**Before push, walk the QC-SPEC.md Critical + Error list against the current `grader.py` + `task.yaml`** and record pass/fail per item. Every item must pass OR be on the accepted-noise list with a one-line reason. A new failing item that is NOT accepted-noise blocks the push.
**Expected 18 substantive pass; verdict FAIL only from `v1_feedback`** (heredoc-invisible ConfigMap schemas). Accepted noise — do not fix by dumping setup internals into task.yaml. Any other check failing is a real signal.

## Step 3 — Push
```bash
horizon tasks push tasks/fanout-cve-rollout 2>&1   # record Version: NNN
```

## Step 4 — Validation
**4a. Oracle:** pass = `1.0` + all five subscores `1`. Anything else → `fanout-cve-validation-debugger`.
**4b. Live red-team** via `horizon-agentic-reviewer` on UUID `31ba76bf-…`. Verify (as `ubuntu`):

| Hypothesis | Probe |
|---|---|
| the stale-env drift is real | `kubectl get cm fanout-tls-config -n bleater -o jsonpath='{.data.AMQP_TLS_VALIDATION_MODE}'` → `peer`; `kubectl exec <fanout pod> -n bleater -- sh -c 'echo $AMQP_TLS_VALIDATION_MODE'` → `full` (drift!) |
| pods are on the vulnerable version | `kubectl get deploy fanout-service -n bleater -o jsonpath='{.spec.template.metadata.labels.bleater\.io/fanout-version}'` → `1.4.2` |
| grader truth is root-only | `cat /opt/grader-cve.json` NOT readable as ubuntu; `harbor-scan-results` IS readable |
| helm/argocd start broken | `fanout-helm-values` fanoutVersion=1.4.2; `argocd-bleater-app` status=OutOfSync |
| a no-op agent scores 0 | reset, run grader — all five subscores 0 |
| partial fixes score partially | apply ONLY the ConfigMap revert → grader returns 0.2 with the roll-the-pods hint |

**4c.** Tell the user verbatim:
> "Oracle passed at version N and live red-team is clean. Please trigger a rollout batch for version N in the Horizon UI."

## Step 5 — Rollout monitoring (reward-shaping lens)
```bash
horizon rollouts pull --version NNN tasks/fanout-cve-rollout 2>&1
```
Use the `fanout-cve-iteration-loop` Phase 3 script. **The target is different from the variance tasks:** you want the partial-credit gradient to be informative, not an avg ceiling.

| Healthy signal | Why |
|---|---|
| The full distribution `{0, 0.2, 0.4, 0.6, 0.8, 1.0}` appears | the reward shaping is working — agents land at different fix depths |
| Each subscore is achieved by SOME agent and missed by SOME | no subscore is deadweight (always-0 = unreachable; always-1 = free) |
| `pods_rolled_live_env` is the most-missed | expected — the stale-env / restart step is the lesson |
| A reasonable share reach 1.0 | the task is solvable, not impossible |

If a subscore is deadweight, or partial credit is structurally impossible, hand to `fanout-cve-score-tuner`.

## Hard constraints
| Constraint | Why |
|---|---|
| No `ENABLE_ISTIO_BLEATER=true` | k3s crash |
| Keep the `COPY data/ubuntu-user-rbac.yaml` path | setup aborts otherwise |
| Keep grading the version off root-only `/opt/grader-cve.json` | else an agent edits `harbor-scan-results` to fake the fix (reward hack) |
| Keep the live-env `kubectl exec` check in `pods_rolled_live_env` | it is the entire pedagogical point (pod snapshot vs ConfigMap) — a spec-only check would let "no restart" pass |
| Keep the 5 subscores independent (no AND-bundling) | bundling destroys the dense gradient the task is designed around |
| Don't add drift controllers / variance mechanics | wrong design for a reward-shaped task |
| Air-gapped images only (`${SIDECAR_IMAGE}`) | the fanout pods must be runnable so the grader can exec them |
| Don't simplify solution.sh to make Oracle pass | it's the oracle standard |

## Skill hand-off map
```
fanout-cve-task-guardian (you are here)
├── Step 4a fails → fanout-cve-validation-debugger
├── Step 4b       → horizon-agentic-reviewer
├── Step 5 wrong  → fanout-cve-score-tuner (reward-shaping reviewer)
└── full loop     → fanout-cve-iteration-loop (orchestrator)
```

