skills/wc-player-ev/SKILL.md
Computes one player's expected FIFA World Cup Fantasy points for a single round (xEV) from a scout signal — folding start probability, the 60-minute appearance cliff, fixture-and-minutes-scaled npxG/xA, clean-sheet and defensive-action floors, the penalty/set-piece premium, and the card/concede downside into one number plus its full decomposition, variance, ceiling, and floor. This is the atomic EV unit the whole system samples from. Use whenever a single player's per-round point distribution is needed — called by wc-scout, every wc-strategist genotype, wc-matchday-tactician, and wc-fitness-eval (which sums these into raw_xEV). Reads the scout signal; never re-scouts. Emits a player-ev signal.
npx skillsauth add lyndonkl/claude wc-player-evInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Implements the xEV(player) formula defined in footballfantasy/context/frameworks/scoring-rules.md. Everything in this system that talks about a player's value — a strategist weighting a pick, the captain ladder sampling a candidate's distribution, wc-fitness-eval summing raw_xEV — ultimately samples the number this skill produces. So it has one job and must do it precisely: turn a scout signal (minutes model, npxG/xA, set-piece duty, card/rotation/injury risk) plus the round's fixture and clean-sheet signals into a player's expected points for this round, with the decomposition exposed so downstream agents reason about why, not just how much.
Two properties dominate every other term and must never be smoothed over:
This skill is the floor-and-ceiling factory. It does not decide captaincy (that sequencing belongs to wc-captain-ladder, which samples the per-player distribution this skill emits), does not score rank-relative fitness (that belongs to wc-fitness-eval), and does not derive its own clean-sheet probability or fixture difficulty (those are upstream signals — read them, never recompute).
wc-scout chains into it to attach an EV to a player it has just profiled.wc-strategist genotype calls it per candidate player and then re-weights the output by genotype (A2 multiplies appeal by low-ownership leverage; A5 maximises the floor term; A4 leans on the fixture-scaled attacking terms against a weak opponent). The skill returns a neutral xEV; the archetype tilt happens in the caller.wc-matchday-tactician calls it for XI/bench ordering and to feed the captain ladder the distributions it sequences.wc-fitness-eval consumes the emitted player-ev signals as raw_xEV inputs — it must read them, not re-derive.- [ ] 1. Read the scout signal for this player+round (start_prob, p60, npxg90, xa90, setpiece_share, pen_taker, rotation_risk, injury, suspension, minutes_model). DO NOT re-scout.
- [ ] 2. Read the round fixture signal (opponent strength / mismatch) and the clean-sheet signal (p_cs, expected_ga for the player's team). DO NOT recompute them.
- [ ] 3. Confirm scoring-rules.md point values: is `confirmed: true`? If not, set a provisional flag — relative ordering holds, absolute magnitudes carry a calibration caveat.
- [ ] 4. Resolve the minutes projection: expected_minutes from minutes_model; derive minutes_factor (= proj_min / 90) and the two appearance probabilities (P(plays), P(plays60+)).
- [ ] 5. Scale attacking output: xG_adj = npxg90 × minutes_factor × fixture_mult; xA_adj = xa90 × minutes_factor × fixture_mult. Add the penalty/set-piece premium into xG_adj/xA_adj where the scout confirms the duty.
- [ ] 6. Compute each EV term with the position-specific lambdas (goal weights GK10/DEF6/MID5/FWD4; cs weights GK4/DEF4/MID1/FWD0; etc.).
- [ ] 7. Sum to xEV. Build the decomposition (one line per term, signed).
- [ ] 8. Estimate variance, ceiling (P90-ish best realistic line), floor (P10-ish — usually the no-60'/blank line).
- [ ] 9. Set confidence: cap at 0.35 if minutes are unconfirmed or any load-bearing fact is web-unconfirmed; cap provisional if step 3 failed.
- [ ] 10. Emit the `player-ev` signal via wc-signal-emitter. Return its path.
xEV(player) = P(plays) · appearance_tier_EV # the 1–59' floor
+ P(plays 60+) · minutes_bonus # the 60' cliff (step, not ramp)
+ λ_goal(pos) · xG_adj # fixture- & minutes-scaled npxG (+ pen/SP premium)
+ λ_assist · xA_adj # fixture- & minutes-scaled xA (+ SP-creation premium)
+ λ_cs(pos) · P(clean sheet | fixture) # GK/DEF/MID only — needs 60'
+ λ_save · expected_saves # GK only
+ λ_defact · P(def-actions threshold) # DEF / ball-winning MID
+ λ_setpiece · set_piece_share # residual premium not already in xG_adj/xA_adj
− λ_card · card_risk # yellow/red expectation
− λ_concede(pos) · expected_GA # GK/DEF — the −1 per 2 conceded bracket
All point magnitudes (appearance_tier_EV, minutes_bonus, the λs) are read from scoring-rules.md. The skill never hard-codes them in a way that survives a rules update — if that file says confirmed: false, every absolute number below is provisional.
| λ | GK | DEF | MID | FWD | note |
|---|---|---|---|---|---|
| appearance_tier_EV (1–59') | +1 | +1 | +1 | +1 | floor of any pick that gets on the pitch |
| minutes_bonus (the 60' tier, total when 60+) | +2 | +2 | +2 | +2 | the cliff — modelled as the extra above the 1–59' tier |
| λ_goal | 10 | 6 | 5 | 4 | a DEF goal pays more but fires far less often |
| λ_assist | 3 | 3 | 3 | 3 | position-flat |
| λ_cs (clean sheet, 60+) | 4 | 4 | 1 | 0 | the defensive-stack engine; MID gets a token, FWD nothing |
| λ_save (per 3 saves) | ~0.33/save | — | — | — | GK only; expected_saves × (1/3) |
| λ_defact (def-actions threshold) | — | +2 | +2 | — | CB/full-back clearances-blocks-interceptions or tackles; the hidden floor |
| λ_concede (per 2 conceded) | −0.5/goal | −0.5/goal | — | — | GK/DEF only; expected_GA × (1/2) |
| λ_card | −1 yellow / −3 red | same | same | same | position-flat |
The asymmetry the manager already knows but the math must honour: a defender's goal is worth 6 to a forward's 4, but a centre-back's per-match npxG is a fraction of a striker's, so on raw goal EV forwards still win — except a set-piece-threat CB against a weak, set-piece-vulnerable opponent, whose xG_adj can spike enough that 6 × xG_adj rivals a forward's 4 × xG_adj. That crossover is exactly what this skill exists to surface; do not pre-judge it, compute it.
1. Minutes resolution (do this first — it gates everything).
From the scout's minutes_model derive an expected_minutes projection. Then:
minutes_factor = expected_minutes / 90 (a 70-minute projection ⇒ ~0.78; this scales all open-play output linearly — a player on the pitch 78% of the match generates ~78% of a 90-minute xG/xA baseline).P(plays) = start_prob + (cameo probability if a likely sub). Use the scout's start_prob/p60 directly; do not invent them.P(plays 60+) = p60 from the scout. This is the gate on the appearance bonus and on every clean-sheet/defensive return (those require the 60' threshold).The cliff in practice: a nailed starter with p60 ≈ 0.92 banks the bonus tier almost every week; a 65%-to-start rotation risk with p60 ≈ 0.5 loses roughly half the appearance bonus and half the clean-sheet EV and widens variance — model it as the probability-weighted step, never as "minutes × a rate."
2. Fixture & minutes scaling of attacking output.
fixture_mult = opponent-adjusted multiplier from wc-fixture-progression's `fixture` signal
(a striker vs a leaky minnow > 1; vs an elite, deep-block defence < 1)
xG_adj = npxg90 · minutes_factor · fixture_mult
xA_adj = xa90 · minutes_factor · fixture_mult
npxg90/xa90 are non-penalty per-90 rates from the scout (npxG strips penalties so they aren't double-counted). fixture_mult is read, not derived — wc-fixture-progression already encodes opponent defensive/attacking strength for this specific fixture, and the team's clean-sheet probability comes from wc-clean-sheet-model. Typical band: ~0.6 (brutal matchup) to ~1.5 (mismatch); the fixture signal supplies the exact value.
3. The penalty / set-piece premium (fold in here, then avoid double-count).
If the scout flags pen_taker: true, add the expected penalty contribution into xG_adj:
xG_adj += P(team wins a penalty this match) · P(this player takes it) · P(scores | takes)
A primary penalty taker on a side that draws ~0.25–0.4 penalties/match is carrying meaningful extra goal expectation that open-play npxG misses entirely — this is the premium, and it is why pen-takers are a top-3 scout focus. For primary set-piece delivery (corners, direct free-kicks), add the incremental created-chance expectation into xA_adj. Whatever portion of set-piece value you have already folded into xG_adj/xA_adj here must NOT be re-added by the standalone λ_setpiece · set_piece_share term — that residual term covers only set-piece duty value not already expressed as xG/xA (e.g. a "won penalty" the player draws but doesn't take). Keep the books clean: premium counted once.
4. Clean-sheet term (GK/DEF, token MID).
+ λ_cs(pos) · P(clean sheet | fixture, team)
P(clean sheet) is read straight from the clean-sheet signal (wc-clean-sheet-model) for the player's team in this fixture — never recompute it here. Note it is implicitly conditioned on the player surviving to 60'; if p60 is low, the realised clean-sheet EV is P(cs) · (p60 / typical_starter_p60) — a rotation-risk defender cannot bank a clean sheet he's subbed out of. FWD: this term is zero. MID: token (×1), but it still tips marginal mid choices toward the strong-defence side.
5. Saves (GK only).
+ λ_save · expected_saves where expected_saves ≈ opponent_shots_on_target_estimate (from fixture signal),
scored at (1/3) point per save.
A shot-stopper behind a mid-tier defence facing a strong attack can paradoxically out-save-EV a keeper behind a dominant side who faces three shots — the save points and the clean-sheet points pull in opposite directions, and this skill exposes both so the caller sees the trade.
6. Defensive-actions threshold (DEF / ball-winning MID — the hidden floor).
+ λ_defact · P(player hits the CBI/tackles threshold this match)
From the scout's per-90 defensive-action rate and the fixture (a defender vs a side that attacks a lot sees more clearance/interception volume → higher threshold-hit probability). This is the term that gives a busy attacking full-back or a destroyer-mid a floor independent of clean sheets, and it is why such players are not pure clean-sheet lottery tickets.
7. Negative terms.
− λ_card · card_risk (E[yellow] · 1 + E[red] · 3 ; a booking-prone player or a derby/high-stakes ref tilts this up;
watch yellow-card accumulation before knockouts — a suspension-risk yellow has cost beyond the −1)
− λ_concede(pos) · expected_GA (GK/DEF only; expected_GA from the clean-sheet signal, scored at −0.5/goal i.e. −1 per 2 conceded)
expected_GA is the same upstream clean-sheet signal's field — read it. A leaky defence's downside enters here; it is what makes a cheap defender on a bad side a negative-tail pick even when his clean-sheet term is non-zero.
A set-piece centre-back, nailed starter (p60 ≈ 0.9), npxg90 = 0.12, on a tournament favourite vs a weak set-piece-vulnerable minnow (fixture_mult ≈ 1.4, P(cs) ≈ 0.55, expected_GA ≈ 0.7), not a pen-taker, hits the defensive-actions threshold ~60% of matches:
appearance ≈ 0.95 · 1 + 0.90 · 2(extra→ here total 2) ≈ ~2.8 (floor: he plays and clears 60')
goal 6 · xG_adj = 6 · (0.12 · 1.0 · 1.4) ≈ 6 · 0.168 ≈ 1.0
assist 3 · xA_adj ≈ small ≈ 0.2
clean 4 · 0.55 ≈ 2.2
defact 2 · 0.60 ≈ 1.2
concede −0.5 · 0.7 ≈ −0.35
card −1 · ~0.15 ≈ −0.15
xEV ≈ 7.1 ceiling ≈ 16 (header + CS), floor ≈ 1 (no 60' / conceded 2+)
The point is mechanical, not the exact total: against the right opponent the λ_goal=6 weight plus a real CS probability lifts a set-piece CB into captain-ladder-adjacent territory — and the decomposition shows the manager it's a set-piece-and-stack bet, not form.
variance_band: low|medium|high.wc-fitness-eval leans on when θ=protect.The ceiling/floor split is the whole reason this skill emits a distribution and not a point estimate: A5 (EV Maximizer) reads the floor, A2 (Differential Hunter) reads the ceiling, the captain ladder reads both.
player-ev signal)Emit via wc-signal-emitter to signals/<round>-player-ev-<player>.md:
---
type: player-ev
round: <round id, e.g. 2026-grp-md2>
date: <YYYY-MM-DD>
emitted_by: wc-player-ev
confidence: <0.00–1.00> # ≤0.35 if minutes unconfirmed or a load-bearing fact web-unconfirmed
source_urls:
- <scout signal path or url> # facts trace to the scout signal / fixture / clean-sheet signals, or manager-provided
provisional_scoring: <true|false> # true if scoring-rules.md is not confirmed:true → absolute magnitudes caveated
---
player: <name>
team: <nation>
position: <GK|DEF|MID|FWD>
price: <m>
fixture: <opponent + home/away>
minutes:
expected_minutes: <n>
minutes_factor: <proj_min/90>
start_prob: <0–1> # from scout — not re-derived
p60: <0–1> # from scout — the cliff gate
xEV: <number> # the headline
decomposition: # signed, sums to xEV — MANDATORY (the board/fitness explain through it)
appearance: <n>
minutes_60_bonus: <n>
goal: <n> # λ_goal(pos) · xG_adj
assist: <n> # λ_assist · xA_adj
clean_sheet: <n> # λ_cs(pos) · P(cs) (0 for FWD)
saves: <n> # GK only
def_actions: <n> # DEF/ball-winning MID
set_piece_residual: <n> # premium NOT already folded into goal/assist
card: <-n>
concede: <-n> # GK/DEF only
scaled_inputs:
xG_adj: <n> # npxg90 · minutes_factor · fixture_mult (+ pen premium if taker)
xA_adj: <n>
fixture_mult: <n>
pen_taker: <true|false>
pen_premium_in_xG: <n> # portion of xG_adj from penalties (0 if not taker)
variance: <n>
variance_band: low|medium|high
ceiling: <n> # ~P90 realistic best line
floor: <n> # ~P10 — usually the no-60'/blank line
flags:
- <e.g. "penalty taker — top-3 EV premium">
- <e.g. "rotation risk: p60 0.5, re-check XI ~24h pre-lock">
- <e.g. "yellow-card accumulation: one booking from a knockout suspension">
recheck_before_lock: <true|false> # true whenever minutes are projected, not confirmed
---
start_prob, p60, npxg90, xa90, setpiece_share, pen_taker, rotation_risk, injury, suspension all come from the scout signal. If it is missing for this player+round, return that the scout must run first — do not fabricate minutes or rates.P(clean sheet), expected_GA, and fixture_mult are read from wc-clean-sheet-model and wc-fixture-progression. Re-deriving them here would desync the system and double-handle uncertainty.scoring-rules.md is confirmed: true before trusting absolute magnitudes. If it is not, set provisional_scoring: true and caveat the xEV — relative ordering between players is still usable, exact point totals are not.injury/suspension unresolved) or any other load-bearing fact could not be web-confirmed, and set recheck_before_lock: true. Minutes are the dominant lever — an unconfirmed start makes the whole distribution speculative.minutes/90 × bonus. It is gated on p60. Clean-sheet and defensive returns are gated on the same threshold.xG_adj/xA_adj must not also appear in the set_piece_residual term. Report pen_premium_in_xG so the caller can see it.wc-fitness-eval's and the strategists' job. This skill emits the raw per-round distribution they sample.wc-captain-ladder handles the across-kickoff doubling. Do not 2× anything here.testing
Cluster a conference's event records into a small set of coarse themes with finer sub-clusters, an explicit outlier bucket, and soft (multi-membership) affinities — using the hybrid embed-then-label pipeline (embed abstracts, reduce, density-cluster, then LLM-label the clusters) when embedding libraries are available, and an LLM-reasoned hierarchical fallback when they are not. Embeddings do the grouping; the LLM only names the groups. Conference-agnostic. Use when turning structured event records into a navigable theme map for preference elicitation and scheduling, when you need 6-8 reasonable themes rather than 20 muddy ones, or when overlapping talks must belong to more than one theme. Trigger keywords - theme clustering, cluster talks, embed then label, soft membership, outlier talks, conference themes, topic map.
development
Build a personal conference schedule as a constraint-optimization problem — hard constraints (no time overlap, room-to-room travel time, capacity/registration, the attendee's own must-attends and blackouts) plus a user-owned weighted objective trading interest against breadth, pacing (maximize contiguous free time), and serendipity. Surfaces unbreakable conflicts (two high-value overlapping talks the model cannot rank) as decisions for the human rather than silently picking, and reports what each choice traded away. Conference-agnostic. Use to turn a preference profile plus a theme map into a day-by-day plan, to resolve overlapping sessions, or to balance a packed vs paced schedule. Trigger keywords - schedule optimization, conference schedule, constraint optimization, overlapping talks, contiguous free time, conflict surfacing, packed vs paced.
development
Parse a heterogeneous conference program (markdown, HTML, PDF-derived text, or JSON) into normalized event records with per-field confidence scores and independent classification axes (topic, depth, format, prerequisites, recorded, capacity). Detects the program's format before extracting, treats every inferred field as uncertain (present vs inferred vs missing), and flags thin or missing abstracts so downstream enrichment can target them. Conference-agnostic. Use when ingesting a conference or event schedule into a structured store, normalizing a talk/session list, or extracting per-session metadata with calibrated confidence. Trigger keywords - program ingestion, parse schedule, session extraction, event records, conference program, talk metadata, per-field confidence.
development
Build a personalized preference profile from a small number of well-chosen, cluster-grounded questions instead of a long survey. Represents the person's interests as an uncertainty region over the theme map, picks the single highest-information-gain choice-based question (contrasting real talks from different clusters), balances exploiting known interests against exploring uncertain ones, deliberately injects outlier probes to fight selection bias, and stops as soon as the schedule would be stable. Also elicits the user-owned objective weights and hard constraints. Interactive — runs where it can actually ask the person. Conference-agnostic. Use to turn a theme map into a preference profile, to decide what to ask a conference attendee, or to elicit scheduling priorities. Trigger keywords - preference elicitation, ask few questions, information gain, choice-based questions, selection bias probe, objective weights, attendee preferences.