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_envexercises live behavior (kubectl execof 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. 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
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
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
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
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)
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)