Changelog

Model updates, feature releases, and bug fixes for the Model90 prediction engine.

v2.6.9Latest2026-06-23

Immediate post-match sync: top scorers, discipline, and standings now update the moment a match finishes.

  • BetterTop scorers, standings, and discipline leaderboards are now synced immediately when a match transitions to FT — previously they waited up to 3 hours for the next scheduled sync cycle.
  • BetterCLV (closing-line value) calculation and the auto-retrain counter are now triggered on match completion via notify_fixture_completed — these were defined but never called.
v2.6.82026-06-23

Fix OAuth login redirect looping to internal Railway address.

  • FixOAuth login was redirecting to http://0.0.0.0:8080 after Google sign-in — the sign-in page now uses window.location.origin (always the real browser URL) instead of a baked-in env var, and the callback Route Handler now reads x-forwarded-host/x-forwarded-proto headers to resolve the correct public origin.
v2.6.72026-06-23

Security, reliability, and quality improvements — error hardening, model cache TTL, improved health checks, smarter API retries, and frontend error boundaries.

  • BetterBackend errors no longer expose internal exception details to API callers — all upstream failures return a generic message; full details are in server logs only.
  • BetterAuth token fetch (JWKS) now uses the same httpx client as all other outbound HTTP calls — consistent timeout, no silent hangs.
  • BetterAdmin token validation now rejects short or whitespace-padded tokens at startup with a clear warning.
  • BetterContent-Security-Policy header added to all API responses.
  • BetterML model artifact cache now expires after 10 minutes — newly trained models become active automatically without requiring a process restart.
  • BetterRequest IDs are now propagated into server log records, enabling full traceability from HTTP response to log line.
  • BetterHealth endpoint now verifies database connectivity and returns 503 when the database is unreachable.
  • BetterScheduler jobs are now protected against overlap — each job can only run one instance at a time.
  • BetterPostgreSQL queries now have a 30-second hard cap to prevent slow queries from exhausting the connection pool.
  • BetterFrontend API calls now retry automatically on 502/503/504 and network errors using exponential backoff — transient deploy blips are invisible to users.
  • NewGlobal error boundary added to Next.js app — unexpected crashes now show a recovery prompt instead of a blank page.
  • NewLoading skeletons and route-level error boundaries added to key pages.
  • BetterDependabot enabled for weekly security updates across Python, Node.js, and GitHub Actions dependencies.
  • FixEWMA form feature was weighting oldest matches most heavily — corrected to give highest weight to the most recent match.
  • FixFeature vector builder now always returns a 30-element array instead of None when fixture data is missing.
  • BetterSentry error reports now tagged with deployment environment and release ID for per-deploy regression tracking.
  • BetterGraceful shutdown: scheduler waits for in-flight sync jobs to finish before the process exits, preventing partial DB writes during deploys.
v2.6.62026-06-23

Bug fixes — corrected invalid Python import syntax in the training module and fixed a FastAPI 204 response body assertion on the comment delete endpoint.

  • FixSyntaxError in models/train.py: `from datetime import datetime, timezone` was incorrectly written with a comma — prevented background model training from starting.
  • FixFastAPI startup assertion: DELETE /api/comments/{id} with status_code=204 now correctly declares response_class=Response so no body is serialised.
v2.6.52026-06-23

Reliability and observability — circuit breaker, retry logic, model checksums, Prometheus metrics, and SEO metadata.

  • BetterAPI Football client now has a circuit breaker (trips after 5 consecutive failures, recovers after 60 s) — prevents cascading failures when the upstream data provider is unavailable.
  • BetterExponential backoff retry (3 attempts: 1 s, 2 s, 4 s) on network errors and 5xx responses from the data provider.
  • BetterHTTP client upgraded from requests to httpx across the ingestion layer.
  • BetterModel artifacts are now checksummed (SHA-256 sidecar file) on save and verified on load — tampered or corrupted models are refused.
  • FixProduction database failure now causes a hard startup error instead of silently falling back to an empty SQLite database.
  • NewPrometheus /metrics endpoint — exposes default Python process and GC metrics for scraping.
  • BetterEvery API response now carries an X-Request-ID correlation header for distributed tracing.
  • BetterAll Python dependency versions pinned in requirements.txt for reproducible builds.
  • BetterSEO metadata (title + description) added to all pages; admin and auth pages marked noindex.
v2.6.42026-06-23

