From thinking-frameworks-skills
Computes clean-sheet probability and expected goals against for a fixture, models the joint distribution of a same-team defensive stack, and returns a stack correlation bonus for sizing defensive stacks, valuing GK/DEF picks, scoring BB2 candidates, or weighing the Clean Sheet Shield chip.
How this skill is triggered — by the user, by Claude, or both
Slash command
/thinking-frameworks-skills:wc-clean-sheet-modelThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Implements the clean-sheet and defensive-returns piece of `footballfantasy/context/frameworks/scoring-rules.md`, and supplies the `cs_corr_bonus` term that `fitness-function.md` adds and that the **Clean-Sheet Spine (BB2)** in `building-blocks.md` exists to capture. Defensive points are *lumpy and team-correlated*: the GK and the whole back line keep a clean sheet **together** or concede **toge...
Implements the clean-sheet and defensive-returns piece of footballfantasy/context/frameworks/scoring-rules.md, and supplies the cs_corr_bonus term that fitness-function.md adds and that the Clean-Sheet Spine (BB2) in building-blocks.md exists to capture. Defensive points are lumpy and team-correlated: the GK and the whole back line keep a clean sheet together or concede together. So a 3-defender-plus-GK stack from one strong defence is not four independent picks — it is one bet with an amplified ceiling and a shared floor. This skill is where that correlation is priced. Every defensive EV in the system traces back to the p_cs and expected_ga it emits; downstream agents read the signal, they do not re-derive it (signal-framework.md).
The two jobs:
- [ ] 1. Read inputs: team, opponent, fixture; the `fixture` signal (team xGA, opp xG, difficulty, p_advance); θ and k from the spawn prompt; the stack composition if scoring a BB2
- [ ] 2. Build expected_GA = blend(team defensive xGA-against-this-opponent, opponent attacking xG) × game-state and home/neutral adjustments
- [ ] 3. p_cs = Poisson P(opponent scores 0 | expected_GA) = exp(−expected_GA), refined by defensive-quality and low-block/game-state tilt
- [ ] 4. Per-player defensive EV: λ_cs(pos)·p_cs + clean-sheet-conditional save/defact terms − λ_concede(pos)·E[concede-bracket penalty]
- [ ] 5. Stack: model the joint via a shared team-defence latent; compute P(whole stack clean) and the marginal-vs-joint gap → stack_corr_bonus
- [ ] 6. Apply θ sign: amplify stack_corr_bonus when gain, discount when protect; flag Clean Sheet Shield fit if a full 3+1 stack vs a weak attack
- [ ] 7. Emit the `clean-sheet` signal: p_cs, stack_corr_bonus, expected_ga, conceding downside, confidence
Everything keys off expected_GA(team, fixture) — the team's expected goals conceded in this match. Build it from both sides of the fixture so neither a stout defence vs a great attack nor a leaky defence vs a poor attack is mispriced:
expected_GA = w_def · xGA_team_vs_this_style # how much this defence usually concedes (per-90, recent form-weighted)
+ w_att · xG_opponent_vs_this_style # how much this attack usually creates (per-90)
scaled by minutes-on-pitch context, then × adjustments below
xGA_team and xG_opponent from the fixture signal (wc-fixture-progression). Do not re-derive fixture difficulty — consume it.w_def = w_att = 0.5; tilt toward the side with the larger, more reliable sample (a defence with 6 tournament matches outweighs an opponent with 1). At the World Cup, samples are tiny — lean on pre-tournament reference-class xGA/xG and cap confidence accordingly (see Guardrails).mismatch_list from the fixture signal.scout signal) widens expected_GA; full-strength back line tightens it. Reference the scout's injury/rotation_risk, do not re-scout.Goals conceded in a match are well-approximated as Poisson with mean expected_GA. A clean sheet is the opponent scoring zero:
p_cs(team, fixture) = P(opponent scores 0) = exp(−expected_GA) # first-order Poisson estimate
Sanity anchors (use to gut-check, not to override the model): expected_GA = 0.7 → p_cs ≈ 0.50; 1.0 → 0.37; 1.3 → 0.27; 1.6 → 0.20; 2.0 → 0.14. A clean sheet is a minority outcome even for a big favourite — never carry p_cs above ~0.60 without a confirmed mismatch, and treat anything above 0.65 as a flag to re-examine expected_GA.
Refinements on the raw Poisson:
p_cs (+0.02 to +0.05) for a genuinely disciplined defensive setup, and a small negative one for a high-line side that bleeds chances even in wins. Keep the adjustment bounded; it tunes, it never dominates expected_GA.p60 from the scout signal — a stack's correlation is worthless on a player who comes off at the hour with the score 0-0 and concedes after.P(opp = 0) jointly with the win/draw distribution — useful when game state feeds back (a team protecting a lead drops deeper, lowering late expected_GA). Default to the simple exponential unless the fixture signal carries reliable two-sided xG.For a GK or DEF (and partial-credit MID per the scoring table), the clean-sheet-and-defence contribution to xEV (the term wc-player-ev consumes) is:
def_EV(player) = λ_cs(pos) · p_cs · p60 # clean-sheet point, gated on 60'+
+ λ_save · E[saves] (GK only) # E[saves] rises as expected_GA rises — partial hedge vs the CS miss
+ λ_defact · P(hit defensive-actions threshold) (DEF / ball-winning MID)
− λ_concede(pos) · E[concede-bracket penalty] # the downside — see below
λ_cs(pos) from scoring-rules.md (provisional GK/DEF +4, MID +1, FWD 0 — confirm). The MID clean-sheet rule is a real, often-overlooked floor on a defensive-minded midfielder in a strong side; surface it.expected_GA lowers p_cs but raises E[saves] — a keeper behind a busy-but-resolute defence has a partial floor even without the sheet. Estimate E[saves] ≈ shots_on_target_faced (scales with expected_GA and opponent xG); credit the per-3-saves point and the penalty-save tail.The mirror of the clean sheet. GK/DEF lose points per the concede bracket (provisional: −1 per 2 goals conceded). Because expected_GA is the same latent that drives both, the downside is mechanically tied to the upside — and for a stack it is shared (they all sit in the same conceding bracket together). Return it explicitly so the board can show the floor, not just the ceiling:
E[concede_penalty(team)] = Σ_g P(opponent scores g | expected_GA) · penalty_bracket(g) # Poisson over goals conceded
For a stack of k defenders + GK, the concede penalty is incurred by every stacked player on the same goals-against draw — so the downside scales ~linearly with stack size on the bad draws, while the upside also scales on the good draws. That two-sidedness is the heart of the stack bonus below.
This is the term BB2 exists for and that fitness-function.md adds as cs_corr_bonus. A naive model treats k defenders + a GK from the same team as k+1 independent clean-sheet draws. They are not — one match, one defence, one realised goals-against count. Model the shared outcome with a single team-defence latent:
clean-sheet indicator for the whole stack = 1{ opponent scores 0 } # a SINGLE Bernoulli, shared by all k+1 players
P(whole stack returns the CS point) = p_cs · Π p60(member) # ~p_cs, lightly haircut for any rotation-risk member
The fantasy value of the stack is the joint distribution of its total defensive points, which has two parts a naive sum misses:
(k+1) clean-sheet points at once — a correlated haul. A 3+1 stack vs a weak attack is a ~p_cs chance at 4 × λ_cs ≈ +16 from clean sheets alone, plus any set-piece CB goal or full-back attacking return riding on top. That correlated spike is exactly the ceiling a chaser wants and a naive independent-sum understates.Compute the bonus as the gap between the joint ceiling/variance the stack actually carries and the independent-draw approximation the rest of the EV pipeline would assume:
# correlation lift to the ceiling (what the joint buys over independent draws):
ceiling_lift = ρ · k · λ_cs(DEF) · p_cs · (1 − p_cs) # ρ→1 within a team; grows with stack size k and with how live the CS is
# shared-downside term (the cost of zero internal diversification on the CS axis):
shared_floor = ρ · k · E[concede_penalty_per_player] # the same bad draw hits all k+1 together
stack_corr_bonus = sign(θ) · ceiling_lift − damp(θ) · shared_floor
where ρ ≈ 1 for same-team back-line members (perfect within-team CS correlation is the modelling premise of BB2), decaying toward 0 as you split the stack across teams — splitting a stack across nations destroys the bonus, which is precisely why crossover keeps BB2 intact and repair downgrades enablers before breaking the spine (building-blocks.md).
The θ sign flip (selection pressure):
| θ | stack_corr_bonus treatment | football logic |
|---|---|---|
gain (chasing) | sign(θ) = +, damp(θ) small → amplify the correlated ceiling | you need the swing; a 4-up clean-sheet haul the field's spread defences don't get is a rank earthquake |
protect (leading) | sign(θ) = + but small, damp(θ) large → discount for the shared floor | a team blank that zeroes four of your players at once is exactly the variance a leader is trying to avoid; prefer spreading defenders |
neutral | symmetric, modest | let the marginal p_cs EV lead; small bonus for coherence |
This makes the same 3+1 stack score as an asset for a chaser and as a liability for a leader — the selection-pressure dial of fitness-function.md, applied to the defensive module.
Chip and building-block synergy (surface, do not decide):
p_cs chance into a near-locked correlated haul and caps the shared floor — the single highest-leverage home for the chip (chip-catalog.md §4). Set shield_fit: true and report the lift to p_cs and the floor the shield would underwrite, so wc-chip-strategist can size the deployment.clean-sheet signal)Emit via wc-signal-emitter to signals/<round>-clean-sheet-<team>.md:
---
type: clean-sheet
round: <round id, e.g. 2026-grp-md2>
date: <YYYY-MM-DD>
emitted_by: wc-clean-sheet-model
confidence: <0.00–1.00> # cap 0.35 if expected_GA rests on unconfirmed xGA/xG or scoring values
source_urls:
- <url for team xGA / opponent xG / predicted back line / venue> # or "manager-provided"
---
team: <team>
fixture: { opponent: <opp>, round: <round>, venue: neutral|host|home, kickoff_utc: <ts> }
expected_ga: <float> # expected goals conceded this match (the engine quantity)
p_cs: <0.00–1.00> # = exp(−expected_ga), refined; clean sheet is a minority outcome
p_cs_60: <0.00–1.00> # p_cs × representative p60 (the actually-bankable CS prob for a starter)
defensive_ev:
gk: { cs_point: <n>, e_saves: <n>, save_points: <n>, concede_penalty: <-n>, def_ev: <n> }
def: { cs_point: <n>, defact_floor: <n>, concede_penalty: <-n>, def_ev: <n> } # per typical stacked DEF
mid_partial_cs: <n> # the MID clean-sheet floor, if relevant
concede_downside:
e_concede_penalty_per_player: <-n> # Poisson over goals-against × bracket; SHARED across a stack
bracket_note: <"−1 per 2 conceded; provisional — confirm scoring-rules.md">
stack:
composition: { team: <team>, defenders: <k>, gk: <bool> } # the BB2 spine being priced
is_real_stack: <bool> # ≥3 same back line + that GK; false = split pseudo-stack (ρ→0, no bonus)
rho: <0.0–1.0> # within-team CS correlation (~1 same team)
p_stack_clean: <0.00–1.00> # P(whole stack returns the CS point together)
ceiling_correlated_haul: <n> # points the stack pays AT ONCE on the good draw (e.g. (k+1)·λ_cs + set-piece spike)
shared_floor: <-n> # points all members lose together on the bad draw
stack_corr_bonus: <+/- n> # the fitness term, AFTER the θ sign flip
theta_applied: { theta: protect|gain|neutral, k: <n> }
shield_fit: <bool> # true = full 3+1 stack vs weak attack → prime Clean Sheet Shield slot
shield_note: <lift to p_cs and floor the chip would underwrite, for wc-chip-strategist>
assumptions:
blend_weights: { w_def: <n>, w_att: <n> }
adjustments_applied: [ mismatch, game_state, venue, personnel, low_block ]
recheck_near_lock: <bool> # true if predicted back line / keeper not yet confirmed
The decomposition is mandatory: wc-fitness-eval reads stack_corr_bonus, wc-player-ev reads the per-position def_ev and concede_downside, wc-chip-strategist reads shield_fit/shield_note, and the Director shows ceiling-vs-floor on the board so the manager sees both sides of the stack bet.
p_cs drift above ~0.60 without a confirmed mismatch (mismatch_list); above 0.65 is a flag to re-examine expected_GA. Big favourites still concede.p_stack_clean, not p_cs^(k+1).shared_floor alongside ceiling_correlated_haul. A correlation that lifts the ceiling lifts the floor's depth equally — hiding the downside would mislead a protecting manager into a single fragile bet.θ isn't supplied, ask — do not silently default to neutral on a stack-heavy candidate; the sign flip is the whole point of the term for fitness.p60 from the scout signal; a defender subbed at the hour is not a clean-sheet bank. A rotation-risk member also drags ρ-adjusted p_stack_clean.p_advance come from the fixture signal; injuries/minutes from the scout signal. Re-deriving them violates signal-framework.md and risks contradicting an upstream agent.scoring-rules.md confirmed: false). If expected_GA rests on unconfirmed xGA/xG or the clean-sheet point value is unverified, cap confidence ≤ 0.35 and set recheck_near_lock: true until the back line and keeper are confirmed.npx claudepluginhub lyndonkl/claude --plugin thinking-frameworks-skillsComputes a single player's expected FIFA World Cup Fantasy points per round (xEV) by combining start probability, minute-based scoring tiers, fixture-scaled npxG/xA, defensive floors, set-piece premium, and downside risks.
Calculate derived football metrics such as xG, xGOT, PPDA, expected threat, and passing networks from raw event data. Useful for football analytics and model building.
Builds a tactical game plan by analyzing opponent weaknesses, team strengths, and match context using performance indicators and decision rules.