Skip to main content

Tourney Copilot: Staff-Level End-to-End System Design

Goal

Build Tourney Copilot, an AI-assisted tournament operations platform that helps club operators create, manage, schedule, and optimize pickleball round-robin tournaments.

The system should allow operators to:

  • Create tournaments
  • Manage player rosters
  • Generate round-robin schedules
  • Detect operational issues automatically
  • Receive AI-powered recommendations
  • Handle player dropouts and schedule changes safely

1. High-Level Architecture


2. Architecture Philosophy

This is primarily an operational workflow system, not an AI product.

AI should help operators make decisions, but AI should never become the source of truth.

Core principles:

Database = Source of Truth Backend Services = Business Logic AI = Advisor Frontend = Workflow Layer

The system must guarantee:

  • No duplicate players
  • No duplicate matches
  • No court conflicts
  • No player double-booking
  • Safe handling of player dropouts
  • Auditable schedule changes

3. Service Architecture

Event Service

Responsible for tournament lifecycle management.

Responsibilities

  • Create event
  • Update settings
  • Validate event readiness
  • Manage event status transitions

Lifecycle

Draft ↓ Scheduled ↓ Active ↓ Completed ↓ Archived

Player Service

Manages player registration and roster operations.

Responsibilities

  • Add player
  • Remove player
  • Manage waitlist
  • Handle player dropout
  • Prevent duplicate registration

Schedule Service

Responsible for generating tournament schedules.

Responsibilities

  • Generate round-robin schedule
  • Regenerate schedules
  • Simulate schedule modifications
  • Court assignment
  • Bye allocation

Important Principle

The schedule engine should be deterministic.

AI may recommend schedule changes. AI should never generate final schedules directly.

Warning Service

Responsible for identifying operational issues.

Example Warnings

Odd number of players Too few courts Duplicate matchup Court conflict Player scheduled twice in same round Uneven bye distribution Dropped player still assigned Missing scores

Warnings should be deterministic and testable.


Copilot Service

Provides AI-powered recommendations.

Example Questions

How can I make this schedule fairer? What should I do if one player drops out? Which players have the most byes? Do I have enough courts?

Tool-Based Architecture

The LLM receives tournament state through tools.

getEventSummary() getSchedule() getWarnings() analyzeFairness() simulateDropout()

The AI never queries the database directly.


4. Database Design

clubs

CREATE TABLE clubs ( id UUID PRIMARY KEY, name TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL );

events

CREATE TABLE events ( id UUID PRIMARY KEY, club_id UUID REFERENCES clubs(id), name TEXT NOT NULL, format TEXT NOT NULL, status TEXT NOT NULL, start_time TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL );

event_settings

CREATE TABLE event_settings ( event_id UUID PRIMARY KEY REFERENCES events(id), court_count INT NOT NULL, games_per_match INT NOT NULL, max_players INT, scoring_format TEXT );

players

CREATE TABLE players ( id UUID PRIMARY KEY, club_id UUID REFERENCES clubs(id), name TEXT NOT NULL, email TEXT, skill_level TEXT, created_at TIMESTAMPTZ NOT NULL );

event_players

CREATE TABLE event_players ( id UUID PRIMARY KEY, event_id UUID REFERENCES events(id), player_id UUID REFERENCES players(id), status TEXT NOT NULL, seed INT, created_at TIMESTAMPTZ NOT NULL, UNIQUE(event_id, player_id) );

courts

CREATE TABLE courts ( id UUID PRIMARY KEY, event_id UUID REFERENCES events(id), name TEXT NOT NULL );

rounds

CREATE TABLE rounds ( id UUID PRIMARY KEY, event_id UUID REFERENCES events(id), round_number INT NOT NULL, UNIQUE(event_id, round_number) );

matches

CREATE TABLE matches ( id UUID PRIMARY KEY, event_id UUID REFERENCES events(id), round_id UUID REFERENCES rounds(id), court_id UUID REFERENCES courts(id), scheduled_order INT NOT NULL, status TEXT NOT NULL );

match_players

CREATE TABLE match_players ( id UUID PRIMARY KEY, match_id UUID REFERENCES matches(id), player_id UUID REFERENCES players(id), side TEXT NOT NULL, UNIQUE(match_id, player_id) );

warnings

CREATE TABLE warnings ( id UUID PRIMARY KEY, event_id UUID REFERENCES events(id), type TEXT NOT NULL, severity TEXT NOT NULL, message TEXT NOT NULL, context JSONB, resolved_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL );

copilot_messages

CREATE TABLE copilot_messages ( id UUID PRIMARY KEY, event_id UUID REFERENCES events(id), role TEXT NOT NULL, content TEXT NOT NULL, tool_name TEXT, tool_result JSONB, created_at TIMESTAMPTZ NOT NULL );

5. Why SQL Instead of Document Database

SQL Strengths

Tournament systems are highly relational.

Event ├── Players ├── Courts ├── Rounds └── Matches ├── Players └── Scores

Common queries:

  • Find all matches for a player
  • Detect duplicate matchups
  • Count player byes
  • Identify court utilization
  • Analyze fairness metrics

SQL excels at these operations.