Security hardening — authentication, rate limiting, concurrency, and input validation improvements across the full stack.

  • BetterAdmin API authentication centralised into a single enforced layer — dual-auth via static token or Supabase JWT, applied uniformly across all admin endpoints.
  • BetterCORS policy tightened to the production origin only.
  • BetterRate limiting (200 req/min per IP) is now active across all endpoints.
  • BetterSecurity headers added to every response: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Referrer-Policy, and HSTS on HTTPS.
  • FixComment like/unlike now uses atomic database operations — counts stay accurate under concurrent traffic.
  • FixInternal caches are now thread-safe, improving reliability under concurrent load.
  • FixAuth redirect flow hardened — redirect targets are validated before use.
  • BetterAdmin UI forwards the user's session token on every API call for proper server-side authorisation.
  • BetterComment input validation tightened: entity type is now a strict enum, body is capped at 2,000 characters, and list results have bounded page sizes.
  • BetterAll internal timestamps are now timezone-aware (UTC) throughout the backend and data pipeline.
v2.6.32026-06-23

Trophy Race — Monte Carlo now truly stochastic, history chart fixed to include AET/PEN matches, auto-sync on empty history.

  • FixMonte Carlo group stage was deterministic: all 3,000 runs shared identical group qualifiers because standings were projected once outside the loop. Group stage now uses Poisson sampling inside each run — every simulation explores a genuinely different tournament scenario.
  • FixPoints History chart missed knockout matches settled by extra time (AET) or penalty shootout (PEN) — standings history now includes all three completed statuses.
  • FixHistory chart never refreshed unless the competition was changed — Refresh button on the odds panel now co-refreshes the chart, and a dedicated Refresh button was added to the Points History section.
  • BetterWhen standings history returns empty for a known league, a background sync is triggered automatically so the next request populates the chart without manual admin intervention.
v2.6.22026-06-22

Translation overhaul — complete Turkish coverage across all pages and components; fixed bad Turkish strings.

  • BetterAdded 70+ missing Turkish translations covering search, comments, team stats, player stats, nav groups, and loading states — previously showing raw English in Turkish mode.
  • FixGoal Difference column header corrected from 'A/F' to 'Av' (standard Turkish football abbreviation for Averaj).
  • FixGroup standings 'Advances' label changed from 'İlerliyor' to 'Tur Atlar' (advances to next round).
  • FixPost-match narrative connector was an empty string in Turkish — replaced with '→' so sentence structure no longer breaks.
  • BetterNav dropdowns (Tournament, Insights, Explore), bottom-nav 'More' / 'All Pages', and individual page links now fully translated via the central i18n system instead of scattered inline checks.
  • BetterComments section (title, placeholder, Post/Cancel/Save/Edit/Delete/Reply buttons, sign-in prompt) now fully translated.
  • BetterSearch component (placeholder, result-type badges, 'No results' message) now reads from the language setting.
v2.6.12026-06-22

Bug fix — corrupted expected-goal predictions (values in the tens of millions) no longer display; root cause patched in the optimizer.

  • FixPoisson optimizer now uses bounded attack/defense parameters (±3.5) — prevents degenerate values when teams have sparse match history.
  • FixExpected-goal lambdas are clamped to a maximum of 8.0 before storage, preventing garbage values from propagating into the DB.
  • FixResults and fixture-detail pages now guard against corrupted predicted-goals values already in the DB — display '–' instead of millions.
  • FixGoals MAE (Off-by metric) and summary mean-goals-MAE skip matches where predicted goals are invalid.
v2.6.02026-06-21

Trophy Race upgraded — multi-competition picker, per-league Monte Carlo simulations, and interactive historical points-timeline chart.

  • NewTournament/league picker on the Trophy Race page — switch between any enabled competition to see its championship probabilities.
  • NewPer-league Monte Carlo simulation: Cup/tournament leagues use the bracket simulation; regular round-robin leagues use a fixture-based season simulation.
  • NewInteractive Points Timeline chart — tracks team points progression round by round throughout the tournament or season.
  • NewClick team pills on the chart to toggle individual team lines for focused comparison.
  • NewAPI endpoint /api/standings/history returns cumulative standings checkpoints for charting.
  • BetterChampionship odds endpoint now accepts league_id and season params with per-league 30-minute background cache.
  • BetterTrophy Race hero banner shows the selected competition name dynamically.
v2.5.22026-06-21

