Changelog
Everything we've shipped on RoundsUp, in order.
V1.7 - October 14, 2026
League chat, and the Operator plan. Your league now has its own members-only chat. Members talk between nights, share photos, GIFs and reactions, and see the director's pinned announcements at the top of the chat. Chat is off until a director turns it on in Settings, under League messages, and the director approves who joins. Members can report a message or block someone, and the director can remove a message or a member. Chat is never public: it is not shown on any league's public pages and is not indexed for search.
League chat comes with Operator, $79 a year: everything in Pro, plus the chat. Every new league gets it free for its first 30 days, the same trial that already covered Pro. When a league's Operator access expires, its enabled chat becomes read only and history is kept. Pilot leagues keep access. Scoring, formats and the big screen stay free on every plan, as they always have.
V1.6 - October 9, 2026
Asked for at the machines. Almost everything in this release started as one sentence on a league night: a player who wanted to find their group without a slow search, a scorekeeper who tapped Confirm one game too early, a director who wanted a raffle draw between rounds. Six weeks of those sentences, built and shipped.
Scan scores. Take a photo of the machine's score screen and the score boxes fill themselves. Set the play order first, so the app knows whose score is whose, then tap Take the photo. Each photo is read three times and a box only fills when the reads agree, so a blurry digit leaves the box for you instead of guessing. Every filled box says "From photo, check it", a box you already typed in is never touched, and nothing saves until you tap Confirm. Scan scores is in early access on a first group of leagues, and a director turns it on in Settings.
Set play order. Groups that care who shoots first can now say so. Tap Set play order above the score boxes, then tap each name in the order you are playing: Player 1, Player 2, and on down. A taken name reads "tap to undo", Same order as last game fills it in one tap, and the score boxes follow the order you played. It is optional and never opens by itself, so the groups that do not care never see it get in the way. A director switches it on for the league in Settings.
Find my group. Tell your phone who you are once ("That's me, remember on this phone") and league night does the rest: your group wears a bold outline and a Your group badge, your name says "you", and Go to my group scrolls straight to it. Your own rows say "you" on your score card and in the standings too. Change clears it, and there is nothing to sign up for.
Enter scores is always on top. The league bar now stays pinned at the top of every league page, and its first stop always reads ENTER SCORES, so a player opening the link for the first time knows exactly where to go. The league home also gained three quick links: the latest night's scores, that night's standings, and the season.
Light mode, one tap away. Bright room, dim phone? Every league page now ends with a plain Light mode / Dark mode button. It says what it does in words, switches instantly, and remembers your choice on that phone.
How to score, on every machine. Every machine on the score screen now carries a How to score tap right under its name, with tips for the titles people argue about and a plain note where there are none yet. Putting RoundsUp on an iPhone got clearer too: the steps sit in their own box, with the buttons to tap in bold.
Check-in that keeps up with the door. A big Check in players button now sits on the meet, and the check-in list stays right when you step away and come back or when two phones are checking people in. Need a hand at the door? Make a helper link from the check-in screen and share it or let them scan its QR code: your helper can check people in for that one night and nothing else, and the link turns itself off when the night is done or after 24 hours. The door kiosk gained a search box ("jose" finds "José") and a plain "Do not see your name? Ask at the door." for walk-ups. Leagues that want it can have guests who check in or RSVP join the league automatically.
Fix a score without the hunt. Tapped Confirm too early? The Scores Saved popup now has Go back and fix these scores. Directors can tap any score on an open group card in the cockpit and change it right there, with no scrolling to the bottom of the night. Score boxes now read billions shorthand (2b, 1.5B) correctly, an odd-looking score names every player it is unsure about before it saves, and a second phone saving the same game never wipes out what you were typing.
Two groups, one machine? Now you'll know. When a group picks a machine another group is still playing, the list says "In use by Group 3" and asks "Pick it anyway?" first. Rooms with fewer machines than groups can still double up; nobody gets surprised.
A faster setup, start to finish. Draw groups builds the whole night from the people in the room in one tap, fours first and threes when the count needs them. Reroll groups reshuffles the board, Draw machines gives every group a starting machine, and Start fills any blanks for you. Autofill now really shuffles, so a new night never comes out the same as last week's. The first-night guide walks a new director through one step at a time and follows what you already did instead of asking twice, a practice game lets anyone rehearse scoring without touching a real night, and Random pick draws a raffle winner from tonight's checked-in players, by division if you use them.
Season standings that run your rules. Drops can wait until the season has held a set number of nights, a missed night can be the one that drops, and best nights can break ties. Divisions can split the standings into blocks and draw league-night groups inside them from the meet you choose. A preview shows which nights would drop before drops begin, night highs and season highs per machine get their own boards, and an optional +1 goes to the top score on each machine each night. Every one of these is off until a director turns it on. And ties now read honestly everywhere: equal totals break by the standard tiebreak chain, never by name, and the leader line never says "leads by" when the top two are level.
Your league, your numbers. Settings now covers what used to be fixed: games per night (a four-game league can finally run), how often you meet (an every-other-week league stops being advertised as weekly), your timezone, and how a DQ scores, including a new option where the DQ scores 0 and everyone else keeps their place (7, 5, 3, 0). A meet keeps the rules it started with, so a settings change never rewrites a night already played. And any night, or a whole season, downloads as a spreadsheet: every score on your own device, just in case.
Forfeit a game, or set the points by hand. Someone arrives late? In the cockpit a director can mark a player's game as forfeited: that player scores 0 for the game and everyone else is scored on the table for one fewer player, so a four-player game pays 7, 4, 1 and 0. The player plays the rest of the night as normal, and the league's DQ rule stays exactly as it is. When a ruling calls for it, a director can also type a player's points for a game directly; the number stays until they switch back to From the table, and public pages mark it with a footnote.
Tournaments: more formats, your own points. New this release: head-to-head and 1v1 knockout with fair byes, and double round robin. Group Knockout now goes by the name players know, Strikes. Directors can write their own point tables for tournaments and leagues, pick balanced pairing so players meet more of the field, and spread machines so the same players repeat the same game less. Group play can record a finishing order with scores optional, qualifying can feed a finals that plays more rounds, ties can stand as shared places, and rounds and games per round stay editable until the cut. Scorekeeper helpers can enter results at the desk, and Run it again copies an event into a fresh draft.
Test mode for tournaments. Rehearsing a new format? Mark the event as a test. It stays out of every public listing and off the venue TV, wears a TEST badge wherever it can be reached, and will not produce an IFPA export, so a practice run can never pass for a real result.
A smarter wall. Before the night starts, the venue TV shows the season standings instead of an empty groups screen, switches to the lineup at Start, and goes live with the first score. A tournament on the wall now hands the screen over when a league night starts. A night closed early, with most of its games unplayed, says "Night closed early" and shows the top three instead of crowning a champion. And the tournament TV draws the whole bracket tree, winners and losers, every line connected.
Under the hood. Every change in this release went through independent review, and the fixes shipped alongside the features: two phones can no longer claim the same game slot at the same moment, a typed draft can never land on a machine it was not typed for, a mis-tapped extra ball can be corrected after submit, knockout placings follow the round you went out in, and a guest who says "different person" is never merged into the roster player with the same name. Reopening a finished meet now asks first and says what it does, Dismiss on a save conflict stays dismissed, every refused save reads clearly on a phone, and lost links land on a branded page. The league directory got a search box and a city on every card, a player in two leagues sees one combined record on their home page, new plain-language pages explain league scoring and tournament formats, Help has a human support line, and the test suite grew past 11,000 checks.
V1.5 - August 29, 2026
Forged on a real league night. On August 27 a full league ran an entire night on RoundsUp - walk-ins, score entry, the broadcast TV, the champion moment - and everything in this release comes from what that room taught us.
Un-start a meet. Started the night and then two more people walked in? The cockpit now has an Unstart button: the meet drops back to Setup so you can fix the lineup, every entered score is kept, and Start picks the night right back up. The TV shows a calm PAUSED lamp instead of pretending nothing happened, and any score a phone was holding when you paused waits patiently and sends itself after you press Start.
A last screen nobody skips. The final review screen now says what it is - "Last step. Your group's scores are NOT submitted yet." - and the renamed Submit Final Scores button rides a bar pinned to the bottom of the phone, on screen the moment the last score lands. No more groups walking away one tap early.
One clean rail across your whole league. Every league page now opens with the same three-stop bar at the top. On a live night it reads SCORING / MY NIGHT / STANDINGS - the lit stop is where you are, each label explains itself, and a small dot tells you when your phone still holds unsent scores. Every other time it reads LEAGUE NIGHT / MY NIGHT / STANDINGS, pointing at tonight's hub and the season race. One tap home from anywhere, and the days of hunting for a way back to score entry are over.
Check-in at the door. Prop up a tablet at the entrance and players mark themselves in: tap your name, confirm, get a welcome splash. The list shows who said they were coming, walk-ups stay the TD's call, and every check-in feeds straight into the meet builder - so building the night from the people actually in the room is now tap, tap, done. The kiosk link (with a QR) lives on your check-in page.
The QR works before you press Start. A meet built for today now shows up on your league-night link ahead of time: the room scans in, sees the lineup and their group, and the moment the TD presses Start every phone flips into score entry by itself - no refreshing, no "try again now."
Reroll the groups. Random groups came out weird? A Reroll button now sits right on the group assignment screen. Two taps (so a stray thumb can't torch your hand-tweaks) reshuffles the whole board, opening machines included, as many times as it takes.
RoundsUp on the home screen. Add RoundsUp to your phone from any league page and it installs like an app - your league's players get an icon that opens straight into league night, full screen, no browser chrome. iPhone users get the exact two steps spelled out; Android gets a real one-tap install.
Rename your league whenever. Settings now has a League card: change the display name any time - your web address stays fixed, so printed QR codes and shared links never break.
A search box nobody can miss. The machine-pick screen's type-to-find field got bigger, grew a magnifier, and now says what it does: type a few letters and the game pops up. It always could - now people will know.
Signage that stops stuttering. Dedicated signage boxes (the cheap TV-stick kind) chugged on the broadcast's smooth motion. The new ?motion=lite switch turns every continuous effect into clean discrete steps the weak hardware handles easily - while the earned moments, the score flashes, stay fully animated. The signage setup guide now reflects real field findings.
Under the hood. The whole release went through adversarial review, and it caught real ones before you could: a way for a hostile league name to inject code into public pages (fixed and locked with a test), a paused meet dropping offline scores (they now survive and replay), and a dozen more. Getting-started now counts an invite the moment you send it, the create-league form speaks plain language, and the test suite grew past 4,800 checks with the first live night's every lesson pinned so none of it can quietly come back.
V1.4 - August 23, 2026
The big screen becomes a broadcast. Add ?mode=broadcast to any meet TV URL and the night gets a real broadcast package. A persistent masthead carries your league's mark, the live lamp, and the night's progress; an apron along the bottom runs the latest result and what's coming up next; and the stage in between composes to the size of the night instead of shrinking one grid. Small nights get a full-screen final table. Big nights get a timing tower: the top three pinned with movement arrows while the entire field glides by on a slow scroll, beside a floor of group panels that rotates through every group on a wall-clock schedule, so two TVs on the same wall stay perfectly in step. It reads from across the room at 72 players. And it is built for real signage hardware: motion honors reduced-motion, a ?motion=still switch pins everything for stubborn players, and there is a setup guide for dedicated signage boxes.
Colors that tell the story. On the broadcast screen, green means playing and your league's color celebrates what is finished: done groups wear the accent frame, winners light up in it, and the champion moment glows with it. Your accent and logo carry through every lamp, chip, and plate automatically, contrast-checked in both directions.
The lineup before the first ball. Until the night's first score exists, the TV shows tonight's lineup: every group, every name, seat by seat, plus the machine each group opens on. The walk-in crowd finds where they belong without crowding the TD's phone. The moment the first score lands, the show goes live on its own, and it stays live for the night.
Two new formats, a finals worth the name, and longer matches. High Score: players post raw scores across the machine pool and the highest totals rise, the arcade model, no groups. Pingolf: a golf course of machines, each with a target score, your strokes are the balls you needed to reach it, lowest total wins. Matchplay qualifying can now cut to PAPA-style grouped finals. And group matches can run best of N games per station when one game per machine is not enough. That is eight launchable formats out of the box, every one free, on every tier.
The late crew is not a problem anymore. A group walks in after round one? Add Group Live seats them into the running meet from the cockpit, audit trail and all, and the old mid-meet wall for late arrivals is gone.
Results worth posting. Every finisher gets a share card: their night, their result, sized for the group chat and the feed, with the native share sheet one tap away.
Answers at 9pm. The new help center at /help covers the questions that actually come up mid-night, written for the TD standing next to a machine, not for a support ticket queue.
The fine print, finally. Terms of Service and a Privacy Policy are live at /terms and /privacy.
A front door for players too. Signing up no longer assumes you are a director: a neutral welcome asks what you are here for and routes players and TDs each to their own path.
Under the hood. This release was field-tested hard: simulated field testers drove every journey end to end, and a full 72-player production night was load-tested at 2,100 concurrent requests with p95 response times under 58 milliseconds. Score edits are concurrency-guarded so two scorekeepers can never silently overwrite each other. Duplicate-player protection now lives at the database level, and season imports keep player identity straight and fail closed instead of guessing. Password reset emails land in the inbox. The TV survives bad wifi, hard reloads, and long unattended nights. Another security pass tightened row-level rules and locked down token columns. And the whole product wears its type better: a sharper display face with tabular numerals everywhere data lives.
V1.3 - July 6, 2026
Sign-ups that run themselves. Schedule a tournament, open registration, and players sign up from the public page with no account. Set a cap and the waitlist handles the overflow: when someone withdraws, the next person in line moves up automatically. Run a sign-up desk at the door, launch when you're ready, and the entrant list flows straight into the bracket. No spreadsheet, no group-chat roll call. Registration is free, on every tier.
A new format: Flip Frenzy. The pinball-native race, free on RoundsUp like everything else. Quick one-on-one games run continuously on every open machine, players cycle through one line, and whoever has the most wins when time is called takes it. Full TD controls, a live public viewer, a big-screen panel, and zero-account player reporting, same as every other format. That makes six formats out of the box, each one free on every tier.
Moments worth looking up for. RoundsUp now catches the big ones. When a house record falls, the big screen calls it out. And Clinch Watch does the math live: exactly who has locked a spot, who is still alive, and who is out, recomputed as scores land, so the room knows what is at stake without anyone doing arithmetic in their head.
The league season, front and center. Your league standings now add up across the whole season, with a drop-the-lowest option so one rough night doesn't sink a player. Public, branded, and free. This is the thing RoundsUp was built for: nobody else owns the modern weekly league night.
A rulings assistant in your pocket. Sixty of the situations that actually come up at a tournament, each with the official ruling and the exact citation from the 2025 IFPA and PAPA rules, checked word for word against the source. It works with no signal at the venue. And on the score screen, a How To Score tap-reveal explains the machines players always argue about.
Seed by rank, and see what the night was worth. Building a bracket of a known field? Seed it by IFPA ranking in one tap, right in the create flow. And when a tournament finishes, RoundsUp estimates its WPPR value so you can see what the night earned before you submit it.
Bring on a co-TD. Invite a co-organizer or a scorekeeper to help run the night. A scorekeeper can enter and fix scores but never touches settings or the roster; a co-organizer can do everything. Remove someone any time, and the last admin can never lock themselves out. Free, on every tier.
A read-only API for the builders. Every league's standings, meets, seasons, and live tournaments are available as clean JSON at a documented, rate-limited endpoint. Point a scoreboard, a stats page, or a stream overlay at it and build. It ships with live examples in the docs.
A big screen worth pointing a camera at. The TV got sharper: an idle marquee that cycles your upcoming events, a live scorebug, a density toggle for the size of the room, and a broadcast overlay for streams. Standings show a podium and movement arrows so you can see who climbed since last time. On phones, score tables pin their headers, fade their edges to show there is more, and hint that you can swipe, and public meet pages refresh themselves as results land.
Score entry that feels instant. Submitting a score confirms right away and syncs in the background, so a slow venue connection never leaves a scorekeeper tapping and waiting. The offline queue keeps working through a dropped signal and reconciles the moment it comes back.
Under the hood. Two co-organizers editing the same tournament settings can no longer overwrite each other. The scorekeeper survives a failed refresh with a banner instead of a blank screen. Every anonymous write is rate-limited and audited, and every change carries who made it, so nothing on the standings is a mystery.
V1.2 - July 2, 2026
Tournaments, start to finish. RoundsUp now runs full tournaments, not just league nights. Five formats out of the box: Single Elimination, Double Elimination, Group Knockout, Round Robin, and Matchplay (grouped qualifying rounds into a finals bracket, IFPA scoring by default). Create one in a couple of screens, seed it, and run it live. Mid-tournament ops are first-class: clear a result, forfeit, swap players, add a latecomer, rebuild a round, and undo any of it. Every format, on every tier, free.
Live brackets anyone can watch. Each tournament gets a public viewer at roundsup.app/<your-league>/t/<tournament>: the live bracket or round grid, updating as results land. Send one link to the group chat and stop fielding "who am I playing next?"
Players report their own results. Group formats and brackets both support self-reporting links. Hand players a link and they submit their own match results from their phones, no accounts. You confirm from the TD view; nothing goes final without you.
Tournament TV and broadcast. Point the venue TV at your tournament and the room sees the live state, big and legible. A broadcast overlay mode is there for streams.
Check-in and RSVP. Schedule a meet ahead of time and players can RSVP from the public page. On the night, a check-in flow at the door replaces the clipboard headcount, and yes-RSVPs pre-fill your attendance list when you build the meet.
Your machine list, from Pinball Map. Link your venue during league setup (or later from the Machines page) and import your lineup straight from Pinball Map. Machines moved in or out? Re-import any time; it never duplicates what you already have.
Your league's look, everywhere. Pro branding is real now: set your accent color and logo in Settings and they carry across your public pages, share cards, and the big screen. The color picker checks contrast for you so your pages stay readable in both themes.
Bring your tournaments with you. Have a finished tournament somewhere else? Export its CSVs and import them here: players (with IFPA numbers), games, and final standings all come across, matched against your existing roster so nobody gets duplicated. No API tokens needed.
A front door worth sending people to. The homepage at roundsup.app is new: a real pitch with a live scoring demo you can try before signing up. And the site now remembers the leagues you visit on your device, so returning players land one tap from their league page, still with no account.
A new face. RoundsUp has its own display typeface across the whole product, paired with tabular numerals in every score grid and standings table so columns line up the way scores should.
Under the hood. A security pass tightened who can write what (rate limiting on all anonymous score entry, stricter database rules, input validation on every route). An accessibility pass landed too: better contrast, states that never rely on color alone, pinch-zoom no longer blocked, and a skip link for keyboard users.
V1.1 - May 24, 2026
Built for league night. What started as "functional but feels like multiple admin pages glued together" is now one coherent cockpit at roundsup.app/<your-league>/admin/meets/<id>. The single screen TDs run league nights from.
One screen, everything in reach. Top bar shows your league name and a meet switcher. Status pills sit where they belong. Live scores stream in a table that flashes new entries red as they arrive. A right rail keeps recent edits, quick actions, audience-view links, and (for VN League) Send-to-Shane within glance distance. Group cards show progress, players, and per-player Mark Out at a glance.
Add players mid-setup, not just to a roster. Quick Add Player on the right rail opens a drawer that adds a player to a specific group in tonight's meet, not just to your org roster. Tab into "Existing Player" to pick from your roster (filtered to people not already in this meet), or "New Player" to create someone fresh. The group picker shows capacity upfront: G2 (3/4) joinable, G4 (4/4 full) disabled, G3 (3/4 ✗ scoring active) disabled when scoring has begun. No 409 surprises after you click Save.
Fix a mistake, undo it. Every score edit now writes to an audit log. The Recent Edits panel in the right rail shows the last 50 edits with a one-click ↶ Undo on each. Click the wrong DQ button? Edit a score wrong? One click reverts the change, points recalculate, and the reversal itself shows up in the feed (so you can un-undo if needed). Edits stay undoable until the meet is marked complete.
Edit a score the right way. The score edit modal now lets you reassign a row to a different player (if the original player attribution was wrong). The dropdown shows only valid swap candidates: players who don't already have a score on that machine. The Save button stays disabled until something actually changed. ESC closes, edits surface in the Recent Edits feed instantly.
Scoring fix for A-vs-Everyone meets. Two pre-existing bugs in the points calculator surfaced during V1.1's wellness pass and shipped fixed: DQ players were getting 0 points instead of sharing the remaining slots (a 4-way DQ now correctly distributes the full point pool); and disqualified A-division players were still triggering bonuses for everyone who "beat" them with a positive score. Both bugs only manifested in A-vs-Everyone meets with at least one DQ. The fix was applied to past meets: Meet #8's scores recomputed cleanly with full audit trail.
Meet list polish. When you don't have an active meet, the "Create New Meet" call to action takes the page. When you do, it shrinks to a small button so the active meet is the main thing on screen. Meet-list rows lose their "Complete" button. Completing a meet now lives in the cockpit only, the source of truth for status changes.
Edit your roster, then jump back. The standalone Players and Machines pages each got an "Open in cockpit ↗" button. One click takes you back to wherever you're running tonight's meet.
Smaller fixes. The RoundsUp wordmark now scales down on mobile so it doesn't overflow on small phones. The auth screens (login, signup, password reset) also got mobile wordmark sizing. The Setup pill on an active or completed meet is now disabled with a tooltip explaining it'll come back in V1.2.
What's coming in V1.2. Three TD asks land in V1.2: a setup-mode editing route so you can adjust groups, players, or machines after creating a meet without deleting and recreating; a clean split between "Step Out" (player paused, scores preserved) and "Disqualify Player" (admin DQ, scores zeroed); and a group-level bulk machine reassign for when scores were entered against the wrong machine.
Behind the scenes. The cockpit's polling layer is now in a useMeetData hook, paving the way for a future Supabase realtime swap-in. Score edits are captured by a single shared backend helper so PUT and Undo always behave identically. The old /papa-view route is deleted (Standings + Summary + Public cover the same surface). The score calculator has its first proper test suite to catch regressions.
V1.0 - May 19, 2026
RoundsUp goes public. A scoring tool for one league becomes a platform any league can be found on. Plus a new name: we're RoundsUp now, not On Tilt.
Find any league at /leagues. The new public directory lists every league running on RoundsUp. Each card shows the league name, location, recurring schedule, and a link to that league's homepage. No login required. Built for the moment someone mentions you in a Discord and a stranger asks "wait, where's that?"
Every league gets a homepage. Visit roundsup.app/<your-league> and you'll see a page built around the league: name, location, tagline, recurring schedule, recent meets, current machine lineup. Updates as meets happen. Share the URL anywhere (Twitter, Discord, iMessage) and the preview card renders with a custom image featuring your league's name (not a generic site preview). This is the page to send people when they ask about your league.
Built to be fast. Every public page is cached close to every visitor. Loads in under a second from anywhere. When 100 curious readers click your league's URL at once, they all see it instantly. We built this assuming it'd someday matter.
Your league's data is yours. Every API endpoint that returns league data verifies the caller is authorized for that specific league. No path exists for one league's admin to see another league's meets, players, machines, or scores, even by passing the right query parameters. Hardened during V1.0, ahead of any second league signing up.
Legacy continuity. The old ontiltleague.com still works exactly as before; vn-league members see zero change.
What's next. V1.1 begins now, focused on the tournament-director experience: distributed scorekeeping, real-time activity on the admin dashboard, edit/undo on scores, and IFPA player rankings. Watch this page or follow the status page.
V0.98 - May 18, 2026
Rebrand to RoundsUp. User-visible platform branding renamed from "On Tilt" to RoundsUp, separating the SaaS product from the OnTilt Pinball venue brand. Text and metadata only, no functional changes.
- Wordmark, page titles, metadata, and platform copy renamed across all public, auth, admin, score-entry, and TV pages
- Slogan "Score it. Send it. Done." kept; appears next to the new RoundsUp wordmark
- TV broadcast and standings print headers now render the org name dynamically (e.g., "VN League") instead of platform brand; these surfaces are about the league, not the platform
- Metadata title set to
RoundsUp · Find pinball leagues - Slug-input preview in
/create-orgswitched fromontiltleague.com/{slug}/adminto path-relative/{slug}/admin - Domain transition:
roundsup.appis the new canonical domain.ontiltleague.comcontinues to work and now renders RoundsUp branding; DNS/Vercel work tracked separately package.jsonandpackage-lock.jsonname fields updated toroundsup-app- Internal identifiers unchanged: CSS variables (
--ontilt-*), CSS classes (tilt-wordmark,ot-red,ot-black, etc.), file/directory names, code symbols, and DB schema (org_id,org_members, etc.) all stay RESERVED_SLUGSadditions from V1.0 work (roundsup,ontilt,on-tilt) protect both old and new brand names from being claimed as org slugs- Historical CHANGELOG entries for V0.85–V0.97 preserved as-is
V0.97 - May 14, 2026
Real auth, members-only meets, multi-tenant cleanup. Replaces the shared admin password with per-user Supabase Auth, adds opt-in members-only meets that require sign-in to enter scores, and closes the multi-tenant scoping gaps left open after V0.96.
Auth:
- Supabase Auth replaces the custom ADMIN_PASSWORD cookie. Legacy cookie still works during the hybrid period; V0.98 will deprecate it.
- Signup with email verification via Resend SMTP, password reset flow, profile page at
/profilefor display name and IFPA edits - Onboarding flow: signup → verify email →
/create-org→ become super_admin of the new org via the atomiccreate_org_with_adminRPC - Logout clears both auths (legacy cookie + Supabase session) and lands on
/login - Display name now syncs from profile to every
org_membersrow on update so admin views stay current OrgRoletype fixed to match the DB CHECK constraint (super_admin | org_admin | scorekeeper | viewer). Pre-V0.97 the code compared against'admin', which doesn't exist as a role; any non-super_admin would have been silently locked out of admin routes. Caught and fixed before a second admin was ever created.
Players / IFPA:
papa_idcolumn dropped; replaced with optionalifpa_idinteger per player per org- All PAPA-facing strings removed from schema, code, and UI. CSV export header now
ifpa_id - "PAPA View" admin link/heading renamed to "Score Mirror". URL path
/papa-view/kept for stability; folder rename deferred to V0.98
Meets / members-only:
- New per-meet
members_onlytoggle on the create-meet form. When ON, only signed-in org members can enter scores; the score URL renders aMEMBERS ONLYwall with a Sign In button for anonymous visitors. Enforcement is at both the API (hard gate) and client UI layers. - Per-group scorekeeper token system removed entirely. Old
/{org}/score/{token}URLs return 404. - Offline submission queue is now auth-aware. Queued scores that hit a 401 stay in localStorage with a
needs_authflag and surface a "N scores need sign-in to submit" banner with a Sign In CTA. Previously they were silently dropped on the first failed retry.
Multi-tenant correctness (V0.96 follow-through):
V0.96 moved all pages under /{org}/... but several enforcement layers were missing. With only one org (vn-league) in production, none of these gaps surfaced; each would have failed the moment a second org was created. This segment closed them:
- All API routes filter by
org_idon reads (19 routes covering meets, scores, group-players, extra-balls, score-table, send-email, papa-view, archive, heartbeat). Cross-org meet access by direct UUID now returns 404 instead of leaking data. app/[org]/meet/[id]/score/page.tsxbrought in line with its three siblings (public/standings/summary): verifies the meet belongs to the URL's org slug- All
revalidatePath()calls now target/{slug}/...paths (~72 calls across 13 routes). Pre-V0.97 these were no-ops against the actual cached page paths - Hardcoded
/admin/...and/meet/...Link targets prefixed with/{org}/...throughout admin UI. Pre-V0.97 they worked only via anext.config.tsredirect hardcoded tovn-league MeetScoreClientreceivesorgas an explicit prop and threads it through every API call and internal Link. Side effect: this is what finally fixed the members-only gate from firing reliably: the broken<Link href="/meet/[id]/standings">was generating an_rsc=h1599404 in the console, and pre-Commit 19 the fetches couldn't return themembers_onlyflag at all because they weren't org-scoped.
Schema:
- Migration 010: drops
players.papa_idandgroups.scorekeeper_token; addsplayers.ifpa_id,meets.members_only,org_members.display_name - Migration 011:
create_org_with_adminSECURITY DEFINER RPC for atomic org + super_admin creation - Row-level security enabled on all 10 tenant tables. Service-role API routes bypass RLS as expected; anonymous score writes are gated by the
can_anon_write_score()SECURITY DEFINER function which checks the meet'smembers_onlyflag.
Bug fixes (incident log):
- Meet creation broken in production for ~1 hour after migration 010 deployed (May 14 morning). Migration 010 dropped
papa_idandscorekeeper_tokencolumns but the meet-creation API still attempted to insert them. The first admin who tried to create a meet got a 500. Hotfixed same morning in commitc956a37. - Send-email route broken silently for the same period.
/api/admin/meets/[id]/send-emailselectedscorekeeper_tokenin its Postgres query. After migration 010 dropped that column, the query errored. Anyone trying to email meet results would have gotten a failure. Probably nobody used it during the window. Fixed in this segment (Commit 13 of the segment, after the original feature work landed). - Heartbeat endpoint quietly read a non-existent column for months.
/api/meets/[id]/heartbeatselectedmeets.updated_at, which has never existed on themeetstable, only onscores. Postgres silently returned the row without the field, leavingmeet.updated_atasundefined. Thelast_updatedcalculation accidentally returned correct values via null-coalescing on the latest score timestamp, so nothing user-visible broke. Cleaned up in Commit 18 of this segment: select is now accurate, calculation simplified to use the score timestamp directly. retryQueue401 data-loss bug. Prior implementation treated any 4xx response (including 401members_only) as a permanent client error and silently dropped the entry from localStorage. A user whose session expired mid-night with queued offline scores would have lost them. Fixed to preserveneeds_authentries and surface a sign-in banner (see Meets section).
Known issues (deferred to V0.98):
- Admin "list all" GET endpoints still return cross-org data.
/api/admin/meets,/api/admin/players,/api/admin/machines,/api/admin/attendancewere not in this segment's scope. Severity low today (single org; admin-facing only) but must-fix before any second org signs up. extra_ballstable has noorg_idcolumn. Migration 009 didn't add it. The three extra-ball API routes currently defend via the parent meet'sorg_id: functionally sufficient, but adding the column directly would provide defense-in-depth.- ADMIN_PASSWORD deprecation. Hybrid auth period continues. V0.98 drops the legacy code path once real Supabase Auth signup is verified end-to-end on production.
/papa-view/folder rename. UI strings already say "Score Mirror"; URL path follows in V0.98.
Breaking changes:
- Old scorekeeper token URLs (
/{org}/score/{token}) return 404 players.papa_idcolumn dropped; external integrations depending on it must migrate toplayers.ifpa_id- Email CSV export header changed from
papa_idtoifpa_id - Cross-org meet access by direct UUID now returns 404 (was previously allowing access; no real-world impact since only one org existed)
V0.96 - May 13, 2026
Multi-tenant foundation. The app now supports multiple leagues sharing one database, each scoped to their own org.
- Path-based org routing: all pages now live under
/{org}/...(e.g./vn-league/leaguenight,/vn-league/admin) - Legacy redirects:
/leaguenight,/tv,/score,/adminpermanently redirect to/vn-league/...; Yodeck TV URLs remain functional - Org validation layout: invalid slug format and unknown orgs return 404; inactive orgs show a holding page
- OrgProvider context: org slug available to all client components via
useOrg()hook org_idcolumn added to 8 tables (meets, groups, players, machines, scores, attendance, group_players, group_machines) with NOT NULL constraint; existing rows backfilled tovn-leagueorg- All INSERT/UPSERT operations now set
org_id; admin routes acceptorgSlugin body and look up the org - All server-side reads now filter by
org_id: leaguenight, TV, meet pages, and admin dashboard are fully scoped
V0.95 - May 9, 2026
Post-launch polish day. Five fixes shipped within 24 hours of going live.
- Flexible roster editing on Assign Groups screen. Admin can add or remove players at any step without navigating back. Removed players are evicted from group slots automatically.
shortName()helper rewritten to parse the singlenamecolumn. Last initials now show on/leaguenight, standings,/tv, PAPA view, score entry, and confirmation overlay.- Score popover on standings replaced with bottom-sheet modal. Full comma-formatted scores no longer clipped on mobile.
- EB (Extra Ball) indicator now shows a red dot next to player names across all games in a meet, persistent visual reminder for the scorekeeper.
- EB persistence migrated to per-game
scores.extra_ballboolean column (migration 008). State now survives navigation, reload, and reconnect. - TV flash made longer and more aggressive (10s, 3 pulses, hard border outline) for better visibility on venue TVs.
V0.90 - May 7, 2026
Live launch. Real users, real scores, zero downtime.
- 30+ players attended at AYCE Gogi Van Nuys
- First score: Moriah, 770,130 on High Speed
- Beta framing: "Hey guys, we're beta testing this new software I built. Let's double-do our scores. Give me bugs, give me feedback."
- Result: Everything logged in, no downtime, no failures, all groups completed, winner screen displayed correctly
View early development history
V0.85 - May 7, 2026 (pre-launch)
Pre-launch hardening pass.
- DQ scoring fix: DQ = 0 points everywhere (was incorrectly receiving last-place points)
- A vs Everyone bonus on DQ'd A player corrected
- PAPA mirror view added for Shane's score entry workflow
- 4th-player row clipping in PAPA view fixed
- Double-submit guard on score submission
- Negative score validation
- Connection timer
- Error state clearing
- Admin route guards
- Custom domain
ontiltleague.comconfigured (apex A record to 76.76.21.21 in Cloudflare, www CNAME via Vercel) - Apex domain redirects to
/leaguenight - Admin login redirect bug fixed (hard navigation after cookie set)
- Cache invalidation for player-dependent pages on PATCH
- Machine pick confirmation modal + edit machine before scoring
V0.80 - May 6, 2026
Bulletproof score submission released.
- Full-screen confirmation overlay with raw scores prominent
- localStorage drafts auto-save every 500ms
- Offline queue with auto-retry on connection recovery
- 3-tier connection recovery (silent reconnect → yellow banner → red TAP TO REFRESH)
- Raw scores shown in submission overlay for verification
- Connection recovery hook integrated
V0.70 - May 5, 2026
A vs Everyone tournament rule and TV broadcast hardened.
- A vs Everyone rule shipped (+1 bonus to non-A players who beat the A player on raw score)
- DQ/OUT points calculation corrected
- Group completion gate added
- Scroll-to-top fix on game change
- Extended TV flash duration
- Admin meet copy raw score only
- Status transition fixes (open → active → complete → submitted)
- Latest 20 scores by default with expand button on admin score table
- Past meets archive view added
- Select All on attendance picker
V0.60 - May 4, 2026
Brand and design pass.
- Renamed from TILT to On Tilt
- Last Game Spotlight added on
/tv - Card layout overlap fix
- Spotlight replaced with slim ticker for better screen real estate
- TV layout supports up to 14 groups (5×3 grid)
V0.50 - May 3, 2026
TV broadcast mode for Yodeck.
/tvview added with all groups, scores, current machine- TV display mode for Yodeck kiosk (no chrome, full screen)
- Smart 60s heartbeat for connection health
- 10-minute hard reload safety net (Yodeck-specific)
- Dual-layer refresh strategy for WebSocket recovery
V0.30 - May 3, 2026
Admin and machine management.
- Admin dashboard with live score table
- Machine active/inactive toggle
- Machine delete
- Status management UI
V0.10 - May 5, 2026 (initial commit retroactively dated to project start)
Initial scaffolding.
- Next.js 15 App Router setup
- Supabase integration (Postgres + auth)
- Score entry skeleton
- Group and meet data model
- Vercel deployment