Transaction Safety

Schedule generation requires atomicity.

Create Rounds Create Matches Assign Courts Assign Players Generate Warnings

Either all succeed or all fail.

BEGIN; ... COMMIT;

Document databases generally require additional complexity for this level of consistency.


Future Analytics

The product will eventually need:

Player participation trends Court utilization Win rates Fairness metrics Tournament statistics

SQL is significantly better suited for analytical workloads.


Use relational tables for operational data.

Use JSONB for flexible AI metadata.

warnings.context JSONB copilot_messages.tool_result JSONB events.metadata JSONB

This provides both structure and flexibility.


6. API Design

Event APIs

Create Event

POST /api/events

Get Event

GET /api/events/:eventId

Update Event

PATCH /api/events/:eventId

Player APIs

Add Player

POST /api/events/:eventId/players

Remove Player

DELETE /api/events/:eventId/players/:playerId

Update Player Status

PATCH /api/events/:eventId/players/:playerId/status

Schedule APIs

Generate Schedule

POST /api/events/:eventId/schedule/generate

Get Schedule

GET /api/events/:eventId/schedule

Regenerate Schedule

POST /api/events/:eventId/schedule/regenerate

Simulate Dropout

POST /api/events/:eventId/schedule/simulate-dropout

Warning APIs

GET /api/events/:eventId/warnings
POST /api/events/:eventId/warnings/recalculate

Copilot APIs

POST /api/events/:eventId/copilot/messages
GET /api/events/:eventId/copilot/messages

7. Schedule Generation Flow

User clicks Generate Schedule ↓ Load event settings ↓ Load active players ↓ Validate inputs ↓ Generate round-robin schedule ↓ Save rounds ↓ Save matches ↓ Generate warnings ↓ Return schedule

All steps occur inside a transaction.


8. Round-Robin Scheduling Algorithm

Circle Method

When player count is odd:

Add BYE placeholder

Example:

P1 P2 P3 P4 P5 BYE

For each round:

Pair first half with second half Rotate players Assign courts Repeat

Complexity:

O(n²)

which is acceptable for club-sized tournaments.


9. Warning Engine

Warnings should be rule-based.

type WarningRule = { type: string; severity: "info" | "warning" | "critical"; evaluate(state: TournamentState): Warning[]; };

Example rules:

OddPlayerCountRule TooFewCourtsRule DuplicateMatchupRule CourtConflictRule PlayerDoubleBookedRule ByeBalanceRule DropoutImpactRule

Benefits:

  • Deterministic
  • Explainable
  • Testable
  • Reliable

10. AI Copilot Architecture

Copilot UI ↓ Copilot API ↓ LLM Orchestrator ↓ Tool Layer ↓ Event Service Schedule Service Warning Service

Example Interaction

User:

How can I make this schedule fairer?

Tool calls:

getEventSummary() getSchedule() analyzeFairness()

Response:

Player A has two byes while others have one. Recommendation: - Add one additional player - Regenerate schedule

11. Frontend Architecture

Pages

/events/:eventId

Components

EventSettingsForm PlayerManager ScheduleTable WarningPanel CopilotPanel SuggestedActions

State Management

Server State

TanStack Query

Used for:

  • Event data
  • Schedule
  • Players
  • Warnings

Local State

Used for:

  • Form inputs
  • Draft edits
  • Copilot prompt input

Layout

-------------------------------------------------- Event Header -------------------------------------------------- Left Column ├── Event Settings ├── Players └── Warnings Right Column ├── Schedule └── AI Copilot --------------------------------------------------

12. Reliability & Safety

Transactions

Use transactions for:

Schedule generation Schedule regeneration Player dropout workflows

Audit Logs

CREATE TABLE audit_logs ( id UUID PRIMARY KEY, event_id UUID, actor_id UUID, action TEXT, before_state JSONB, after_state JSONB, created_at TIMESTAMPTZ );

Track:

Schedule regeneration Player removal Court changes AI-approved actions

AI Safety

Never allow:

LLM → Database

Always require:

LLM Suggestion ↓ User Approval ↓ Backend API ↓ Database Update

13. Observability

Track business metrics.

Events

event_created player_added schedule_generated schedule_regenerated warning_created copilot_message_sent copilot_action_accepted

System Metrics

Schedule generation latency Copilot response latency Warning count Regeneration rate Player dropout frequency

14. MVP Roadmap

Phase 1

Create event Manage players Generate schedule Show warnings

Phase 2

AI copilot Dropout simulation Suggested actions Audit logs

Phase 3

Doubles support Manual schedule editing Skill balancing Notifications Live scoring

Final Recommendation

Build as a modular monolith initially:

Frontend: Next.js + React Backend: Node.js / NestJS Database: PostgreSQL AI: LLM + Tool Calling Infrastructure: Docker + Kubernetes (optional initially)

Core Principles

Postgres is the source of truth. Scheduling is deterministic. Warnings are rule-based. AI provides recommendations only. All mutations go through audited APIs.

This architecture optimizes for:

  • Fast MVP delivery
  • Operational correctness
  • Future AI expansion
  • Maintainability
  • Scalability
  • Auditability