OpenRally

Tournament operations for Asia-Pacific racket sports

“Give tournament organizers, clubs, and players across Asia-Pacific a single platform for running and playing in serious padel tournaments — with feder”

Versions

9 shipped · 0 in progress · path to v1.0 General Availability.

v0.1.0Shipped2026-04-25

Foundation

Next.js + Supabase scaffold, magic-link auth, RLS baseline, realtime architecture spike.

  • Next.js 15 + Tailwind 4 + shadcn/ui scaffold
  • Supabase project + first migration
  • RLS baseline + policy testing harness
  • Auth: email magic link only
  • i18n harness (TH + EN)
  • Design system tokens from `openrally-design-system.md`
  • **Realtime spike:** Supabase Realtime POC — broadcast/subscribe + payload size sanity check

Done when: Realtime POC validates 2-client round-trip <500ms; magic-link login + RLS gates on protected tables both work.

v0.2.0Shipped2026-05-07

Player Identity

Magic-link auth, email infrastructure, player profile, authenticated home. **Scope amendment (2026-04-28):** Google + Facebook OAuth deferred to v0.2.1+ alongside Apple (gate D2 expanded to all three social providers). v0.2.0 ships with magic-link only; the social buttons are env-var-gated (`NEXT_PUBLIC_OAUTH_*_ENABLED`) and stay off until v0.2.1.

Player
  • Magic-link sign-in (email-only)
  • [⏭️] Social sign-in: Google + Facebook + Apple — **deferred to v0.2.1+** (code shipped + button shells in place; providers not yet configured)
  • **Email infrastructure:** Resend wrapper + bilingual welcome template + send-welcome-email Edge Function (idempotent + rate-limited) + Resend webhook receiver (Svix-verified). openrally-web W4 server action invokes via `supabase.functions.invoke`. Activation gated on Pap creating Resend account + domain DKIM/SPF.
  • Player profile: name, DOB, gender, city/country, phone/LINE/WhatsApp, hand/side
  • `/me/settings` + `/me/registrations` (empty state)
  • Player home `/`
  • Cross-cutting DoD: a11y axe-verified clean on public routes (4.10.2, 0 violations); Sentry runtime + `withSentryConfig` build wrapper wired (env-var-gated); audit log emits `profile.*` + `email.*` rows; privacy notice + consent UI on `/onboard`.
  • [⚠️] Native-Thai i18n review pending — checklist ready at `Plans/v02-th-i18n-review.md`.

Done when: Magic-link sign-in works on prod and a transactional welcome email sends from the prod-configured domain. **Ship-blocker (2026-04-28):** Pap-side prereqs — Resend account + DKIM/SPF on `openrally.padelthailand.com` (P4-P6), Vercel deploy (P7). Domain (D1) resolved → `openrally.padelthailand.com`.

v0.3.0Shipped2026-04-28

Organizer + Venues + Admin

TO onboarding, admin approval, venue management, draft tournament creation.

OrganizerVenueAdmin
  • TO application flow (`/organizer/apply`, `/organizer/apply/status`)
  • Admin dashboard + organizer applications list + detail (`/admin`, `/admin/organizer-applications`)
  • Venue create/select (name, city, court count + names, address)
  • Tournament create as `draft`: name, dates, venue, draw size, last-set rule
  • Organizer dashboard + tournament management hub
  • Organizer public profile `/organizer/[slug]`

Done when: A player applies → admin approves → approved TO creates a venue + draft tournament invisible to the public.

v0.4.0Shipped2026-04-28

Registration

Solo + doubles registration with partner matching and waitlist.

PlayerOrganizer
  • Tournament lifecycle: `draft → registration_open → registration_closed`
  • Solo registration
  • Doubles registration + partner search modal + invite tokens
  • `/invite/[token]` accept/decline flow
  • TO registrations tab: auto-accept, waitlist, waitlist promotion email
  • Withdrawal + add/remove partner

Done when: Doubles team registers via partner-invite token; waitlist promotion email fires when a slot opens.

v0.5.0Shipped2026-04-28

Draw Engine + Public Bracket

Single-elim bracket generation with manual seeding, byes, and read-only public view.

OrganizerSpectator
  • Bracket sizing (4–128, top seeds get byes)
  • Manual drag-drop seeding UI
  • Bye placement algorithm
  • Draw as persistent document (decoupled from tournament state)
  • Publish draw → tournament `published`
  • **Public read-only bracket view** at `/tournaments/[organizer-slug]/[tournament-slug]/bracket` (D11 vanity URL; UUID-fallback redirects to canonical slug; page refresh; realtime arrives v0.8)

