The entry form, the account model, the AI commentary path - and the decision behind each one, not just what it looks like. Extracted and condensed from Tourney's own in-app product documentation.
A full entry is 22 separate picks across 5 groups (seeds 1-4, 5-8, 9-12, 13-16, and coaches) - tabbed by group, with a live pick counter per group and per seed so a player always knows where they stand. Eliminated players stay visible on a roster that already had them, so a pick can be removed but never silently disappears.
None of that in-browser guidance is what actually decides whether an entry counts. The one thing that intercepts submission client-side is a confirmation popup for an undecided play-in pick, asking the player to confirm they understand it could still be eliminated - everything else, including "did you pick exactly the right number in every group," is enforced server-side on submit. The UI is there to help a player get it right the first time; the server is what's actually trusted to get it right at all.
Anywhere a game, player, or team appears - a standing, a bracket, a comparison - it links straight back to that exact ESPN page, built directly from the same ESPN IDs the scoring pipeline itself uses.
That's a deliberate choice, not a convenience feature: the links aren't proxied through the app's own routes, so a link and the score sitting next to it are always describing the same underlying ESPN record. A player never has to just trust a number - they can click through to the exact box score, player page, or team page it came from.
Register, log in, submit or edit an entry, reset a password if needed - four actions, each with one real security decision behind it.
The bracket is never half-empty: a game already final locks in as a win or loss, and a game still ahead shows a projected winner, favoring whichever team the entry actually drafted players from. If two of an entry's own picks are on a collision course to meet each other, the bracket flags the earliest round that could happen, before it happens.
The head-to-head compare view doesn't duplicate any of that - it calls the exact same projection function twice, once per entry, then colors each slot by whether entry A picked that team, entry B did, both did (a genuine collision), or neither did. One function, reused, is what keeps the single-bracket view and the compare view from ever quietly drifting apart.
Every day the app writes a short AI commentary on how the standings changed, and a player can run it two ways: Simple, where the app hands the model everything it needs in one message, or Agentic, where the model gets no data upfront and has to ask for what it needs from a fixed, short list of questions before it writes. Either mode can run on Claude (hosted, real tokens spent, access-code gated) or on the self-hosted Spark (free, no code needed) - the same task, run head-to-head on two fundamentally different kinds of model.
Three safety rules hold regardless of which path runs: the self-hosted model never touches the database directly, every answer it gets is one the app decided to hand it, checked every time; personal contact information is never sent to any model, in any environment - only a display name and bracket name already public on the leaderboard; and nothing a model writes is trusted as formatting or code, its output is filtered before anyone sees it. A player can turn on "Watch processing" and see the actual prompt, any tool calls, and a timeline of the run - not a curated demo of it.
Before trusting the scoring engine with a live, once-a-year tournament, it gets tested against a tournament that already happened - by re-pointing the active year at a past season and re-running the exact same import-and-score pipeline against real ESPN data from that year. Same tables, same scoring logic, just scoped by year.
That reuse is deliberate: historical testing exercises the actual production scoring path, not a simulated stand-in for it. Because both historical mode and a separate daily-simulation mode work by pointing the same "active year" setting at a different value, running either one means the current live tournament isn't the active year for that session - an admin switches back explicitly when testing is done, rather than the two modes silently overlapping.