Bracket page now supports any tournament — tournament picker, generic group/knockout display, dynamic bracket sizing.

  • NewTournament picker on the bracket page — switch between any enabled league (World Cup, Champions League, domestic leagues) via a horizontal scroll of cards with logo, name, and season.
  • NewBracket page adapts to any group-stage competition — groups, qualifiers, and knockout matches are built dynamically from whatever data exists for the selected tournament.
  • BetterBracket size is now dynamic: 12-group tournaments (WC) produce a Round of 32 with 8 best-3rd slots; 8-group tournaments (UCL) produce a Round of 16 with no extra slots.
  • BetterKnockout stage label updates automatically (Round of 16, Round of 32, etc.) based on the computed bracket size.
  • BetterHero banner shows the selected tournament's logo and name instead of hardcoded World Cup content.
  • NewEmpty state shown when a selected tournament has no group-stage data yet.
  • NewAPI endpoint /api/bracket/leagues returns all enabled tournaments for the picker.
v2.5.12026-06-21

Bracket page fix — non-World Cup teams no longer appear in group standings or Best 3rd-Place Qualifiers.

  • FixBracket page was showing teams from other leagues (e.g. Serie A, Atalanta) in Best 3rd-Place Qualifiers — /api/bracket now defaults to the World Cup league when no league_id is provided.
v2.5.02026-06-21

Prediction engine interactive overhaul, results redesign, and national-team transfers view.

  • BetterPrediction engine page rebuilt with animated live-accuracy counters, 8-stage interactive pipeline explorer, and 4-tab deep-dive (Core Math / 6 Context Signals / Ensemble & Meta / In-Play Engine).
  • BetterResults page redesigned — accuracy-vs-baseline progress bar, circular correct/wrong badges, top-pick scoreline star, per-match Brier score inline.
  • NewTransfers page now browsable by all 48 WC 2026 national teams with flag pill selectors.
  • NewCompact Transfermarkt-style squad table: player avatar, club badge, jersey number, position, transfer fee/type, and expandable transfer history timeline.
  • NewAPI endpoints /api/wc-nations and /api/wc-squad-transfers for squad and transfer data per nation.
  • FixTransfers sync now correctly uses PostgreSQL ON CONFLICT upsert (was silently falling back to SQLite dialect in production).
  • FixDuplicate transfer rows no longer cause CardinalityViolation on batch upsert — rows are deduplicated by (player_id, transfer_date, team_to_id) before insert.
  • FixResults page server-side crash — event handler onClick was incorrectly passed from a Server Component to a Client Component (Link).
v2.4.02026-06-21

Prediction engine enhancements, CLV backfill, and admin tooling.

  • NewCLV (Closing Line Value) backfill endpoint — historical predictions now include closing-odds comparison.
  • NewAdmin sync endpoints for on-demand fixture and odds refresh.
  • BetterStacker threshold lowered to 30 samples for faster ensemble activation.
  • FixNumPy bool/int/float JSON serialization in model registry.
  • FixAsync error handling in comment handlers no longer crashes the fixture page.
v2.3.0Major2026-06-14

Stacker ensemble introduced, team fixture self-match bug fixed.

  • ModelVoting ensemble (vensemble_v1) stacks XGBoost + LightGBM + Random Forest with learned weights.
  • ModelELO ratings integrated as live features — model now adjusts for tournament momentum.
  • NewPer-round performance tracking (Brier, log-loss, RPS, ECE) stored after each round.
  • NewSHAP feature importance computed and exposed via /api/model-insights.
  • FixTeam fixtures page was showing the team itself as the opponent due to str/int type mismatch.
  • FixHydration mismatch crash on fixture page resolved.
v2.2.02026-06-01

Live scores, in-play probabilities, and mobile navigation overhaul.

  • NewLive in-play probability updates — model recalculates odds each minute during a match.
  • NewLive goals timeline on fixture page.
  • NewBottom nav 'More' sheet with full page directory.
  • BetterFixture page two-column layout on desktop.
  • BetterShare button with clipboard copy on fixture pages.
  • FixOdds display showing stale pre-match values during live games.
v2.1.02026-05-20

Head-to-head data, player ratings, and match events timeline.

  • NewHead-to-head match history displayed on fixture pages.
  • NewPlayer ratings and MVP highlight from API-Football.
  • NewMatch events timeline (goals, cards, substitutions).
  • NewFormation pitch visualisation for starting XIs.
  • BetterScoreline distribution chart updated to highlight actual result for finished matches.
v2.0.0Major2026-05-01

Model90 public launch — Dixon-Coles Poisson model with ELO edge.

  • ModelDixon-Coles Poisson model for goal scoring rates with low-scoring correction.
  • ModelELO edge feature — relative team strength from historical results.
  • ModelAPI-Football integration for form, injuries, lineups, and live odds.
  • NewProbability bars with model confidence chip on each fixture page.
  • NewELO rankings page with historical trend charts.
  • NewTeam comparison tool with side-by-side xG and form.
  • NewLight and dark theme with system preference detection.

Model90 is continuously updated throughout the 2026 World Cup.