Done when: A published bracket renders correctly to an unauthenticated viewer for both power-of-2 and non-power-of-2 draw sizes. **Phase A (B36-B47) + Phase B (W45-W54) shipped 2026-04-28; Phase C ship gate is DoD2 walkthrough by Pap (15 sub-checks: a-o).**

v0.6.0Shipped2026-06-22

Live Scoring

TO score entry, bracket cascade, retirement, score-edit undo.

Organizer
  • Per-set score entry (best-of-1 / best-of-3)
  • Standard set + tiebreak + super tiebreak validation
  • Retirement (partial scores → opponent advances)
  • Bracket cascade: winner auto-advances
  • Cascading undo for score edits + walkover undo
  • Tournament `live` state on first match start

Done when: A mock 8-team tournament is scored end-to-end with cascade, retirement, and undo all passing E2E tests.

v0.7.0Shipped2026-06-22

Scheduling

Court × time grid scheduling with auto-schedule and conflict detection.

Organizer
  • Court × time grid (15-min increments)
  • Per-round duration config
  • Auto-schedule algorithm + drag-drop manual adjustments
  • Court availability blocking
  • Conflict detection (player double-book, bracket dependency)
  • Multi-day support

Done when: Auto-schedule produces a conflict-free schedule for a representative 16-team multi-day tournament.

v0.8.0Shipped2026-06-22

Realtime + Spectator

Supabase Realtime bracket updates and TV-friendly spectator mode.

SpectatorPlayer
  • Realtime channel design + broadcast triggers
  • Live bracket view (~100 concurrent viewers)
  • Match status indicators (upcoming / in progress / completed)
  • Spectator mode `?spectator=true` (hides nav, enlarges bracket, TV-ready)
  • Presence / viewer count

Done when: 100 concurrent simulated viewers receive score updates within 1 second.

v0.9.0Shipped2026-06-22

Placements + Polish

Placements, cancellation, social sharing, pre-launch polish.

OrganizerPlayer
  • Placements auto-derived (1st/2nd/optional 3rd-place match)
  • Manual placement override (audit-logged)
  • Tournament cancellation flow (notify + void registrations)
  • OpenGraph rich previews for LINE/WhatsApp sharing
  • Email template inventory complete
  • [⏭️] ~~Native-Thai i18n review~~ — **DROPPED 2026-05-08:** v1 ships English-only per Pap decision; Thai (and other locales) deferred to post-v1. See `~/.claude/MEMORY/projects/-Users-preedee-Desktop-Cowork/project_openrally_v1_english_only.md`. The bilingual scaffolding remains in code (next-intl + message bundles) so re-enabling Thai post-v1 is a one-line restore in `routing.ts`.

Done when: End-to-end mock tournament finishes with correct placements and a shared link renders an OG preview on LINE.

v1.0.0PlannedQ2 '26

General Availability

Production-hardened, first real Thailand padel tournament runs on OpenRally. **Scope amendments (2026-06-25, audit-first v1.0 kickoff):**

OrganizerPlayerSpectatorVenue
  • **ISC-61 amended → English-only.** Per the 2026-05-08 Pap decision (see v0.9 note + DECISIONS 2026-06-25), v1 ships EN-only. ISC-61's "TH + EN" is hereby read as "EN" for v1.0 GA. The bilingual scaffolding (next-intl + message bundles) stays in code so re-enabling Thai post-v1 is a one-line `routing.ts` restore — ISC-62 (zero-code locale add) remains a live requirement.
  • **ISC-01b (social OAuth) activation deferred → v1.1.** The Google/Facebook/Apple sign-in **code is implemented and shipped** (env-gated via `NEXT_PUBLIC_OAUTH_*_ENABLED`); only provider-credential configuration is outstanding. v1.0 GA ships magic-link as the live auth path; OAuth flips on in v1.1 once provider apps are configured. Not a GA blocker.
  • Performance pass (Lighthouse budgets met) — ISC-66/67; needs staging RUM
  • Accessibility audit (WCAG AA) — ISC-69; baseline AA verified 2026-06-25, reduced-motion guard added
  • Security review (secrets, RLS coverage, audit log completeness) — ISC-63; clean as of 2026-06-25 audit
  • All v1 ISC criteria from build-prompt §15 met (as amended above) — ISC-58/59 "My next match" card shipped 2026-06-25
  • Pap on-site for first tournament

Done when: A real Thailand padel tournament runs end-to-end on OpenRally without engineering intervention.

Documents

Specification files powering OpenRally's product definition.

Spec files

8 docs
Build PromptDraft v0.5

openrally-build-prompt.md

