| name | wc-fixture-progression |
| description | 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. |
wc-fixture-progression — difficulty, progression odds, mismatches, swings
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.
Inputs
- The round id, phase, and bracket from
tournament-state.md (group mini-tables, who plays whom, current standings).
- Per-team attacking and defensive strength: team xG-for and xGA per match (web-searched, cited; manager-provided where a model exists). Seed/pot and world ranking at the draw for the reference class.
- Observed results to date this tournament (goals, goal difference, xG over/under-performance) for the Bayesian update.
- Confirmed kickoff order within the round (from
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.
Workflow
- [ ] 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
Method
1. Fixture difficulty — two directions, never one number
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-analyst reads both columns, never an averaged "FDR".
2. Progression odds — reference class, then Bayes
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):
- Group stage — P(advance from group) by pot: Pot 1 ≈ 0.80, Pot 2 ≈ 0.60, Pot 3 ≈ 0.40, Pot 4 ≈ 0.20 (these sum to ~2 per group of four, i.e. two qualifiers — sanity-check that each group's four priors total ≈ 2.0 and renormalise if not).
- Knockout single tie — P(win the tie) from the seed gap: roughly even (≈0.50) between peers, ≈0.62 for a one-tier favourite, ≈0.72 for a two-tier favourite, ≈0.82 for a three-tier-plus gulf. Never 1.0 — the highest a single-match World Cup favourite earns is ~0.85; the residual is the upset rate the reference class exists to preserve.
(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:
- With one round to go, list the realistic remaining scorelines, find which combinations qualify the team (including the tie-breakers in order: points → goal difference → goals scored → head-to-head per the official rule, confirm in
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).
- 2026 best-thirds path (do not use top-two-only math): the top two per group plus the 8 best third-placed teams (of 12) advance. So
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.
- Surface clinched (
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.
3. Weak-group / mismatch detection
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:
- Powerhouse-vs-minnow trigger: strength delta ≥ ~2 difficulty bands and one of {
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.
- Weak-group flag: a whole group containing a clearly dominant side and ≥2 weak attacks — it will generate multiple cheap clean sheets and goal hauls across the group across all three matchdays; flag the group so the strategists know to mine it, not just the single fixture.
- For each mismatch, name the direction(s) (attack, defence, or both) and the asset type unlocked, so the board can route it: A1 captains the favourite's premium attacker, A4 stacks the favourite's defence, A2 looks for the low-owned member of the same mismatch (the third-choice striker who'll still feast).
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.
4. Fixture swings — the forward calendar
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:
- For each team build the difficulty path across the next
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:
- Easy→hard swing (sell signal): a team enjoying a 1-2 stretch hits a 4-5 fixture in round t+k → plan to transfer its attackers out before round t+k, and (if KO) note whether its elimination-risk round arrives first (don't pay a transfer to dodge a fixture in a team that may be gone anyway — coordinate with
tournament-state.md elimination horizons).
- Hard→easy swing (buy signal): a team coming off tough fixtures into a 1-2 run → get there a round early, before ownership rises, to bank the value the lagging field hasn't priced.
- Annotate each swing with the round id, the direction, the asset class affected (attack vs defence — a swing can be one-directional, e.g. the opponent is leaky but also dangerous:
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.
5. Dead-rubber / rotation guard
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:
- Tag the 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.
- A clinched team still shows
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.
- 2026 caveat — third place is usually alive, so true dead rubbers are rarer than top-two math suggests. A team sitting 3rd on MD3 typically still has a live best-thirds path (above), and even a clinched team may play its starters to win the group (a kinder R32 bracket slot) or to protect goal difference for the thirds tie-breaker its rivals need. Only tag
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.
Output — the 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>
source_urls:
- <url for team xG/xGA>
- <url for results/standings>
- <url for seeds/ranking>
---
fixture_difficulty:
- team: <nation>
round: <round id>
opponent: <nation>
home_away: H|A|N
fd_att: <1-5>
fd_def: <1-5>
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:
- 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>
by_round: { r32: <p>, r16: <p>, qf: <p>, sf: <p>, final: <p> }
upset_rate: <1 - p_win_tie>
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.
Guardrails
- Never price a favourite as a certainty. Every
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 before narrative. Anchor on the seed/pot/ranking base rate first (
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.
- Two directions, always. Never collapse
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.
- Dead-rubber rotation is a hard flag, not a footnote. Once a team has qualified or is out, its remaining group asset minutes are unreliable — tag it so the EV and fitness skills discount it. A clinched team's progression is 1.0 but its next-fixture value is not.
- Swings must respect the elimination clock. Don't recommend a transfer to dodge a future hard fixture in a knockout team whose elimination-risk round arrives first (
tournament-state.md) — coordinate the swing calendar with each asset's horizon, or the move is wasted.
- Confirm the strength inputs or cap confidence. Team xG/xGA, seeds, and standings are load-bearing; web-search and cite each, or mark manager-provided. Any unconfirmed load-bearing fact caps the whole signal's
confidence at 0.35 and gets a "confirm before lock" note for the board (signal-framework.md).
- Emit; don't re-derive. If a
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.