skills/wc-fixture-progression/SKILL.md
Rates fixture difficulty (separately for an attacking and a defending fantasy asset, 1-5 each way, from opponent xGA/xG), computes team progression probabilities (group qualification odds from the mini-table; round-by-round knockout survival down the bracket) by reference-class forecasting updated Bayesianly on observed form and group state, detects weak-group/mismatch fixtures (powerhouse-vs-minnow -> captain candidates and clean-sheet locks), and builds a multi-round swing calendar of where each team's difficulty flips so transfers can be planned ahead. Emits the `fixture` signal. Use when called by wc-fixture-analyst, by the strategists in the populate stage, and by wc-fitness-eval for its progression_carry term — and never re-derive these once the signal exists.
npx skillsauth add lyndonkl/claude wc-fixture-progressionInstall 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.
The engine behind wc-fixture-analyst and the source of the p_advance figures wc-fitness-eval discounts in its progression_carry term. It does four jobs the rest of the system reads off rather than recomputing: rate each fixture's difficulty in both directions, put a calibrated probability on each team advancing each round, flag the lopsided fixtures where points come cheapest, and lay out a forward calendar of difficulty swings so transfers land ahead of the field's lag (game-theory-meta.md §4).
The probability work is deliberately two-stage: an outside view first (reference-class advance rates by seed/ranking — reference-class-forecasting), then a Bayesian update on what we actually observe (form, goal difference, group state — bayesian-reasoning-calibration). A favourite is never priced as a certainty; the reference class always carries an upset rate, and the dead-rubber rotation flag fires the moment a team has clinched.
tournament-state.md (group mini-tables, who plays whom, current standings).wc-matchday-timing if already emitted) for the swing calendar's day-tagging — optional, fixture difficulty does not need it.Read the round's clean-sheet and scout signals if present, but do not re-derive them — this skill supplies the fixture-strength scalars those skills consume, not the other way round.
- [ ] 1. FIXTURE DIFFICULTY — rate every fixture 1-5 for an attacker AND 1-5 for a defender (opponent xGA -> attack ease; opponent xG -> clean-sheet ease)
- [ ] 2. PROGRESSION ODDS — outside-view base rate by seed (reference class), then Bayesian update on form + group state; groups via the mini-table, knockouts round-by-round down the bracket
- [ ] 3. WEAK-GROUP / MISMATCH — flag powerhouse-vs-minnow fixtures; tag attackers as captain candidates, defenders as clean-sheet locks
- [ ] 4. FIXTURE SWINGS — find the rounds where a team's difficulty flips (easy->hard or hard->easy); build the swing calendar to plan transfers ahead
- [ ] 5. DEAD-RUBBER CHECK — flag any fixture where a team has already qualified/is already out (rotation risk on that fixture's assets)
- [ ] 6. Emit the `fixture` signal (both-direction difficulty grid, p_advance per round, weak_group_flags, mismatch_list, swing_calendar) via wc-signal-emitter
A fixture is not equally hard for the attacker you own and the defender you own. A goalkeeper against a toothless attack and a striker against a porous defence can sit in the same match with opposite difficulty. So rate every fixture twice on a 1-5 scale (1 = easiest for that asset, 5 = hardest):
Attacking ease (fd_att) — driven by the opponent's defensive concession rate. Read the opponent's xGA-per-match and convert to a band. The easier the defence leaks, the lower (better) the score for your forwards/attacking mids:
| opponent xGA/match | fd_att | meaning for your attacker |
|---|---|---|
| ≥ 1.8 | 1 | shooting gallery — captain/ceiling territory |
| 1.4 – 1.8 | 2 | soft |
| 1.0 – 1.4 | 3 | neutral |
| 0.7 – 1.0 | 4 | stingy |
| < 0.7 | 5 | elite back line — fade your attackers |
Clean-sheet / defensive ease (fd_def) — driven by the opponent's attacking output. Read the opponent's xG-for-per-match. The blunter their attack, the lower (better) the score for your GK/defenders:
| opponent xG/match | fd_def | meaning for your defence |
|---|---|---|
| < 0.7 | 1 | clean-sheet lock — stack candidate |
| 0.7 – 1.0 | 2 | strong CS odds |
| 1.0 – 1.4 | 3 | neutral |
| 1.4 – 1.8 | 4 | leaky game likely |
| ≥ 1.8 | 5 | shootout — avoid the defensive stack here |
Adjust each band by ±1 for home/away (host or strong-home-support nations defend better and attack more freely at home — nudge fd_def down and fd_att down at home, up away) and clamp to [1,5]. These are the scalars wc-player-ev multiplies xG/xA by and wc-clean-sheet-model reads for P(clean sheet | fixture) — emit them, do not let those skills re-estimate opponent strength independently.
The two directions can disagree sharply and that is the point: a 1/5 split (
fd_att=1,fd_def=5) is a high-scoring-game signal — load attackers, avoid the back line; a 5/1 split is a low-event game — your GK is a lock, your striker is a trap.wc-fixture-analystreads both columns, never an averaged "FDR".
The probability a team advances is a forecast, so build it the disciplined way: outside view, then update.
(a) Outside view — the reference class (reference-class-forecasting). Anchor on the historical advance rate for a team of this seed/pot/ranking before looking at the current team's narrative. Starting base rates (World Cup historical seed-advance frequencies; refine against the cited source, mark provisional until confirmed):
(b) Bayesian update on observed reality (bayesian-reasoning-calibration). Treat the base rate as the prior and update on what this tournament has actually shown:
prior_odds = p_base / (1 - p_base)
posterior_odds = prior_odds × Π_i LR_i
p_advance = posterior_odds / (1 + posterior_odds)
Evidence likelihood ratios LR_i (multiply the ones that apply; keep each bounded so no single signal swamps the prior):
| evidence | LR direction | rough magnitude |
|---|---|---|
| won MD1 / positive goal difference | > 1 | 1.3 – 1.7 |
| lost MD1 / negative GD | < 1 | 0.6 – 0.8 |
| xG strongly out-performing (over-performing finish — regress) | slightly < 1 | 0.85 – 0.95 |
| xG strongly under-performing (unlucky — expect mean-reversion up) | slightly > 1 | 1.05 – 1.20 |
| key player injured/suspended for the decisive match (cite scout) | < 1 | 0.5 – 0.8 |
| favourable remaining group draw vs unfavourable | > 1 / < 1 | 0.8 – 1.25 |
Cap the total swing so a single MD1 result can't move a Pot-1 side from 0.80 to a near-certainty — calibration over reactivity. After MD2/MD3, the group mini-table arithmetic dominates the prior: compute qualification combinatorially.
(c) Groups — qualification odds from the mini-table. Once games are played, enumerate the remaining-results space rather than trusting the seed prior:
league-config.md/competition rules), and weight each combination by the per-match win/draw/loss probabilities implied by the two sides' fd/strength. p_advance_group = Σ P(qualifying combinations).p_advance_group = P(top two) + P(3rd) × P(3rd-place points/GD ranks in the best 8 | the cross-group thirds table). The cross-group comparison means a "likely 3rd" team is often ~50–70% alive, not out — and qualification can stay undecided until other groups finish. Track the live thirds table once MD2 results land.p_advance = 1.0, but tag dead-rubber — see step 5) and eliminated (p_advance = 0.0) explicitly; these flip the whole asset calculus.(d) Knockouts — round-by-round survival down the bracket. A team's value horizon is the product of surviving each successive tie:
p_reach(round R) = Π_{r = current..R} p_win_tie(r)
where each p_win_tie(r) is the seed-gap base rate updated by form, against the likeliest opponent at that node (use the bracket; if the next opponent is itself unresolved, average over that opponent's own p_reach to this node). Emit the full ladder p_advance[round] for r32, r16, qf, sf, final — this is exactly the per-round survival curve wc-fitness-eval multiplies into progression_carry (Σ_r P(team alive at r) × discount^r). A team that is 0.72 to win the R16 but only 0.30 to reach the QF tells the carry term to value its assets for ~1.7 more rounds, not the whole bracket.
Points come from mismatches (the A4 Fixture Exploiter thesis). Flag a fixture as a mismatch when the strength gulf is large enough that returns become cheap and near-deterministic:
fd_att ≤ 2 for the favourite's attackers, fd_def ≤ 2 for the favourite's defence}. Concretely: favourite's fd_att = 1-2 → its attackers are captain candidates (ceiling + safety together — the chalk-captain and differential-captain logic both start here); favourite's fd_def = 1-2 vs a sub-0.7-xG attack → its defenders/GK are clean-sheet locks and a natural BB2 stack + Clean Sheet Shield target.Mismatches are where ownership concentrates and where the field is quickest to converge — so a mismatch flag is also an instruction to wc-ownership-analyst to expect chalk, and to the differential hunters to find the under-owned way into the same game.
The field is slow to react to fixture turns (ownership lags reality by ~a round). Identify, per team, the rounds where its difficulty flips so transfers land before the field moves:
H rounds (group: the remaining matchdays; knockout: the bracket path weighted by p_reach). A swing is a flip of ≥2 bands in either direction:
tournament-state.md elimination horizons).fd_att improves while fd_def worsens), and a plan-ahead lead (how many rounds before the swing the transfer should fire to beat the field). Feed this straight to wc-transfer-planner.The moment a team has clinched (or been eliminated from) qualification, its final group fixture becomes a rotation risk: managers rest starters, the clean-sheet and minutes assumptions break, and an owned asset's xEV can collapse without any injury. For every such fixture:
dead_rubber: true and drop a rotation_risk: high note keyed to that team's assets, so wc-scout and wc-player-ev discount minutes and wc-fitness-eval doesn't carry phantom progression value on a benched starter.p_advance = 1.0 for progression, but its next-fixture difficulty and minutes carry the dead-rubber caveat — keep the two separate so the carry term isn't double-penalised.dead_rubber: true when the team's qualification and seeding/GD incentives are both settled — otherwise tag rotation_risk: medium with the reason, and let wc-scout confirm the team-news reality near lock.fixture signal---
type: fixture
round: <round id, e.g. 2026-grp-md2>
date: <YYYY-MM-DD>
emitted_by: wc-fixture-progression
confidence: <0.00-1.00> # cap at 0.35 for any load-bearing strength/odds fact web-search couldn't confirm
source_urls:
- <url for team xG/xGA>
- <url for results/standings>
- <url for seeds/ranking> # or "manager-provided"
---
fixture_difficulty: # both directions, 1 (easy) .. 5 (hard), per team per round
- team: <nation>
round: <round id>
opponent: <nation>
home_away: H|A|N
fd_att: <1-5> # from opponent xGA — ease for YOUR attackers
fd_def: <1-5> # from opponent xG — ease for YOUR defence/GK
opp_xGA_per_match: <n>
opp_xG_per_match: <n>
note: <e.g. "1/5 split — high-scoring game, load attack, fade the back line">
p_advance: # the per-round survival ladder fitness-eval discounts
- team: <nation>
p_advance_this_round: <0.00-1.00>
method: reference-class+bayes | group-mini-table | clinched | eliminated
base_rate: <0.00-1.00> # the outside-view prior (seed/pot/seed-gap) BEFORE update
by_round: { r32: <p>, r16: <p>, qf: <p>, sf: <p>, final: <p> } # p_reach = Π survival
upset_rate: <1 - p_win_tie> # explicit — a favourite is never 1.0
drivers: [ "won MD1 +2 GD (LR 1.5)", "xG over-performing, regress (LR 0.9)" ]
weak_group_flags:
- group: <id>
dominant: <nation>
weak_attacks: [<nation>, <nation>]
note: "multiple cheap CS + goal hauls available across all three MDs"
mismatch_list:
- fixture: <FAV vs MINNOW> (<round id>)
strength_delta_bands: <n>
favourite: <nation>
unlocks: [ captain_candidate (attack), clean_sheet_lock (defence) ]
differential_angle: <e.g. "FAV's 2nd-choice striker — same feast, sub-8% owned">
swing_calendar:
- team: <nation>
swing_round: <round id>
direction: easy_to_hard | hard_to_easy
axis: attack | defence | both
delta_bands: <n>
plan_ahead_lead: <rounds before the swing to transfer to beat the field>
action: "sell attackers before <round>" | "buy a round early, pre-ownership-rise"
elimination_caveat: <e.g. "team's R16 elim-risk precedes this swing — may be moot">
dead_rubber_flags:
- team: <nation>
fixture_round: <round id>
status: clinched | eliminated
rotation_risk: high|medium
note: "discount minutes/CS for this team's assets this fixture"
Every numeric field declares its range so the emitter can range-check it (signal-framework.md). Downstream agents read these columns; they must not re-estimate opponent strength or advance odds for the same round.
p_advance carries its base_rate and an explicit upset_rate from the reference class; the single-match ceiling is ~0.85. If you've written 1.0 for anything other than a mathematically clinched group position, the Bayesian update has over-fit to a small sample — pull it back to the reference class.reference-class-forecasting), then update on form (bayesian-reasoning-calibration). Do not let a single MD1 scoreline or a hot-form story set the prior; bound each likelihood ratio so no one signal swamps the base rate.fd_att and fd_def into one "difficulty" number — a fixture that is easy for attackers can be hard for the defence and vice-versa, and conflating them is the most common fantasy fixture error. Emit both columns.tournament-state.md) — coordinate the swing calendar with each asset's horizon, or the move is wasted.confidence at 0.35 and gets a "confirm before lock" note for the board (signal-framework.md).fixture signal already exists for this round, read it. This skill is the single source of fixture-strength scalars and progression odds for the round — downstream skills consume it rather than recomputing opponent strength.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.