The master specification — mission, scope, tech stack, architecture, milestones, screen specs, ISC acceptance criteria, and anti-requirements.

94 KBUpdated 2026-10-20
v1 SpecsDraft v0.5

openrally-v1-detailed-specs.md

Detailed v1 specifications — tournament lifecycle, match lifecycle, registration, draw generation, scoring, Realtime architecture, scheduling, Edge Functions, emails, and navigation.

41 KBUpdated 2026-10-20
v2 ReferenceDraft

openrally-v2-v9-reference.md

Deferred v2 features — referee roles, federation sanctioning, payments (Stripe + Omise for Thailand), rating integration.

13 KBUpdated 2026-10-20
AnalysisComplete

openrally-challonge-analysis.md

How TPS currently uses Challonge — every API operation mapped so OpenRally can replace it natively.

9 KBUpdated 2026-10-20
Design SystemDraft

openrally-design-system.md

Visual language — color palette (light + dark), Inter typography scale, status badges, component patterns (shadcn/ui), bracket and scheduling styling.

8 KBUpdated 2026-10-20
SchemaDraft

openrally-database-schema.sql

Postgres DDL — 14 enums, 16 active v1 tables, 6 v2-v9 reserved tables, RLS policies, helper functions. Source of truth for the data model.

36 KBUpdated 2026-10-20
Version RoadmapDraft

version-roadmap.md

Per-version decomposition of the path to v1.0 — 10 minor releases (v0.1.0 Foundation → v1.0.0 GA), each with theme, description, feature list, and target quarter. Powers the Versions section.

10 KBUpdated 2026-10-20
Decisions LogLiving

decisions.md

Living log of roadmap planning decisions — sequence changes, scope trims, architectural pivots, risk calls. Most recent first.

25 KBUpdated 2026-10-20

Features

Feature catalog by persona with v1/v2 scope designations.

4 Differentiator Pillars

1

Deep rating integration

v1 Not in v1

v3 Rating integration with one external provider — profiles, brackets, auto-seeding, per-match impact, leaderboard

2

Federation Support

v1 Not in v1

v7 Sanctioning UI, official tournament approval workflows, federation ranking management, referee mode

3

Tournament Formats

v1 Single Elimination

v2 Double Elimination

v4 Round Robin, Groups + Knockout

4

Tournament Configuration

v1 TO-mode live scoring, per-set scores, realtime bracket, auto-derived placements, spectator mode

v2 Player score entry, dispute flow

v4 Scoring configs, registration approval

v7 Referee mode

Product Roadmap

9 versions
VersionThemeKey Features
v1MVP — Thailand single-elim padelTO live scoring, registration + draws, realtime bracket (Supabase), social auth + magic link, spectator mode, TH + EN, no payments
v2Complete the live experiencePlayer score entry, dispute flow, push notifications, double elim, match history, TV display mode
v3Ratings + platform maturityRating integration, historical tournament import, TPS APIs
v4Formats + configRound robin, groups+KO, scoring configs, registration approval, personal records, sponsorship
v5MonetizePayments, refunds, TO payouts
v6MessagingWhatsApp + LINE notifications
v7FederationsSanctioning UI, ranking management, referee mode
v8Multi-sportTennis, pickleball, badminton, squash
v9IndonesiaBahasa Indonesia, GoPay/OVO/Dana, Telegram

Feature Catalog

Player Features

13 v1
IDFeatureScope
PL-V1-01Player profile created on signupv1
PL-V1-02Browse tournament listv1
PL-V1-03Register for a tournament solov1
PL-V1-04Register for doubles by searching OpenRally usersv1
PL-V1-04bAdd partner laterv1
PL-V1-04cRemove partnerv1
PL-V1-05View live single-elim bracketv1
PL-V1-06View "my next match" card during a tournamentv1
PL-V1-07View final placementsv1
PL-V1-08Spectator modev1
PL-V1-09Social sharingv1
PL-V1-10Waitlist promotion notificationv1
PL-V1-11Withdrawalv1

Tournament Organizer Features

12 v1
IDFeatureScope
TO-V1-01Create a tournamentv1
TO-V1-02Open / close registration windowv1
TO-V1-03Manage entry listv1
TO-V1-04Seed players manuallyv1
TO-V1-05Generate draw from seedsv1
TO-V1-06Schedule matches to courts and time slotsv1
TO-V1-07Regenerate the draw if registrations change before publishv1
TO-V1-08Publish the drawv1
TO-V1-09Start tournamentv1
TO-V1-10Enter live scoresv1
TO-V1-11Walkover / withdrawal / retirementv1
TO-V1-12Cancel tournamentv1

