Changelog
Everything we've shipped on RoundsUp, in order.
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