Special Features

A full, honest look under the hood: how scores go from ESPN to your standings, the visual bracket logic, the search for what predicts a good draft pick, and the tradeoffs behind decisions like skipping a traditional database backup. This is the implementation-level follow-on to Big Picture, which stays the at-a-glance summary.

01How ESPN scores become player standings

not yet — wait window open box score write + log repeats immediately, plus an hourly re-check Scheduler tick every 60 seconds — active window? 5+min since last? Ask ESPN today's games, filtered to teams still in our bracket Compute & write winner + every player's points atomic — old numbers cleared first Standings update live for every affected player + logged for admins to watch
gate passed
automatic
repeats / waits

Scoring is fully automated: the system checks ESPN for updates every five minutes during active tournament windows, without anyone needing to press a button. It's built as a lightweight, self-managed process rather than a dedicated job-scheduling service — a pragmatic choice for the current scale (see Big Picture §01), safeguarded so that only one process is ever checking scores at a time. Once a game finishes, ESPN's result is matched to the correct teams, players, and picks in our system, and every affected player's standing updates automatically. As a safety net, results are re-checked once an hour even after a game is marked final, and admins have a manual "run now" button plus a one-click repair tool if ESPN's own data ever needs correcting. A separate "daily simulation" mode lets the team test the scoring logic against real, in-season games before the tournament starts, without affecting live bracket data. The admin dashboard's own long-running actions (an ESPN import, Run All Steps) use a related but separate technique to show live progress in the browser: a background thread writes to a shared log table while the page polls a small partial on a short interval — no websockets, just polling. Full detail on that mechanism is in the admin-only build docs, not here.

Two APIs, two directions. "API" shows up twice in how a score gets here, in opposite directions: app/espn.py above is outbound — this app is the client, fetching team info and rosters, resolving each player's season points-per-game from ESPN's athlete-stats API, and pulling full box scores per game, explicitly excluding NCAA-tournament games from the season-schedule fetch (games are fetched without seasontype=3) so regular-season analytics never accidentally mixes in tournament games. app/blueprints/worker_api.py is the other direction — inbound, this app as the server — letting the Spark queue's worker process claim a pending job and post progress and results back over HTTP, without ever holding a database credential. It's the newer of the two, built as part of Spark Queue v2 and proven working end to end in dev: a real job ran the full queue-and-poll loop against real MySQL and a real local Ollama with correct event ordering. SPARK_ENABLED stays off in production until that path is turned on there — see AI: Foundation vs. Open-Weight for the mTLS transport and the rest of the queue's engineering detail.

02The visual bracket report

This is the centerpiece feature players see: a full tournament bracket that colors in as the tournament unfolds, showing at a glance who's still alive and who's out. It does more than display results — it projects the entire bracket forward, even for games that haven't been played yet.

1 Houston 16 SIU-Edw. ↳ advances (r-win) 9 TBD — not yet played
  1. Completed games — once a result is final, the team is locked in as a win (green) or loss (red, name struck through).
  2. Games not yet played — the bracket still shows a projected winner for every remaining matchup, favoring whichever team the viewer actually drafted players from, so a player's bracket always looks complete rather than half-empty.
  3. Early-warning alerts — if a player has drafted players from two teams that are on a collision course to face each other, the bracket flags the earliest round that could happen with a 💣 warning icon, so the risk is visible before it happens.

There's also a side-by-side comparison view, so two players can see their brackets projected next to each other.

