maddy-score-tuner
QC Spec compliance — assert before every tuning edit
Any difficulty/variance lever you pull must STILL satisfy the QC Spec (.claude/QC-SPEC.md). Before recommending a change, assert it does not breach:
- No Binary Bundling — never recover variance by AND-ing more heterogeneous checks into one subscore.
- Incremental Rewards / No Dead Weights — no subscore left always-0 or always-1; every lever keeps both subscores varying.
- Functional Subscore Variance — at least one functional subscore takes ≥2 distinct values across a batch.
- Equal Weights — never rebalance weights to hit a number.
- Functional Tests as Subscores / No Answer Leakage — don't convert a functional subscore into static matching, and don't leak the answer to manufacture passes.
A lever that lowers avg by breaching any of these is NOT a valid fix — pick a different lever.
Execution Mode — FULLY AUTONOMOUS
NEVER call ask_user or pause for confirmation. Pick the best path and execute it immediately.
Goal
Tune task difficulty and variance safely. The objective is not to "pass" — it is to keep avg rollout score < with both subscores varying, neither deadweight, and the two subscores decorrelated. Every edit must be the smallest reversible change that explains the observed symptom.
Invoked when:
- Rollouts regress (avg ≥ 0.60)
- A subscore becomes deadweight (always 0 or always 1)
- The two subscores move together every batch (correlation collapse — the maddy-specific failure mode)
maddy-task-guardianStep 6 hands off
NOT invoked for Oracle failures — use maddy-validation-debugger.
Task Identity
| Field | Value |
|---|---|
| Task UUID | <TASK_ID> |
| Task slug | maddy-delivery-split-brain |
| Scoring | delivery_consistency × 0.5 + observability_governance × 0.5, both binary |
| Target | rollout avg < <TARGET_MEAN>, both subscores in {0,1}` somewhere in the batch, and NOT perfectly correlated |
How difficulty is constructed (read once, internalise)
The agent's runtime job: diagnose a Maddy SMTP split-brain (one replica on smarthost config, one stale replica on direct-delivery config behind the same Service), converge it, make it observable, and stop the automation that keeps re-creating the stale replica. Both subscores are graded against the live world after the agent's solution runs.
The shared surface (the thing that makes this task tricky to tune)
Three drift controllers re-apply a byte-identical maddy-legacy Deployment on a loop. Names are randomized (pool stem + numeric suffix); discover by behaviour:
| Drift source | Namespace | Cadence | Discover by | Scenario key |
|---|---|---|---|---|
| kube-system reconciler | kube-system |
5s | annotation bleater.io/purpose: "maddy rollout convergence" |
reconciler |
| bleater-env reconciler | bleater-env |
20s | annotation bleater.io/purpose: "maddy rollout convergence" |
env_reconciler |
canary config-pinner sidecar |
bleater |
11s | container named config-pinner; RBAC <canary>-runtime |
canary / canary_runtime |
These three are graded by BOTH subscores. While active they re-spawn the legacy replica → delivery_consistency Layer 1/2 fails. They are also directly checked at replicas=0 by observability_governance Layers 4/5/6. So stopping the drift helps both subscores simultaneously. If drift-discovery becomes the only hard thing, the two subscores correlate: an agent who finds all three passes both, one who misses one fails both → avg swings to 0 or 1 as a block, and you lose independent signal.
delivery_consistency difficulty axis
check_delivery_consistency (wait_consistent(passes=5, gap=8s, timeout=220s)):
maddy-config(maddy.conf) is the smarthost config (relay_mode = smarthost, notdirect)- NO
app=maddypod mountsmaddy-config-legacy maddy-legacyDeployment absent /replicas=0maddyDeployment converged:spec.replicas >= 2,updated == ready == spec,observedGenerationcurrent- ≥ 2 Ready
app=maddypods mountmaddy-config
Independent (decorrelating) lever: the rollout-convergence requirement (scale maddy to 2 + all pods Ready on smarthost). An agent can find and stop all drift yet still fail consistency if they delete maddy-legacy but never scale maddy from 1 → 2, or leave the rollout half-applied. This is the part of delivery_consistency that does NOT depend on drift discovery.
observability_governance difficulty axis
check_observability_governance (wait_consistent(passes=5, gap=8s, timeout=200s)):
maddycontainer exposes pod identity (POD_NAMEviafieldRef metadata.name)maddy-gitops-sourcevalues.yaml=replicas: 2+smarthost, notmode: direct- a per-pod divergence rule ConfigMap exists — real PromQL (
- record:/- alert:/expr:) + signal (rate/success/delivery/divergence) + per-pod dimension (by (pod)etc.) - kube-system reconciler
replicas=0 - canary mutation path neutralized (sidecar OR
-runtimeRBAC) - bleater-env reconciler
replicas=0
Independent (decorrelating) levers: the three observability artifacts (Layers 1–3). These have NOTHING to do with drift — an agent who stops all drift still fails governance if they don't add POD_NAME, fix the GitOps CM, and write a valid per-pod PromQL rule. The per-pod-rule gate (Layer 3) is the strongest governance-only variance driver — many agents will write a vague ConfigMap with the word "pod" in a comment and fail the by (pod) requirement.
Where the hidden difficulty comes from
- Cross-namespace discovery — agents who only audit
bleatermiss thekube-systemANDbleater-envreconcilers. The bleater-env one is doubly hidden: different namespace + innocuous non-maddy name (only thebleater.io/purposeannotation links it). The task.yaml hint ("Reapply pressure may originate in more than one namespace — check all namespaces in.allowed_namespaces") and the incident notes are the breadcrumbs. - The per-pod PromQL rule — the
by (pod)+ rule-key requirement filters agents who hand-wave the observability ask. - Rollout convergence — agents who fix the config but forget to actually scale
maddyto 2.
These are the entire design. Every lever sharpens or collapses one of them.
⚠️ Structural deadweight check (do this FIRST when governance is stuck at 0)
If observability_governance is always 0, suspect the bleater-env reachability bug before anything else: if ALLOWED_NAMESPACES doesn't grant the agent bleater-env, Layer 6 is unstoppable and the whole subscore is structurally impossible. This is a maddy-task-guardian Step 0 / Dockerfile issue, NOT a tuning lever. Verify on a live container (guardian Step 4b) before touching grader/setup.
Four primary rules
Rule 1: Preserve variance
A subscore stuck at one value is broken. Always-0 = structurally impossible (re-check bleater-env reachability + Dockerfile). Always-1 = trivially passing. Healthy: avg –0.55; both subscores show 0 and 1 in any 5-rollout batch.
Rule 2: Avoid correlation collapse (the maddy-specific rule)
Before any change ask: does this lever touch ONLY the shared drift surface? If yes, it moves both subscores together and worsens correlation. Prefer the decorrelating levers:
- to move
delivery_consistencyalone → the rollout-convergence requirement - to move
observability_governancealone → the three observability artifacts (esp. the per-pod-rule gate)
Reject a change whose only effect is "make drift easier/harder to find" unless the explicit goal is to move avg and you accept both subscores shifting.
Rule 3: Prefer functional variance over static checks
Functional (does the converged state hold under live drift; does a real PromQL by(pod) rule exist) is durable. Static (an exact ConfigMap name, an exact label value the baseline documents) evaporates the moment an agent reads the baseline. The per-pod-rule check is name-agnostic on purpose — keep it that way.
Rule 4: Minimal safe edits
One file, one purpose, small, reversible. Reject multi-system rewrites, simultaneous setup+grader+task.yaml changes, architectural redesigns mid-iteration.
Tuning order — pull the safest lever first
Level 1 — setup.sh levers (safest)
Verify the current value in setup.sh before editing — grep the named variable.
Levers that move the SHARED drift axis (move BOTH subscores — use to shift avg, not to decorrelate):
| Lever | Where | Current | Raise difficulty | Floor |
|---|---|---|---|---|
| kube-system reconciler cadence | emit_drift_deploy "${RECONCILER}" kube-system ... 5 |
5s |
already fast | 5s (API rate limits) |
| bleater-env reconciler cadence | emit_drift_deploy "${ENV_RECONCILER}" bleater-env ... 20 |
20s |
decrease toward 10s | 10s |
| canary config-pinner cadence | sidecar loop sleep 11 |
11s |
decrease toward 8s | 8s |
| bleater-env name-pool innocuousness | ENV_RECONCILER_STEMS |
innocuous non-maddy stems | keep innocuous (vaguer = harder to find) | must stay annotation-discoverable |
| baseline-config misdirection density | <<BASELINE heredoc |
— | add plausible inert entries | misdirection ≠ leaking a real drift name |
Levers that move observability_governance ALONE (decorrelating — preferred for governance):
| Lever | Where | Direction | Guard |
|---|---|---|---|
| GitOps source staleness obviousness | maddy-gitops-source heredoc (replicas: 1, mode: direct) |
keep the stale values; this is the artifact the agent must fix | don't pre-fill the correct values |
| Per-pod-rule expectation strength | task.yaml Expected Outcome wording about "per-pod divergence signal" | vaguer ⇒ fewer agents produce a valid by(pod) rule |
already in Expected Outcome — do not name a ConfigMap |
POD_NAME hint strength |
incident notes "logs ... no pod name" line | vaguer ⇒ fewer agents add the downward-API env | cannot name fieldRef / downward API |
Levers that move delivery_consistency ALONE (decorrelating — preferred for consistency):
| Lever | Where | Direction | Guard |
|---|---|---|---|
Initial maddy replicas |
maddy Deployment replicas: 1 |
keep at 1 so the agent must actively scale to 2 | this is the convergence lever; don't pre-set to 2 |
| Rollout-readiness pressure | maddy container resources / readiness |
minor | don't make pods un-schedulable |
Level 2 — grader.py levers (use only when setup.sh is exhausted)
| Lever | Effect | Current | Ceiling |
|---|---|---|---|
delivery_consistency wait_consistent gap |
larger ⇒ must stay clean longer | gap=8 |
gap=8s |
delivery_consistency wait_consistent timeout |
lower ⇒ less recovery budget | timeout=220 |
floor 180s |
observability_governance wait_consistent |
same | passes=5, gap=8, timeout=200 |
floor 150s |
| Per-pod-rule strictness (Layer 3) | the by (pod) + rule-key + signal triple-gate |
current triple | governance-only lever — loosening raises governance pass rate, tightening lowers it. Do NOT require an exact ConfigMap name. |
INTENDED_REPLICAS |
bar for convergence | 2 |
leave at 2 unless task.yaml changes too |
Above any ceiling, legitimate solutions flake into dead-zero (drift restart churn). The ceilings are empirical.
Level 3 — task.yaml wording (last resort, surgical only)
Highest-leverage — a few words swing variance a lot.
Allowed: strengthen/weaken the "more than one namespace" hint; soften/sharpen the "per-pod divergence signal" or "pod identity in logs" phrasing.
Forbidden: naming any drift controller or the bleater.io/purpose value; naming bleater-env or kube-system explicitly as where drift lives; naming config-pinner; itemizing the fix path step-by-step; naming fieldRef/POD_NAME/an exact ConfigMap name.
Change exactly one phrase; revert immediately if variance moves the wrong way.
Diagnosis guide — symptom → root cause → fix
Always read at least 2 rollout transcripts (1 pass, 1 fail) before tuning.
| Observed symptom | Most likely root cause | Correct lever |
|---|---|---|
delivery_consistency always 1 |
Drift too easy to find AND agents reliably scale to 2 | Speed up the bleater-env/canary cadence (shared) OR make the bleater-env name vaguer; if convergence is the gap, it's already minimal |
delivery_consistency always 0 (Oracle passes) |
A drift source re-spawns the legacy pod faster than agents stop it, OR a source is undiscoverable | Confirm all three are annotation/sidecar-discoverable; if a cadence is below floor, raise it slightly |
observability_governance always 0 (Oracle passes) |
bleater-env unreachable (structural) OR per-pod-rule gate too strict for legitimate agents | FIRST verify bleater-env reachability (guardian Step 0). Only then consider loosening Layer 3 |
observability_governance always 1 |
Per-pod rule + discovery too easy | Tighten the per-pod-rule requirement is risky (already strict); prefer making bleater-env discovery harder |
| Both subscores move together every batch (avg ~0 or ~1, never 0.5) | Correlation collapse — drift discovery is the only hard thing | Add independent difficulty: keep maddy initial replicas=1 (consistency-only) and ensure the per-pod-rule gate bites (governance-only). Do NOT pull a shared-drift lever |
| avg 0.60–0.70 | Slightly too easy | Tighten a governance-only or consistency-only lever before a shared one |
| avg > | A subscore is probably deadweight-1 | Run the variance script first to confirm which |
Specialized diagnostic roles
Role 1: Rollout Analyst
Q: What did agents actually do, and why pass/fail? Run the analysis script (from maddy-iteration-loop Phase 3), read ≥2 transcripts (≥1 pass, ≥1 fail), classify the distribution (deadweight / healthy / correlated). Output one paragraph; no fixes yet.
Role 2: Setup Drift Specialist
Q: Of the three drift sources, which are actually biting? For each (kube-system reconciler, bleater-env reconciler, canary config-pinner) check whether failing transcripts mention discovering/stopping it. The bleater-env one is usually the most-missed (innocuous name + namespace). Output: ranked variance impact. Prefer setup.sh edits.
Role 3: Consistency Specialist
Q: What controls delivery_consistency variance independent of governance? Inspect: do failing agents stop all drift but forget to scale maddy to 2? Is the legacy replica re-spawning inside the window? The decorrelating lever is the convergence requirement (initial replicas=1). Output: a setup.sh/grader edit that moves consistency without touching governance discoverability.
Role 4: Governance Specialist
Q: What controls observability_governance variance independent of consistency? The three artifacts: POD_NAME env, maddy-gitops-source edit, per-pod PromQL rule. Read failing transcripts: did agents write a ConfigMap with "pod" in a comment but no by (pod) expr (Layer-3 miss)? Did they skip the downward API? First rule out bleater-env reachability as a structural deadweight. Output: a task.yaml hint tweak or a Layer-3 strictness change (within reason).
Role 5: Coupling Reviewer (the maddy-critical role)
Q: Does the proposed edit move ONLY the shared drift surface? Walk the inventory:
- Drift cadence/discoverability → moves BOTH subscores (correlation risk). Only acceptable when the explicit goal is shifting avg.
- Initial
maddyreplicas → consistency-only (safe to decorrelate) - The three observability artifacts → governance-only (safe to decorrelate)
wait_consistenton one subscore → affects only that subscore's window (drift restart churn can flake both if both windows shrink) Output: "no correlation risk — moves only X" OR "REJECT — this is a shared-surface lever; it will move both subscores; propose a decorrelating alternative."
Role 6: QC Reviewer
Q: Would this edit break local quality or add a permanent fail? Expected: 18 substantive checks pass; the v1_feedback section FAILs as accepted noise (heredoc-invisible baseline config + annotation-based discovery). Reject: naming a drift controller / bleater.io/purpose / bleater-env / config-pinner in task.yaml or visible setup; requiring an exact ConfigMap name in the per-pod-rule check. Output: "QC delta 0/+1" or "REJECT — reviewer will flag Y."
Role 7: Final Recommender
Produce exactly this 8-point block:
- Root cause (one sentence)
- Why avg shifted (the score math)
- Smallest safe fix (one edit, one file)
- Expected rollout impact (projected avg + per-subscore distribution + correlation)
- Expected QC impact (delta on the 18-check baseline)
- Risk assessment (ranked)
- Exact files to modify
- Exact lines/components affected
Only after the Recommender's block does the orchestrator (maddy-iteration-loop) hand off to maddy-task-guardian for the edit + push.
Operating procedure — diagnose, propose, approve, apply
NEVER edit immediately on a fresh diagnosis. Walk in order:
- Confirm trigger — Oracle failure → hand to
maddy-validation-debugger. This skill is rollout regressions only. - Read — grader.py, setup.sh, task.yaml, latest rollout JSONs, ≥2 transcripts (1 pass, 1 fail).
- Diagnose — Role 1 for the distribution; look up the symptom; decide if it's deadweight, correlation collapse, grader strictness, or drift intensity.
- Pick the lever — Level 1 (setup.sh) unless exhausted; Level 2 (grader) only if necessary; Level 3 (task.yaml) last. Prefer a decorrelating lever unless intentionally shifting avg.
- Refine — adopt the matching specialist role (Consistency / Governance / Setup Drift).
- Review — Role 5 (Coupling) and Role 6 (QC). If either rejects, return to step 4.
- Recommend — Role 7's 8-point block.
- NEVER apply from this skill. Hand to
maddy-iteration-loop→maddy-task-guardianfor syntax check, push, validation.
Hard constraints (same as task-guardian)
| Constraint | Why |
|---|---|
Do NOT add ENABLE_ISTIO_BLEATER=true |
k3s startup crash → score: 0, feedback: null |
Do NOT remove kube-system from ALLOWED_NAMESPACES |
governance Layer 4 always-0 |
Do NOT remove bleater-env reachability once added |
governance Layer 6 unsolvable |
Do NOT remove COPY data/ubuntu-user-rbac.yaml |
setup aborts (DO NOT CHANGE block) |
Do NOT name drift controllers / bleater.io/purpose / config-pinner / bleater-env in task.yaml or baseline heredoc |
collapses observability_governance to always-1 |
| Do NOT require an exact ConfigMap name in the per-pod-rule check | static dead-weight; keep it name-agnostic with the by(pod) functional requirement |
| Do NOT use external registry images for drift | air-gapped — failed pull turns a drift source into a ghost |
Do NOT exceed wait_consistent(passes=5, gap=8s) (consistency timeout=220, governance timeout=200) |
above ceiling legitimate solutions flake to dead-zero |
| Do NOT simplify solution.sh to make Oracle pass | solution.sh is the oracle standard, not a tuning surface |
What is NOT graded (do not add these to grader)
- Exact ConfigMap names (per-pod rule is name-agnostic by design)
- Replica count on the drift Deployments beyond
==0 - The egress NetworkPolicy contents (scenario realism, not graded)
maddy.conffields beyond therelay_mode = smarthost/ not-directmarkers- Annotation/label values that the baseline documents (copy-from-baseline dead-weight)
Preferred outcomes
Healthy: 0.0 / 0.5 / 1.0 all represented across a 5-rollout batch; avg –0.55; both subscores vary AND are not perfectly correlated; failures are legitimate (a discoverable drift source missed, or a vague per-pod rule, or forgot to scale to 2).
Unhealthy: any subscore deadweight; avg > ; the two subscores correlate perfectly every batch (correlation collapse); a structural impossibility (bleater-env unreachable); static-only grading creeping in.
If you observe an Unhealthy pattern, halt and write a summary rather than pushing another fix. Two cycles chasing the same Unhealthy pattern means the lever choice was wrong.