Venue Features

3 v1
IDFeatureScope
CV-V1-01When creating a tournament, the TO selects an existing venue or creates a new one inline (name, city/location, court count, optional address)v1
CV-V1-02Tournament detail page shows the venue's name, location, and court countv1
CV-V1-03A simple "Venues" page lists all venues that have OpenRally tournaments, each linking to the list of that venue's tournamentsv1

Detailed Specs

v113 sections
#Section
1Tournament Lifecycle State Machine
2Match Lifecycle State Machine
3Registration Flows
4Draw Generation Algorithm
5Scoring Validation Rules
6Realtime Architecture
7Match Scheduling
8Edge Functions Inventory
9Email Notification Triggers
10Organizer Dashboard
11Landing Page
12Admin Panel
13Navigation Map

Competitive Landscape

5 competitors analyzed
ProductCategoryPlatformVerdict
WeCourtsWeCourts
Rating-first padel platformWeb + iOS + AndroidStrong in MENA. Rating (WPPR) is the moat; organizer tooling is thin. Geo-blocks non-MENA IPs.
PadelutionPadelution
Multi-format padel tournament toolWeb + iOS + Android + macOSFinnish, launched June 2025, ~2K downloads, free, no monetization disclosed. Bracket-only "Quick Tournaments"; Americano lives in a separate heavier Events/Leagues module. Not in the "instant Americano" space.
Americano Padel AppAmericano Padel App
Americano-specialized tournament appiOS + Android (Flutter) + Flutter Web spectatorStrongest head-to-head competitor. 75+ countries, 4.9 rating, player-freemium + TO subscription. Mobile-first — no web TO dashboard. 6 format variants at launch. Anonymous device-scoped auth. TV display link is premium-gated Flutter Web canvas (unreliable on venue TVs).
Playtomic TournamentsPlaytomic Tournaments
Booking-first padel super-app's tournament moduleMobile + webIndustry leader for padel booking. Tournament module is secondary and thin. Weak in Asia.
BracketMaker / americanopadel.app / Padelpuffin / Padel Point / PadelMix
Niche Americano toolsMixLong tail of small tools. Mostly PDF generators or watch companions.

Design System

Visual language — colors, typography, status badges. Source: openrally-design-system.md

Color Tokens

12 tokens
TokenLightDarkUsage
primary
#4285F4
#4285F4
CTAs, active states, links
primary-hover
#3367D6
#5A9BFF
Button hover
primary-foreground
#FFFFFF
#FFFFFF
Text on primary buttons
background
#FFFFFF
#0F0F0F
Page background
card
#FFFFFF
#1A1A1A
Card surfaces
muted
#F5F5F5
#262626
Secondary surfaces, empty states
border
#E5E5E5
#333333
Dividers, card borders
foreground
#1A1A1A
#FAFAFA
Primary text
muted-foreground
#737373
#A3A3A3
Secondary text, labels
destructive
#DC2626
#EF4444
Cancel, delete, errors
success
#16A34A
#22C55E
Confirmed, completed
warning
#F59E0B
#FBBF24
Waitlisted, conflicts

Typography

7 levels
Display
The quick brown fox
H1
The quick brown fox
H2
The quick brown fox
H3
The quick brown fox
Body
The quick brown fox
Body small
The quick brown fox
Caption
The quick brown fox

Status Badges

13 states
DraftRegistration OpenRegistration ClosedPublishedLiveCompletedCancelledUpcoming (match)In Progress (match)Completed (match)WalkoverRetiredBye

Database Schema

Postgres DDL — 14 enums, 16 active tables, 6 reserved. Source: openrally-database-schema.sql

v1 Active Tables

16 tables
TableDescription
sportSport (first-class dimension, not a constant)
userUser (extends Supabase auth.users)
venueVenue (OpenRally-native in v1, no TPS dependency)
tournamentTournament
round_durationRound Duration (configurable match duration per round)
court_availabilityCourt Availability (blocked time ranges per court)
teamTeam (for doubles pairing; solo players also have a team of 1)
team_memberTeam (for doubles pairing; solo players also have a team of 1)
registrationRegistration
partner_invitePartner Invite
drawDraw
draw_seedDraw Seed (persistent, auto-saved per drag)
matchMatch
placementPlacement (auto-derived, TO can override)
organizer_applicationOrganizer Application
audit_logAudit Log (immutable)

v2-v9 Reserved Tables

6 reserved

These tables exist in the v1 schema but stay empty until their target version. Reserving the shape now avoids a migration scramble later.

