Gates for GW6, checked on the payload Thu 8 Oct 2026 00:40 BST · buildPayload. Producer’s own gate summary: pass — {"clubs":20,"ids_unique":true,"fixtures_target_gw":10,"target_round_shape":{"gw":6,"fixtures":10,"scheduled":10,"unscheduled":0,"finished_count":0,"complete":false,"first_ko":1791631800,"last_ko":1791831600,"span_h":55.5,"clubs_playing":20,"clubs_doubled":0,"placeholder_fixtures":0,"placeholder":false},"results":50,"results_missing_fd":0,"results_with_bfe":50,"results_with_fd_xg":50,"score_mismatches":0,"availability_rows":128,"understat":true,"understat_clubs":20,"unequal_played":[],"duplicate_fixture_ids":[],"fotmob_played_skew":[],"odds_join_collisions":[],"clock_source":"live","engine_hash":"97cc49a9abb300438863b7cba83e2ff69fdbe89740303a06ebfe7f5882ec86e8","engine_port_region":["PLIngest.validate","PLIngest.fromPayload","ratesData","resultsIndex","playedPairs","playedCount","playedBy","contextData","gsnFactors","factorTerms","impliedGoals","phaseStrengths","solveMultipliers","solve","strengths","marketPrior","engineFor"]}
- G0 Clock · Pass
- 380 fixtures across 38 rounds, 0 unscheduled, 200 placeholder, 10 this round, 330 unfinished ahead. Target round 6, review 5.
- G1 Currency · Pass
- Team-appearance parity: 100 club-appearances in the fixture list against 100 declared across clubs[]. Exact.
- G2 Integrity · Pass
- Read from the payload’s producer gates, not typed: Understat delivered for 20 of 20 clubs; no poison markers reported. Score mismatches across the two result pipelines: 0. Producer orphans: none.
- G3 Provenance · Partial
- Passed for the team layer — two models, both labelled, disagreements listed. Still unmet for the player layer: no second rating model.
- G4 Cross-source · Pass
- Scorelines from two pipelines, FPL and football-data.co.uk, with 0 mismatches. Availability from two sources, FotMob and FPL’s own status flags, on 20 of 20 clubs (128 entries); where they disagree both are listed rather than reconciled. Premier Injuries was the third source up to GW3.
- G5 Temporal · Pass
- Every capture keeps its own instant: the reference family is stamped when it is captured (model_ref_at, pm_ref_at) and the commit at locked_at, and nothing is re-fetched or re-dated afterwards. 20 of 30 ledger rows carry a reference capture; K6 and K7 hold the two families to their windows.
- G6 Transport · Pass
- Every numeric source is fetched directly as JSON or CSV, never through a page extractor: the sheet’s Apps Script reads FPL, FotMob and football-data.co.uk’s E0.csv each morning (it replaced Coupler on 9 September), and the Payload script reads Understat and Polymarket. This morning’s run: 17 of 18 datasets OK.
- G7 Reconciliation · Pass
- A call’s lock timestamp is the endpoint’s locked_at and its denominator is the round’s fixture count in rounds[]. A rebuild never creates a lock or moves a call, and a correction is an appended row (L12, L14). The ledger holds 30 rows across GW3–GW5.
- G8 Benchmarks · Partial
- External lines the routine logged into the ledger: Opta GW3 9 of 10 · GW4 10 of 10 · GW5 1 of 10; Forebet GW4 10 of 10 · GW5 4 of 10; Solio clean sheets GW3 10 of 10. Polymarket’s reference price is on 20 of 30 rows. A call with no external line is marked unbenchmarked.
- G9 Self-review · Pass
- Each routine run reviews its own ingest before it publishes and records any defect in its run note on claude/desk-runs. At GW3 (4 Sep) it caught three: positional column misalignment in the injury union, a stale snapshot served after a query fix, and an unscoped path that flattened a team payload into 21,623 columns.
- G10 Selection · Pass
- New in v1.6. Every named goalscorer is checked against the availability manifest before its card ships. At GW3 it rejected Hinshelwood (Brighton, out to mid-September), and the rejection was printed on the card rather than quietly swapped. Measured this run: 60 scorer entr(ies) across 20 club(s) · 43 marked available · none contradicted by availability[] · manifest keyed on 20 of 20 clubs.
- G11 Normalisation · Pass
- New in v1.6. Factor weights asserted to sum to exactly 100% and no single weight to move more than four points in a round. v1.5 shipped at 108% for two gameweeks, which is the whole reason this gate exists. Measured this run: 100% nominal across 13 factors · 88% effective + 12% suspended (Managerial reset).
- G12 Sample scaling · Pass
- Any factor keyed to a season-to-date residual must declare a shrinkage prior, and from v1.7 one per phase: τ = 5 open play and strengths, 6 regression, 14 set pieces.
- G13 Coherence · Pass
- New in v1.7. The published scoreline, the confidence number and the clean-sheet line must come from one joint distribution, and the scoreline must lie inside the outcome region the probabilities favour. Asserted on every live card. Measured this run: 10 of 10.
- G14 Opponent adjustment · Pass
- New in v1.7. Every rate entering the engine is divided through by the strength of the opponents actually faced, solved to a fixed point (check S4 prints the passes and the residual). Raw season-to-date rates can no longer reach a match card. Measured this run: geometric 0.992 / 1.017 · arithmetic 1.005 / 1.027.
- G15 Venue parity · Pass
- New in v1.8. The home term may appear once, on the home intensity. Asserted by checking that a fixture and its reverse produce goal expectations differing by exactly the home multiplier, not its square. Measured this run: max deviation 0 against ENGINE.HOME = 1.14 (squared would be 1.2996).
- G16 Weight realisation · Pass
- Every published weight must be readable by the simulation or declared as living inside the strength solve, with its measured share of adjustment printed. v1.8 flagged partial because managerial reset realised most of the card against a nominal 12%: fourteen of twenty clubs carry a first-season manager and the penalty was absolute, so it was deflating every fixture at once. v1.9 splits the factors into relative and tempo terms and zero-centres the relative ones within each fixture, which preserves the margin effect exactly and removes the league-wide deflation. Realised shares are printed beside the nominal weights on the Model tab. Measured this run: 100.0% attributed · Σ|dh|+|da| 0.8328 against 0.8328 recomputed.
- G16b Prose coherence · Pass
- New in v1.8. No card may state an engine figure in its own text. Splits, margins, confidences, scorelines and the Opta stance are tokens filled from that fixture’s engine object at render, so an engine change cannot leave the prose arguing a withdrawn call — which it did on three successive versions.
- G17 Game state · Pass
- The decay filter discounts chance volume accumulated while two or more behind to 0.86 and credits volume accumulated while ahead at 1.28, normalised to a league mean of 1.00 so it redistributes attacking credit without moving the goal mean. Met from v2.1 change 10: the payload carries per-club score-state xG, so the filter runs on real splits and the per-club multipliers are published on the strengths table beside the solved pair. The 0.86 / 1.28 weights were set by judgment and this is their first outing, so read the multiplier column as a claim under test. Measured this run: active on 20 clubs · multipliers published on the strengths table.
- G18 Key parity · Pass
- New in v1.9. Club names are the join key across the rates table, the fixture list and the five context maps, and a single mismatched spelling silently drops a club from the opponent adjustment or lets a played fixture be simulated twice. All keys reconciled against the rates table; orphans are reported here rather than absorbed. None this run. Measured this run: no orphans across 7 tables.
- G19 Sample parity · Pass
- New in v2.0. The declared match count, the fixture list and the per-club appearance counts must agree, because everything that turns a season total into a rate divides by that number. v1.9 hard-coded two matches in three separate places, so a payload appending a round would have halved every rate in the league and under-weighted the evidence at the same time. Measured this run: 50 pairs for played = 5 · all 20 clubs on 5.
- G20 Lock immutability · Pass
- New in v2.0. A fixture past its kickoff renders the price it carried at that moment, from a stored snapshot. Every version up to v1.9 re-derived all ten cards from the live engine on every render, so each model change silently re-priced matches already played while the page asserted that calls were immutable. The live engine is still published beside the snapshot, and where they now differ the gap is printed. Measured this run: in-file GW3 card · 10 of 10 frozen · 0 checked against a ledger model_split · every frozen card matches its ledger row · 3 now differ from the live engine.
- G21 Round clock · Pass
- New in v2.0. The page compares each fixture’s kickoff with the current time and reports a round whose matches have been played but whose results have not been ingested, instead of continuing to present the card as live. Asserted on the review round. Measured this run: GW5 · closed Sun, 20 Sept · 10 of 10 results ingested.