Reconcile Understat branch with current main #30
No reviewers
Labels
No labels
architecture
cleanup
dependencies
enhancement
needs-info
needs-triage
performance
priority: high
priority: medium
ready-for-agent
ready-for-human
reliability
security
testing
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
solvreven/FPL!30
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/understat-current-npxg"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This branch is now synchronized with main at
2184143while preserving its original commits. Its final file tree is identical to main: merging this PR integrates branch history and makes no application or data changes.The substantive Understat commits
4f20625,70e8b0fand8958166are already exact ancestors of main. The branch-only CI change is patch-equivalent. The update retains current per-club coverage, recurring retries, digest-based rebuilds, and the modular player-index implementation.Validation:
9c6bc16: syntax, 2,144 tests passed / 145 skipped, and wheel build/install/import checks.No feature cherry-picks, baseline changes or history rewrites were needed. This PR remains open for merging.
Removes the six-book consensus from the served surfaces and makes NT the market for match odds, anytime goalscorer and anytime assist. The consensus files stay on disk and score_goalscorer.py still scores both every round, so this is a code change, not a lost comparison. NT does not open its markets all at once -- measured 2026-09-01/02, goals open ~4 days out and assists ~2, per fixture -- so a single pre-deadline pull could never carry both. nt_watch in chores.py polls on COVERAGE ("does the newest banked run carry both markets for every fixture of the next round", answered from the manifests on disk) rather than on a clock or a completion marker, so a pull someone ran by hand counts. The denominator is the round's fixture count from FPL, never the run's. Four producer-without-a-consumer defects surfaced while wiring it, three created by the NT-only switch itself: disagreements and fixtures_without_market were computed and discarded by main.matches; gw{n}_market_nt.json was written by nt_resolve with nothing rebuilding the cache to read it; and n_fixtures counted match_odds while the table was drawn from a different subset. _unpriced_but_ours no longer returns one flat list. "No book priced X -- the omission is the bookmakers' saying he is unlikely to feature" was printed over five players in two fixtures NT had not opened at all, which is a verdict reported where the book had said nothing. It returns {"declined": [...], "unopened": [...]} keyed on the FPL team id, because the two need opposite responses. pre_deadline_steps is now pull -> resolve -> rebuild -> snapshot, so the frozen cache is the one built from the frozen prices. snapshot_pit.odds_for takes both market files; the raw NT run is zipped rather than pointed at, since prune_nt_runs can delete the directory. Retires _books_expect_him: the coverage-share gate cannot be computed from one book. Reported rather than patched around -- raising our own start_prob floor would let the weakest number in the system decide membership. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014adnmRztkdAPwinWXnNQ7NAdds `understat_watch` to chores.py, beside `nt_watch`. It asks "does the banked current-season file reach the round that has been played" and answers it FROM THE FILE, so a fetch someone ran by hand counts and the ledger is never the evidence -- a completion marker is not evidence of what was completed. NOT folded into after_gameweek.py, and the reason is measured rather than assumed. Understat had GW2 by 48h after that round's last kickoff, while after_gameweek fires the moment the round ends. Chaining it there would fetch before Understat published and leave us permanently one round behind: self-healing in principle, never actually caught up. One observation bounds the lag at <=48h and cannot pin it tighter, which is the argument for keying on coverage rather than on a clock -- the design does not need the number. THE FETCH IS ALWAYS FOLLOWED BY A REBUILD. foreign_rates.json and the current-season file are read at CACHE BUILD time and nowhere else, so a fetch with no rebuild behind it refreshes a file no projection ever sees. That is the shape nt_resolve sat in for a week, writing gw{n}_market_nt.json that only a rebuild reads. No season is written down: the season id and the output filename are both DERIVED from the label FPL reports (understat_season_arg, understat_out_path). A BUG THAT WAS LIVE FOR ABOUT A MINUTE, and the sharpest thing here. Coverage counted all five leagues. Ligue 1 had played 3 rounds while the PL had played 2, so it returned 3, never fell below the denominator, and the watch would have been PERMANENTLY SATISFIED without ever firing once. The denominator is the FPL gameweek, so the numerator has to be EPL rows -- counting one population while describing another is the "20 fixtures" coverage line in a different file. Pinned by test_coverage_counts_only_the_league_the_denominator_belongs_to. 8 tests here, 21 across the three Understat files. Every cut RED, including two on the WIRING rather than the mechanism: removing the planner hook goes red while every helper test stays green, and making the watch fire unconditionally goes red too. A mechanism nothing calls is a comment. Verified at commit time: the 21 targeted tests pass and all mutation cuts go RED. The full suite was still running; its result is reported separately rather than asserted here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014adnmRztkdAPwinWXnNQ7NUnderstat: current-season npxG, a live squad list, and a chore that fetches itto Reconcile Understat branch with current main