TableDescription
RESERVEDrating_providerRating Provider (v3)
RESERVEDplayer_provider_linkPlayer Provider Link (v3 — links OpenRally user to external rating provider)
RESERVEDrating_pushRating Push (v3 — outbound match result push to rating provider)
RESERVEDsanctioningSanctioning (v7 — federation tournament link)
RESERVEDpaymentPayment (v5 — entry fee transactions)
RESERVEDscoreScore (v2 — richer polymorphic per-sport scoring)

Enums

14 enums
gender_typeplaying_handpreferred_sidetournament_statustournament_formatmatch_formatlast_set_rulematch_statusmatch_typeregistration_statusregistration_typeorganizer_application_statusdraw_statusaudit_action

Tech Stack

v1 technology choices and architecture overview.

Show stack details

v1 Stack

Web appNext.js (App Router) + TypeScript + shadcn/ui + Tailwind
BackendSupabase (PostgreSQL + Edge Functions in TypeScript/Deno + Storage + Realtime)
**Auth**Supabase Auth (email + magic link + Facebook + Google + Apple OAuth)
Bracket rendering`@g-loot/react-tournament-brackets` or equivalent off-the-shelf library
VenuesOpenRally-native — TO creates venues in OpenRally directly
Player profileOpenRally-native — basic profile created on signup
Partner searchOpenRally-native — search registered OpenRally users
State (Next.js)Zustand
Feature flagsPostHog
Issue trackingLinear (new `MD-` project)
CI/CDGitHub Actions
MonitoringSentry + PostHog events
EmailResend
Scheduling UI`dnd-kit`

Domain Model (v1 Schema)

All entities exist in v1 schema per the full-backlog architecture principle.

ACTIVE User — platform account. Includes nullable `gender` (enum `male`/`female`/`other`/`prefer_not_to_say`) — reserved for v2 mixed-format pairing constraints.

ACTIVE PlayerProfile — padel-specific profile on top of User (playing_hand, preferred_side, years_playing, home_club_id)

RESERVED RatingProvider — config row stub, empty in v1

RESERVED PlayerProviderLink — `(player_id, provider_slug, provider_player_id, ...)` — empty in v1

ACTIVE Venue — OpenRally-native venue (name, city, court_count, address)

ACTIVE Tournament — top-level event. Format is `single_elim` in v1. Includes `match_format` enum (`best_of_1` / `best_of_3`), `last_set_rule` enum (`full_set` / `tiebreak` / `super_tiebreak`), `has_third_place_match` boolean. Schema can extend with new format values in v2.

ACTIVE Event — a draw/category inside a tournament (e.g., "Men's Open", "Mixed 4.0")

ACTIVE Registration — player/team → event

ACTIVE Team — for doubles events

ACTIVE Draw — bracket structure for an event

ACTIVE Match — a single match in a draw. Key columns:

RESERVED Score — richer polymorphic score table, empty in v1, reserved for v2 detailed per-sport/per-format scoring

RESERVED RatingPush — outbound result push record — empty in v1

RESERVED Sanctioning — optional federation link for a tournament — empty in v1

RESERVED Payment — entry fee transaction — empty in v1

Screens

23 screens in v1 across 4 persona groups.

23 screens
#ScreenPathPersona
1Landing page/ (unauth)Public
2Sign-in/loginPublic
3Tournament detail (public)/tournaments/[id]Public
4Player home / dashboard/ (auth)Player
5Tournament list/tournamentsPlayer
6Tournament detail (auth, with register CTA)/tournaments/[id]Player
7Partner search modal(modal from tournament detail)Player
8Partner invite accept/invite/[token]Player
9My registrations/me/registrationsPlayer
10Settings/me/settingsPlayer
11Organizer dashboard/organizerTO
12Create tournament/organizer/tournaments/newTO
13Tournament management hub/organizer/tournaments/[id]TO
14Registrations tab/organizer/tournaments/[id]/registrationsTO
15Draw tab (seeding + generation + publish)/organizer/tournaments/[id]/drawTO
16Schedule tab (court × time grid)/organizer/tournaments/[id]/scheduleTO
17Placements tab (post-tournament)/organizer/tournaments/[id]/placementsTO
18Organizer public profile/organizer/[slug]Player
19Apply to be an organizer/organizer/applyPlayer
20My application status/organizer/apply/statusPlayer
21Admin dashboard/adminAdmin
22Organizer applications list/admin/organizer-applicationsAdmin
23Organizer application detail/admin/organizer-applications/[id]Admin

Acceptance Criteria

v1 ISC — 82+ atomic, binary-testable criteria.

Auth + Identity

5 criteria

