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-25Foundation
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-07Player Identity
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.
- 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-28Organizer + Venues + Admin
Organizer + Venues + Admin
TO onboarding, admin approval, venue management, draft tournament creation.
- 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-28Registration
Registration
Solo + doubles registration with partner matching and waitlist.
- 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-28Draw Engine + Public Bracket
Draw Engine + Public Bracket
Single-elim bracket generation with manual seeding, byes, and read-only public view.
- 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-22Live Scoring
Live Scoring
TO score entry, bracket cascade, retirement, score-edit undo.
- 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-22Scheduling
Scheduling
Court × time grid scheduling with auto-schedule and conflict detection.
- 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-22Realtime + Spectator
Realtime + Spectator
Supabase Realtime bracket updates and TV-friendly spectator mode.
- 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-22Placements + Polish
Placements + Polish
Placements, cancellation, social sharing, pre-launch polish.
- 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 '26General Availability
General Availability
Production-hardened, first real Thailand padel tournament runs on OpenRally. **Scope amendments (2026-06-25, audit-first v1.0 kickoff):**
- **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
Spec files
8 docsopenrally-build-prompt.md
The master specification — mission, scope, tech stack, architecture, milestones, screen specs, ISC acceptance criteria, and anti-requirements.
openrally-v1-detailed-specs.md
Detailed v1 specifications — tournament lifecycle, match lifecycle, registration, draw generation, scoring, Realtime architecture, scheduling, Edge Functions, emails, and navigation.
openrally-v2-v9-reference.md
Deferred v2 features — referee roles, federation sanctioning, payments (Stripe + Omise for Thailand), rating integration.
openrally-challonge-analysis.md
How TPS currently uses Challonge — every API operation mapped so OpenRally can replace it natively.
openrally-design-system.md
Visual language — color palette (light + dark), Inter typography scale, status badges, component patterns (shadcn/ui), bracket and scheduling styling.
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.
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.
decisions.md
Living log of roadmap planning decisions — sequence changes, scope trims, architectural pivots, risk calls. Most recent first.
Features
Feature catalog by persona with v1/v2 scope designations.
4 Differentiator Pillars
4 Differentiator Pillars
Deep rating integration
v1 Not in v1
v3 Rating integration with one external provider — profiles, brackets, auto-seeding, per-match impact, leaderboard
Federation Support
v1 Not in v1
v7 Sanctioning UI, official tournament approval workflows, federation ranking management, referee mode
Tournament Formats
v1 Single Elimination
v2 Double Elimination
v4 Round Robin, Groups + Knockout
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
Product Roadmap
9 versions| Version | Theme | Key Features |
|---|---|---|
| v1 | MVP — Thailand single-elim padel | TO live scoring, registration + draws, realtime bracket (Supabase), social auth + magic link, spectator mode, TH + EN, no payments |
| v2 | Complete the live experience | Player score entry, dispute flow, push notifications, double elim, match history, TV display mode |
| v3 | Ratings + platform maturity | Rating integration, historical tournament import, TPS APIs |
| v4 | Formats + config | Round robin, groups+KO, scoring configs, registration approval, personal records, sponsorship |
| v5 | Monetize | Payments, refunds, TO payouts |
| v6 | Messaging | WhatsApp + LINE notifications |
| v7 | Federations | Sanctioning UI, ranking management, referee mode |
| v8 | Multi-sport | Tennis, pickleball, badminton, squash |
| v9 | Indonesia | Bahasa Indonesia, GoPay/OVO/Dana, Telegram |
Feature Catalog
Player Features
13 v1
Player Features
13 v1| ID | Feature | Scope |
|---|---|---|
| PL-V1-01 | Player profile created on signup | v1 |
| PL-V1-02 | Browse tournament list | v1 |
| PL-V1-03 | Register for a tournament solo | v1 |
| PL-V1-04 | Register for doubles by searching OpenRally users | v1 |
| PL-V1-04b | Add partner later | v1 |
| PL-V1-04c | Remove partner | v1 |
| PL-V1-05 | View live single-elim bracket | v1 |
| PL-V1-06 | View "my next match" card during a tournament | v1 |
| PL-V1-07 | View final placements | v1 |
| PL-V1-08 | Spectator mode | v1 |
| PL-V1-09 | Social sharing | v1 |
| PL-V1-10 | Waitlist promotion notification | v1 |
| PL-V1-11 | Withdrawal | v1 |
Tournament Organizer Features
12 v1
Tournament Organizer Features
12 v1| ID | Feature | Scope |
|---|---|---|
| TO-V1-01 | Create a tournament | v1 |
| TO-V1-02 | Open / close registration window | v1 |
| TO-V1-03 | Manage entry list | v1 |
| TO-V1-04 | Seed players manually | v1 |
| TO-V1-05 | Generate draw from seeds | v1 |
| TO-V1-06 | Schedule matches to courts and time slots | v1 |
| TO-V1-07 | Regenerate the draw if registrations change before publish | v1 |
| TO-V1-08 | Publish the draw | v1 |
| TO-V1-09 | Start tournament | v1 |
| TO-V1-10 | Enter live scores | v1 |
| TO-V1-11 | Walkover / withdrawal / retirement | v1 |
| TO-V1-12 | Cancel tournament | v1 |
Venue Features
3 v1
Venue Features
3 v1| ID | Feature | Scope |
|---|---|---|
| CV-V1-01 | When 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-02 | Tournament detail page shows the venue's name, location, and court count | v1 |
| CV-V1-03 | A simple "Venues" page lists all venues that have OpenRally tournaments, each linking to the list of that venue's tournaments | v1 |
Detailed Specs
v113 sections
| # | Section |
|---|---|
| 1 | Tournament Lifecycle State Machine |
| 2 | Match Lifecycle State Machine |
| 3 | Registration Flows |
| 4 | Draw Generation Algorithm |
| 5 | Scoring Validation Rules |
| 6 | Realtime Architecture |
| 7 | Match Scheduling |
| 8 | Edge Functions Inventory |
| 9 | Email Notification Triggers |
| 10 | Organizer Dashboard |
| 11 | Landing Page |
| 12 | Admin Panel |
| 13 | Navigation Map |
Competitive Landscape
5 competitors analyzed
| Product | Category | Platform | Verdict |
|---|---|---|---|
WeCourts | Rating-first padel platform | Web + iOS + Android | Strong in MENA. Rating (WPPR) is the moat; organizer tooling is thin. Geo-blocks non-MENA IPs. |
Padelution | Multi-format padel tournament tool | Web + iOS + Android + macOS | Finnish, 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 App | Americano-specialized tournament app | iOS + Android (Flutter) + Flutter Web spectator | Strongest 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 Tournaments | Booking-first padel super-app's tournament module | Mobile + web | Industry leader for padel booking. Tournament module is secondary and thin. Weak in Asia. |
BracketMaker / americanopadel.app / Padelpuffin / Padel Point / PadelMix | Niche Americano tools | Mix | Long 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
Color Tokens
12 tokens| Token | Light | Dark | Usage |
|---|---|---|---|
| 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
Typography
7 levelsStatus Badges
13 states
Status Badges
13 statesDatabase Schema
Postgres DDL — 14 enums, 16 active tables, 6 reserved. Source: openrally-database-schema.sql
v1 Active Tables
16 tables
v1 Active Tables
16 tables| Table | Description |
|---|---|
| sport | Sport (first-class dimension, not a constant) |
| user | User (extends Supabase auth.users) |
| venue | Venue (OpenRally-native in v1, no TPS dependency) |
| tournament | Tournament |
| round_duration | Round Duration (configurable match duration per round) |
| court_availability | Court Availability (blocked time ranges per court) |
| team | Team (for doubles pairing; solo players also have a team of 1) |
| team_member | Team (for doubles pairing; solo players also have a team of 1) |
| registration | Registration |
| partner_invite | Partner Invite |
| draw | Draw |
| draw_seed | Draw Seed (persistent, auto-saved per drag) |
| match | Match |
| placement | Placement (auto-derived, TO can override) |
| organizer_application | Organizer Application |
| audit_log | Audit Log (immutable) |
v2-v9 Reserved Tables
6 reserved
v2-v9 Reserved Tables
6 reservedThese tables exist in the v1 schema but stay empty until their target version. Reserving the shape now avoids a migration scramble later.
| Table | Description |
|---|---|
| RESERVEDrating_provider | Rating Provider (v3) |
| RESERVEDplayer_provider_link | Player Provider Link (v3 — links OpenRally user to external rating provider) |
| RESERVEDrating_push | Rating Push (v3 — outbound match result push to rating provider) |
| RESERVEDsanctioning | Sanctioning (v7 — federation tournament link) |
| RESERVEDpayment | Payment (v5 — entry fee transactions) |
| RESERVEDscore | Score (v2 — richer polymorphic per-sport scoring) |
Enums
14 enums
Enums
14 enumsTech Stack
v1 technology choices and architecture overview.
Show stack details
v1 Stack
| Web app | Next.js (App Router) + TypeScript + shadcn/ui + Tailwind |
| Backend | Supabase (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 |
| Venues | OpenRally-native — TO creates venues in OpenRally directly |
| Player profile | OpenRally-native — basic profile created on signup |
| Partner search | OpenRally-native — search registered OpenRally users |
| State (Next.js) | Zustand |
| Feature flags | PostHog |
| Issue tracking | Linear (new `MD-` project) |
| CI/CD | GitHub Actions |
| Monitoring | Sentry + PostHog events |
| Resend | |
| 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
| # | Screen | Path | Persona |
|---|---|---|---|
| 1 | Landing page | / (unauth) | Public |
| 2 | Sign-in | /login | Public |
| 3 | Tournament detail (public) | /tournaments/[id] | Public |
| 4 | Player home / dashboard | / (auth) | Player |
| 5 | Tournament list | /tournaments | Player |
| 6 | Tournament detail (auth, with register CTA) | /tournaments/[id] | Player |
| 7 | Partner search modal | (modal from tournament detail) | Player |
| 8 | Partner invite accept | /invite/[token] | Player |
| 9 | My registrations | /me/registrations | Player |
| 10 | Settings | /me/settings | Player |
| 11 | Organizer dashboard | /organizer | TO |
| 12 | Create tournament | /organizer/tournaments/new | TO |
| 13 | Tournament management hub | /organizer/tournaments/[id] | TO |
| 14 | Registrations tab | /organizer/tournaments/[id]/registrations | TO |
| 15 | Draw tab (seeding + generation + publish) | /organizer/tournaments/[id]/draw | TO |
| 16 | Schedule tab (court × time grid) | /organizer/tournaments/[id]/schedule | TO |
| 17 | Placements tab (post-tournament) | /organizer/tournaments/[id]/placements | TO |
| 18 | Organizer public profile | /organizer/[slug] | Player |
| 19 | Apply to be an organizer | /organizer/apply | Player |
| 20 | My application status | /organizer/apply/status | Player |
| 21 | Admin dashboard | /admin | Admin |
| 22 | Organizer applications list | /admin/organizer-applications | Admin |
| 23 | Organizer application detail | /admin/organizer-applications/[id] | Admin |
Acceptance Criteria
v1 ISC — 82+ atomic, binary-testable criteria.
Auth + Identity
5 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
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
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
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
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
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
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
Tournament Cancellation
1 criteria☐ ISC-60: TO can cancel a tournament — all registered players receive cancellation email, registrations voided
Non-functional
9 criteria
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
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
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
Environments
4 envs| Env | Purpose | Always-on? |
|---|---|---|
| Local dev | Per-engineer development | Yes |
| CI | Ephemeral per-PR test runs | On-demand |
| Prod | Live production (PITR enabled) | Yes |
| Staging | High-risk changes only | On-demand |
Planned Services
7
Planned Services
7Geographic Market Expansion
4 tiers
Geographic Market Expansion
4 tiers| Priority | Markets | Notes |
|---|---|---|
| P1 — Home market | 🇹🇭 Thailand | TPS's home market. OpenRally launches here first. Languages: TH + EN. Payments: PromptPay + Stripe. Federation: TPA (Tha |
| P2 — Primary expansion | 🇮🇩 Indonesia | Large 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, 🇨🇳 China | Added only after P1-P3 are validated. Each requires meaningful new investment (language, payment, federation). Architect |
WeCourts
Padelution
Americano Padel App
Playtomic Tournaments