This logic now has automated test coverage as well (see Deploy) — the exact scenarios above (a confirmed result overriding a projection, a projection favoring the viewer's own pick, and the earliest-collision math) are each checked automatically on every change.

03Predicting which players will score the most

This is a predictive question, not a bracket simulator: given everything known about a player, which factors actually predict how many points they'll score in the tournament — and can that help players draft smarter? The active approach is a dedicated analytics warehouse, covered in full on its own page.

Current direction: a separate DuckDB-based warehouse pulls conformed, analysis-ready copies of teams/players/games/odds out of the live database, kept deliberately apart from it so exploration never touches production. See Data Warehouse for the full schema, the reporting-then-statistics-then-AI approach, and the first real findings.

An earlier attempt at this same question exists in the codebase (app/blueprints/analytics.py) — a self-contained pipeline that pulled nine years of ESPN box scores, built a per-player feature profile (seed, scoring average, rebounds, assists, team strength, national ranking, hot streaks), and trained a gradient-boosting model to rank likely top scorers. It's fully built but was shelved — the blueprint is present but not registered, so none of its routes are reachable. It's kept as reference for feature ideas rather than revived, since the warehouse-based approach gives a cleaner, more reproducible foundation to build the same answer on.

04Why there's no traditional database backup

This is a deliberate call rather than a gap, given what the data actually is and what it costs to lose.

The reasoning: almost everything in the database is either re-collectable or re-derivable from a source of truth outside our own system, so a full backup-and-restore pipeline would mostly be insuring against a scenario with a cheap fallback.
  1. Almost nothing in this system is typed in by hand — the one exception is what a player selects on the entry form. The 68-team bracket field and every team's roster are pulled directly from ESPN once the field is announced on Selection Sunday night, not manually keyed in by an admin. Scores are then pulled from ESPN in real time throughout the tournament, automatically, as covered in §01 above. The only information that actually originates inside our own system is a player's entry — their picks, name, and contact info from the entry form.
  2. Every result traces straight back to ESPN, and can be checked against it directly. Because games, teams, and players are all keyed to ESPN's own IDs, every score and standing in the app can be validated by following a link back to the exact ESPN game, team, or player page it came from — there's no "trust us" step; the source is always one click away.
  3. All game and scoring data is rebuildable straight from ESPN. ESPN's public API is the actual source of truth the app already pulls from continuously — if the database were ever lost, every score, result, and standing could be regenerated by simply re-running the same ESPN fetch the app already does every five minutes during the tournament. Player picks are the only data that isn't ESPN-sourced, and that's also the smallest, lowest-volume dataset in the system — a single form submission per player, recoverable by re-collecting entries if it ever needed to be rebuilt.
  4. The scale doesn't justify the overhead. This is a small, free pool for a modest group of players, not a system where downtime or data loss carries financial or contractual consequences — so the cost of maintaining a backup pipeline isn't worth paying today. This is exactly the kind of tradeoff worth revisiting if the app ever grows in scale or stakes (see Big Picture §01).

05Other components worth knowing about

ComponentRole
Claude / Anthropic + the self-hosted open-weight modelPowers the optional admin daily-commentary feature via direct API calls to both a hosted foundation model (Claude) and a self-hosted open-weight model — see AI: Foundation vs. Open-Weight for the full comparison.
Amazon SES (Simple Email Service)The only other production AWS service in use besides the server itself — sends account emails such as password resets
Traffic protectionsBuilt-in safeguards against request flooding and cross-site form abuse, applied across the whole site

06Known architecture decisions, scoped for Tournament 2027

These three items are not gaps discovered after the fact — they're known architecture tradeoffs, made deliberately, and already scoped as planned work rather than open risk. 2026 delivery prioritized shipping on a fixed, non-negotiable deadline — the tournament starts when it starts — on top of infrastructure already proven to work. That's the right call under a hard timeline, and it also means these three specific improvements were consciously deferred, not missed. All three are scoped for the offseason ahead of Tournament 2027, when they can be made outside the pressure of live tournament delivery.

Planned itemWhat it adds
Real database migration toolReplaces the current startup-time approach (see Deploy) with a standard, versioned migration tool — giving every schema change a reviewable, reversible history instead of relying on application code
Deploy rollback pathA way to revert a bad release to the last known-good version directly, rather than the current approach of pushing a new fix commit forward
Monitoring and alertingActive notification (uptime checks, error alerts) if a core process — like the scoring scheduler — stops running, instead of relying on someone noticing
This page reflects the system as it is actually built and deployed today, based on a direct review of the source code, configuration, and deployment pipeline. Previously the second half of Big Picture, split onto its own page 2026-08-03 so Big Picture could stay an at-a-glance summary. Also reachable at /about/architecture, which redirects here.