☐ ISC-01: User can sign up and sign in via Supabase Auth email + magic link

☐ ISC-02: Successful auth creates or updates a local `User` row keyed by Supabase `auth.uid`

☐ ISC-03: On first login, user sees a profile completion form (display name, playing hand, preferred side) and data is stored in `User` table

☐ ISC-04: User signs out cleanly; subsequent requests return 401 until re-authenticated

☐ ISC-05: `User.roles` defaults to `['player']` for every new user

TO Onboarding

17 criteria

☐ ISC-08: `/organizer/apply` page is accessible to any authenticated user and presents the TO application form

☐ ISC-09: Application form requires LINE ID, logo upload, and ToS agreement checkbox before submission

☐ ISC-10: Logo upload enforces 500×500 pixel output via a client-side square-aspect-ratio cropper

☐ ISC-11: Logo upload accepts PNG or JPG, rejects other formats, and caps upload size at 2 MB

☐ ISC-12: Uploaded logos land in the Supabase Storage `organizer-logos` bucket with RLS restricting writes to the uploading user

☐ ISC-13: Submitting an application creates an `OrganizerApplication` row with `status = 'submitted'` and stores optional fields (phone, WhatsApp, Instagram, website, Facebook, other social, reference) in `form_data` jsonb

☐ ISC-14: On submission, a "Application received" email is sent to the applicant via Resend in their active locale (TH or EN)

☐ ISC-15: `/admin/organizer-applications` is accessible only to users with the `admin` role

☐ ISC-16: Admin review page lists applications in a paginated list, newest first, with name + submitted date + status

☐ ISC-17: Admin can open an application detail view and see all form responses

☐ ISC-18: Admin Approve action transactionally adds `organizer` to `User.roles` and sets application status to `approved`

☐ ISC-19: Admin Approve action triggers the "Application approved" email via Resend with a link to create a first tournament

☐ ISC-20: Admin Reject action requires a non-empty reason text and sets application status to `rejected`

☐ ISC-21: Admin Reject action triggers the "Application rejected" email via Resend including the admin's reason

☐ ISC-22: Every Approve or Reject action writes an audit log row with user, timestamp, and the action taken

☐ ISC-23: A rejected applicant can reapply immediately (no cooldown in v1)

☐ ISC-24: Users with the `organizer` role see the "Create Tournament" CTA on their dashboard; users without it do not

Venues + Partner Search

4 criteria

☐ ISC-25: TO can create a new venue inline during tournament creation (name, city, court count, court names, address)

☐ ISC-26: Venues are reusable — TO can select a previously created venue when creating a new tournament

☐ ISC-27: OpenRally user search is called with a 300 ms debounce from the partner search UI

☐ ISC-28: User search is rate-limited to 30 requests per minute per user

Tournament Creation + Registration

18 criteria

☐ ISC-31: Organizer creates a tournament with fields: name, dates, venue, draw size, match format (best-of-1 / best-of-3), last-set scoring rule (full set / tiebreak / super tiebreak), 3rd-place match toggle, free-text level band, free-text entry info

☐ ISC-31a: New tournaments are created in `draft` status and are visible ONLY to the creating TO and OpenRally admins

☐ ISC-31b: Draft tournaments do NOT appear in the public tournament list or in any player's registration flow

☐ ISC-31c: TO transitions a tournament from `draft` to `registration_open` via an explicit "Publish tournament" action on the management hub or the dashboard draft card

☐ ISC-31d: Draw size input is a hybrid button group (`4 / 8 / 16 / 32 / 64`) plus a "Custom" option that reveals a numeric input

☐ ISC-32: Tournament format is `single_elim` only in v1 (no format picker needed — hardcoded)

☐ ISC-33: Organizer opens and closes the registration window via explicit actions

☐ ISC-34: Player browses the tournament list showing date, venue, format, and registration status

☐ ISC-35: Player registers solo with one click; registration auto-accepts until draw capacity is reached

☐ ISC-36: Player registers for doubles by searching OpenRally users and selecting a partner

☐ ISC-37: Partner receives an invite email via Resend with a magic-link confirmation URL

☐ ISC-38: Partner clicks the link, signs in via Supabase Auth (account auto-provisioned if needed), and sees an accept/decline prompt

☐ ISC-39: Partner accept transitions the doubles registration to confirmed; partner decline rolls back the solo half

☐ ISC-40: Registrations beyond draw capacity auto-transition to `waitlist` status

☐ ISC-41: Organizer can manually remove a registration or promote a waitlisted registration

☐ ISC-42: Solo registrants and confirmed doubles pairs receive a registration confirmation email via Resend

☐ ISC-42b: Waitlisted player receives an email notification when promoted to confirmed

☐ ISC-42c: Tournament pages have OpenGraph meta tags for rich link previews when shared on LINE/WhatsApp

Draw Generation + Bracket View + Scheduling

11 criteria

☐ ISC-43: Organizer manually seeds single-elim players via drag-and-drop into seed slots

☐ ISC-44: Single-elim generator produces a bracket with correct bye placement — top seeds automatically receive first-round byes for non-power-of-2 draw sizes

☐ ISC-45: (Removed)

☐ ISC-46: Bracket view renders via `@g-loot/react-tournament-brackets` or an equivalent off-the-shelf library (not hand-rolled)

☐ ISC-47: (Removed)

☐ ISC-48: Organizer schedules each match to a specific court + time window

☐ ISC-49: Organizer regenerates the draw before publish without a permanent record of the discarded version

☐ ISC-50: Organizer publishes the draw via an explicit action; the draw becomes visible to registered players

☐ ISC-50b: "Draw published" email is sent to all registered players when the TO publishes the draw

☐ ISC-51: Post-publish edits (player swaps, withdrawals, schedule changes) are permitted until the tournament start date

☐ ISC-52: Every post-publish edit writes an audit log row with user, timestamp, and before/after snapshot

Live Scoring + Bracket Progression

11 criteria

☐ ISC-53: TO can start a tournament, transitioning it to `live` state

☐ ISC-53b: Match status transitions: `upcoming` → `in_progress` → `completed`

☐ ISC-53c: TO can enter per-set scores for any match (e.g. 6-4, 7-5, 10-8)

☐ ISC-53d: Last set scoring follows the tournament's configured rule (full set / tiebreak / super tiebreak)

☐ ISC-53e: Score validation enforces valid padel/tennis scoring rules

☐ ISC-53f: On match completion, winner auto-advances to the next bracket slot via Edge Function

☐ ISC-53g: Supabase Realtime broadcasts bracket updates on channel `tournament:{id}:bracket`

☐ ISC-53h: All connected clients see bracket updates within 1s p95 under 100 concurrent viewers

☐ ISC-53i: TO can mark a walkover (no scores, opponent advances) or retirement (partial scores recorded, opponent advances)

☐ ISC-53j: Tournament auto-completes when the final match is scored

☐ ISC-53k: Every score entry/edit writes an audit log row

Placements + Spectator

6 criteria

☐ ISC-54: Placements auto-derived from bracket (1st = winner, 2nd = finalist, 3rd/4th = semi-finalists)

☐ ISC-55: TO can manually override auto-derived placements (audit-logged)

☐ ISC-56: Players see final placements on the public tournament page after the event

☐ ISC-57: Spectator mode (`?spectator=true`) hides navigation, enlarges bracket, auto-refreshes via Realtime

☐ ISC-58: "My next match" card is visible on the player's tournament page showing court, time, opponent, round

☐ ISC-59: "My next match" updates when bracket progresses (previous-round opponent decided)

Tournament Cancellation

1 criteria

☐ ISC-60: TO can cancel a tournament — all registered players receive cancellation email, registrations voided

Non-functional

9 criteria

☐ ISC-62: Adding a new locale requires only a new JSON bundle + a locale registration row, zero code changes elsewhere

☐ ISC-63: Every user-scoped DB table has RLS policies enforced and covered by at least one integration test

☐ ISC-64: Next.js lint and `tsc --noEmit` report zero warnings in CI

☐ ISC-65: Deno lint reports zero warnings on Edge Functions in CI

☐ ISC-66: Bracket view renders in <300 ms p95 on a mid-range phone browser

☐ ISC-67: Web LCP is <2.5 s p95 on 4G

☐ ISC-68: Realtime bracket updates propagate to all subscribed clients within 1s p95 under 100 concurrent viewers

☐ ISC-69: WCAG 2.1 AA accessibility smoke test passes on the registration flow, bracket view, admin review page, and TO application form

☐ ISC-70: Full end-to-end simulation (sign up → TO apply → admin approve → create tournament → register solo + doubles → draw → start → TO scores all matches → bracket completes → placements visible → spectator mode verified) passes in staging with zero blocker defects before v1 launch

Anti-Requirements (what NOT to build)

Build restrictions

20 criteria

☐ AR-01: Do NOT build OpenRally's own rating algorithm, ever (we integrate with an external provider — in v3)

☐ AR-02: Do NOT build any rating UX in v1 — no rating display, no rating-based seeding, no rating leaderboard, no rating history. All rating work is v3.

☐ AR-03: Do NOT build a Flutter mobile app — OpenRally is a responsive web product. TPS is the mobile surface for players.

☐ AR-04: Do NOT build entry fee payment collection in v1 — no Stripe integration, no Omise integration, no checkout flow. TOs collect fees offline.

☐ AR-05: Do NOT build player-mode or referee-mode scoring in v1 — only TO can enter scores. Player scoring + dispute flow arrives in v2. Referee mode arrives in v7.

☐ AR-06: Supabase Realtime in v1 is scoped to the bracket update channel only (`tournament:{id}:bracket`). Do NOT add other Realtime channels in v1.

☐ AR-06b: Do NOT build any tournament format other than single-elimination in v1.

☐ AR-07: Do NOT build referee or official features in v1

☐ AR-08: Do NOT build federation sanctioning UI in v1

☐ AR-09: Do NOT support tournament formats other than single elimination in v1. Double elim is v2; round robin + groups+KO is v4.

☐ AR-10: Do NOT support sports other than padel

☐ AR-11: Do NOT build court reservation (TPS owns this domain)

☐ AR-12: Do NOT build coach/lesson booking (TPS owns this domain)

☐ AR-13: Do NOT merge the OpenRally codebase into the TPS codebase

☐ AR-14: Do NOT bypass Supabase RLS from any client

☐ AR-15: (Removed — OpenRally has no Flutter code)

☐ AR-16: Do NOT build livestream video in v1 or v2

☐ AR-17: Do NOT commit secrets, `.env` files, or service role keys

☐ AR-18: Do NOT skip pre-commit linter runs

☐ AR-19: Do NOT merge a PR without both Lead Engineer and Security Engineer approval

Architectural obligations

8 criteria

☐ AR-20: DO ship the full entity schema from §9.1 in v1 — including empty tables for Payment, Score, RatingPush, Sanctioning

☐ AR-21: DO define the `RatingProvider` and `PaymentProvider` interfaces as TypeScript types in v1, even though no concrete adapters exist

☐ AR-22: DO ship a `Sport` table with padel as the first row, not a constant enum — v2 sports are data, not code changes

☐ AR-23: DO model user roles as data, not constants — v2 referee/federation/admin roles are role-rows, not schema changes

☐ AR-24: DO use the `{amount_minor, currency_code}` Money shape on any fee-related column from v1, even though no money flows in v1

☐ AR-25: DO emit domain events (`tournament.published`, `registration.confirmed`) from v1 even if no subscriber exists yet — v2 features subscribe without touching the emitter

☐ AR-26: DO NOT use `if country == X` conditionals anywhere in code (even for non-payment country-specific logic) — country behavior lives in config tables

☐ AR-27: DO ship all user-visible strings as i18n keys from day one, never hardcoded — adding a new locale in v2 is a bundle file, not a code change

Infrastructure

Planned deployment topology and environments.

Environments

4 envs
EnvPurposeAlways-on?
Local devPer-engineer developmentYes
CIEphemeral per-PR test runsOn-demand
ProdLive production (PITR enabled)Yes
StagingHigh-risk changes onlyOn-demand

Planned Services

7
Next.jsPlanned
SupabasePlanned
Supabase AuthPlanned
`@g-loot/react-tournament-brackets` or equivalent off-the-shelf libraryPlanned
OpenRally-native — TO creates venues in OpenRally directlyPlanned
OpenRally-native — basic profile created on signupPlanned
OpenRally-native — search registered OpenRally usersPlanned

Geographic Market Expansion

4 tiers
PriorityMarketsNotes
P1 — Home market🇹🇭 ThailandTPS's home market. OpenRally launches here first. Languages: TH + EN. Payments: PromptPay + Stripe. Federation: TPA (Tha
P2 — Primary expansion🇮🇩 IndonesiaLarge population, growing padel scene, distinct language + payment rails. Languages: ID (Bahasa Indonesia). Payments: Go
P3 — Southeast Asia🇸🇬 Singapore, 🇲🇾 Malaysia, 🇵🇭 Philippines, 🇻🇳 Vietnam (plus Cambodia, Laos, Myanmar as long tail)Shared SEA tournament circuit potential. Languages: MS, TL, VI + EN. Payments: GrabPay, Stripe, locally-preferred rails.
P4 — Rest of Asia🇯🇵 Japan, 🇰🇷 Korea, 🇮🇳 India, 🇦🇺 Australia, 🇭🇰 Hong Kong, 🇹🇼 Taiwan, 🇨🇳 ChinaAdded only after P1-P3 are validated. Each requires meaningful new investment (language, payment, federation). Architect