From f717abf0a07f46edae4ed31cccd4c4a6b560eaa7 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Mon, 1 Dec 2025 22:01:58 +0100 Subject: [PATCH] wip --- ROADMAP.md | 20 - docs/ARCHITECTURE.md | 1624 +++++++++++++++-------------------- docs/CONCEPT.md | 402 --------- docs/ROADMAP.md | 701 ++++++--------- docs/concept/ADMINS.md | 236 +++++ docs/concept/COMPETITION.md | 283 ++++++ docs/concept/CONCEPT.md | 232 +++++ docs/concept/DRIVERS.md | 236 +++++ docs/concept/LANDING.md | 192 +++++ docs/concept/RACING.md | 250 ++++++ docs/concept/RATING.md | 224 +++++ docs/concept/SOCIAL.md | 204 +++++ docs/concept/STATS.md | 270 ++++++ docs/concept/TEAMS.md | 211 +++++ 14 files changed, 3305 insertions(+), 1780 deletions(-) delete mode 100644 ROADMAP.md delete mode 100644 docs/CONCEPT.md create mode 100644 docs/concept/ADMINS.md create mode 100644 docs/concept/COMPETITION.md create mode 100644 docs/concept/CONCEPT.md create mode 100644 docs/concept/DRIVERS.md create mode 100644 docs/concept/LANDING.md create mode 100644 docs/concept/RACING.md create mode 100644 docs/concept/RATING.md create mode 100644 docs/concept/SOCIAL.md create mode 100644 docs/concept/STATS.md create mode 100644 docs/concept/TEAMS.md diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index 5755751a8..000000000 --- a/ROADMAP.md +++ /dev/null @@ -1,20 +0,0 @@ -# Roadmap - -## IRacing Selectors Update (2025-11-27) - -**Summary:** Fixed 15 selectors against [`html-dumps-optimized/iracing-hosted-sessions/`](html-dumps-optimized/iracing-hosted-sessions/) dumps per [`IRacingSelectors-update-plan.md`](packages/infrastructure/adapters/automation/IRacingSelectors-update-plan.md). - -**Key changes:** -- Critical (adminList → step-specific tables; sliders → label-proximity) -- Recommended (chakra- prefixes, label~inputs) -- BLOCKED_SELECTORS unchanged/safe. - -**Verification:** -- 4 unit tests green in [`IRacingSelectors.test.ts`](packages/infrastructure/adapters/automation/IRacingSelectors.test.ts) -- Debug verified 70+ matches across 18 dumps. - -**Artifacts:** -- [`IRacingSelectors-update-plan.md`](packages/infrastructure/adapters/automation/IRacingSelectors-update-plan.md) -- updated comments in [`IRacingSelectors.ts`](packages/infrastructure/adapters/automation/IRacingSelectors.ts) "VERIFIED against html-dumps-optimized 2025-11-27" - -**Next:** E2E automation integration. \ No newline at end of file diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 469abd59b..6983fb4e3 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,1102 +1,888 @@ # GridPilot Architecture -This document provides a technical deep-dive into GridPilot's Clean Architecture implementation. For business context, see [CONCEPT.md](./CONCEPT.md). For technology stack details, see [TECH.md](./TECH.md). +This document describes how the current GridPilot repository fits into the broader **competition platform** vision described in the concept docs, with a primary focus on the **GridPilot Web Platform (website + core platform services)** and a supporting focus on the **Automation & Companion subsystem**. + +- Product vision and competition layer: see [`CONCEPT.md`](docs/concept/CONCEPT.md), [`ADMINS.md`](docs/concept/ADMINS.md), [`DRIVERS.md`](docs/concept/DRIVERS.md), [`COMPETITION.md`](docs/concept/COMPETITION.md), [`RACING.md`](docs/concept/RACING.md), [`STATS.md`](docs/concept/STATS.md), [`RATING.md`](docs/concept/RATING.md), [`SOCIAL.md`](docs/concept/SOCIAL.md), [`TEAMS.md`](docs/concept/TEAMS.md). +- Technology overview and tooling: see [`TECH.md`](docs/TECH.md). +- Testing strategy: see [`TESTS.md`](docs/TESTS.md). + +The architecture is organized into two main parts: + +1. **GridPilot Web Platform Architecture** (primary; website + core platform services). +2. **Automation & Companion Architecture** (supporting subsystem that executes hosted sessions for the platform and admins). + +A short system context comes first, then the web platform, then the automation/companion subsystem, then evolution and integration. --- -## iRacing Automation Strategy +## 1. System Context and Boundaries -**IMPORTANT**: Understanding the distinction between iRacing's interfaces is critical for our automation approach. +The concept docs define GridPilot as a full **competition layer** for iRacing leagues: -### Two iRacing Interfaces +- League identity, seasons, signups, teams, results, penalties, stats, social, discovery. -1. **iRacing Website (members.iracing.com)**: Standard HTML/DOM web application accessible at `https://members-ng.iracing.com/`. This is where hosted session management lives. Being a standard web application, it can be automated with browser automation tools like **Playwright** or **Puppeteer**. This is 100% legal and our preferred approach. +The **current codebase in this repo** implements only a **narrow, but critical slice** of that vision: -2. **iRacing Desktop App (Electron)**: The iRacing desktop application is a sandboxed Electron app. Its DOM is inaccessible, and any modification violates iRacing's Terms of Service. This is why tools like iRefined were shut down. +- A **desktop companion app** that runs on the admin’s machine. +- A **hosted-session automation engine** that drives iRacing’s web UI (members.iracing.com) via Playwright. +- Supporting domain models for sessions, steps, page validation, checkout safety and overlays. -### Automation Rules +Everything else from the concept docs (league/season management, result aggregation, team stats, complaints, social features) is **future or external** to this repo and should be treated as an integrating platform that will later consume this automation layer. -**Allowed Approaches:** -- ✅ Browser automation of the iRacing website using Playwright/Puppeteer -- ✅ Standard DOM manipulation and interaction via browser automation APIs +### 1.1 Current Scope (this repo) -**Forbidden Approaches:** -- ❌ DOM automation inside the iRacing Electron desktop app -- ❌ Script injection into the desktop client -- ❌ Any client modification (similar to what got iRefined shut down) +- Electron companion app: + - [`index`](apps/companion/main/index.ts) + - [`di-container`](apps/companion/main/di-container.ts) + - [`ipc-handlers`](apps/companion/main/ipc-handlers.ts) + - React renderer: [`App`](apps/companion/renderer/App.tsx), UI components under [`components`](apps/companion/renderer/components). -### Technology Stack +- Automation & browser integration: + - Domain models around hosted sessions and steps, e.g. [`AutomationSession`](packages/domain/entities/AutomationSession.ts:1), [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1). + - Application layer ports and use-cases, e.g. [`IAutomationEngine`](packages/application/ports/IAutomationEngine.ts:1), [`ISessionRepository`](packages/application/ports/ISessionRepository.ts:1), [`StartAutomationSessionUseCase`](packages/application/use-cases/StartAutomationSessionUseCase.ts:1). + - Playwright-based automation adapter and helpers, e.g. [`PlaywrightAutomationAdapter`](packages/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1), [`CheckoutPriceExtractor`](packages/infrastructure/adapters/automation/CheckoutPriceExtractor.ts:1). + - Logging, config and in-memory repository implementations, e.g. [`InMemorySessionRepository`](packages/infrastructure/repositories/InMemorySessionRepository.ts:1), [`AutomationConfig`](packages/infrastructure/config/AutomationConfig.ts:1), [`LoggingConfig`](packages/infrastructure/config/LoggingConfig.ts:1), [`PinoLogAdapter`](packages/infrastructure/adapters/logging/PinoLogAdapter.ts:1). -- **Primary**: Playwright for browser automation of members.iracing.com -- **Alternative**: Puppeteer (if Playwright isn't suitable for specific use cases) +- Testing: + - Unit tests for domain, application and infrastructure. + - Integration tests across automation, Electron, and renderer. + - E2E workflows that drive the hosted-session steps using Playwright and fixtures. + - See [`TESTS.md`](docs/TESTS.md) and the `tests/*` tree. -### Development vs Production Mode +### 1.2 Future / External Scope (concept platform) -- **Development Mode**: Launches a Playwright-controlled browser to automate the real iRacing website -- **Production Mode**: Same as development - browser automation targeting members.iracing.com -- **Test Mode**: Uses mocked browser automation (no real browser interaction) +The following are **product/domain concerns** from the concept docs that are NOT implemented in this repo and should be treated as external or future services: -### HTML Fixtures (resources/iracing-hosted-sessions/) +- League/season/series modeling and identity (`League`, `Season`, `Race`, etc.). +- Driver and team accounts, cross-league profiles, stats and history. +- Standing/points systems, drop weeks, team scoring, analytics. +- Complaint & penalty workflow, protest queues, visibility and audit trails. +- Social graph, discovery, messaging, notifications. -The HTML files in `resources/iracing-hosted-sessions/` are **static snapshots for reference and testing**. They help developers understand the iRacing UI structure and serve as fixtures for E2E tests. Production automation always targets the REAL iRacing website at members-ng.iracing.com. +In a future state, these would live in separate services or applications (e.g. “GridPilot Core Platform”) and integrate with this automation layer via API or IPC boundaries. The automation layer is deliberately kept small, testable and ToS-safe to serve as a **reliable hosted-session engine** for those higher-level features. + +--- +## 2. GridPilot Web Platform Architecture + +This section describes the **GridPilot Web Platform** (website + core platform services) as the primary system. The automation and companion slice in this repo exist to serve this platform and the admins/drivers described in the concept docs. + +### 2.1 Web Platform Scope + +The web platform is responsible for the competition-layer features described in the concept docs, presented through the website and backed by platform services: + +- **League and season management** + - League identity, branding and public presence as described in [`CONCEPT.md`](docs/concept/CONCEPT.md) and [`ADMINS.md`](docs/concept/ADMINS.md). + - Season calendars, formats, rules and information pages. + - Configuration of point systems, drop weeks, solo vs team modes. + +- **Driver and team system** + - Driver registration, league join flows and rosters as described in [`DRIVERS.md`](docs/concept/DRIVERS.md) and [`TEAMS.md`](docs/concept/TEAMS.md). + - Team creation and management, constructors-style championships and history. + +- **Results, standings, stats and rating** + - Import and storage of race results (either via manual upload or result automation) and computation of standings. + - Season and all-time statistics per [`STATS.md`](docs/concept/STATS.md). + - Rating computation and presentation per [`RATING.md`](docs/concept/RATING.md). + +- **Complaints, penalties and classification** + - Complaint intake, review workflows and penalty assignment as described in [`COMPETITION.md`](docs/concept/COMPETITION.md) and [`ADMINS.md`](docs/concept/ADMINS.md). + - Penalty-aware classification and standings adjustment. + +- **Social and discovery** + - League landing and discovery surfaces per [`SOCIAL.md`](docs/concept/SOCIAL.md) and [`LANDING.md`](docs/concept/LANDING.md). + - Links to Discord, streaming, content, and lightweight social graph features. + +Architecturally: + +- The **website frontend** lives in this monorepo under [`apps/website`](apps/website:1) as the primary web presentation surface (even if only partially implemented today). +- The **core platform / backend services** form a separate layer of APIs, databases and background workers that implement the competition, stats and rating logic; they may live in this monorepo or in separate services, but are conceptually distinct from the automation slice. +- The **automation & companion subsystem** in this repo acts as a hosted-session execution backend that the platform can call when it needs to create or manage iRacing hosted sessions. + +### 2.2 Layering for the Web Platform + +The web platform itself is designed using a Clean / Hexagonal style, conceptually mirroring the layering used for the automation slice but with a competition-first domain: + +- **Presentation (Web)** + - Website SPA/SSR app under [`apps/website`](apps/website:1). + - Routes for league pages, season views, schedules, standings, driver profiles, team pages, stats dashboards, rating insights and social/discovery pages. + - UI components for admin configuration flows (league setup, season configuration, team/driver management) and for driver-facing views (signups, results, standings, stats, complaints). + +- **Application (Platform use-cases)** + - Use-cases that implement the flows described in the concept docs, for example: + - CreateLeague, ConfigureSeason, OpenRegistration, CloseRegistration. + - ImportResults, RecalculateStandings, ApplyPenalty, RebuildClassification. + - SubmitComplaint, ReviewComplaint, AssignPenalty. + - ComputeStatsSnapshot, ComputeRatingUpdate. + - These use-cases orchestrate domain models, repositories and external services but stay independent of HTTP frameworks and database details. + +- **Domain (Competition models)** + - Conceptual aggregates such as League, Season, Event, Race, Driver, Team, Complaint, Penalty, Standing, SeasonStats, DriverStats, TeamStats, RatingSnapshot. + - Invariants around eligibility, registration windows, scoring rules, penalty impacts and rating updates. + - These models are **conceptual** in this architecture document; they are not yet implemented as concrete TypeScript files in this repo and should be treated as the target design for future platform work. + +- **Infrastructure (Platform adapters)** + - Persistence adapters for storing leagues, seasons, events, results, stats, rating snapshots, complaints and penalties in one or more databases. + - Messaging/queue adapters for background processing (e.g. delayed stats/rating recomputation after penalties). + - Integration adapters for authentication, payment/billing (if added later), email/notifications and external identity providers. + - HTTP/GraphQL controllers that expose the application use-cases to the website, companion and other consumers. + +The same dependency rule applies as in the automation slice: + +- Presentation and infrastructure depend **inwards** on application and domain. +- Domain and application stay free of framework and transport details so that the platform can support multiple frontends (web, admin consoles, APIs) over time. + +### 2.3 Website Frontend Architecture + +The website under [`apps/website`](apps/website:1) is the primary presentation surface for admins, drivers and teams: + +- **Application shell and routing** + - A SPA/SSR architecture (for example, React/Next-style) with route groups for: + - League landing and overview pages. + - Season calendars, event detail and schedule pages. + - Standings and championship tables (driver and team). + - Driver profiles and team pages. + - Stats dashboards and rating views as described in [`STATS.md`](docs/concept/STATS.md) and [`RATING.md`](docs/concept/RATING.md). + - Complaint and penalty views, including driver- and admin-facing lists. + +- **State and data management** + - A query-driven data access pattern (e.g. query client plus local UI store) to fetch and cache platform data from backend APIs. + - UI state for filters, table sorting, visualization settings and admin workflows kept distinct from server data. + - Optimistic updates used carefully for admin actions where eventual consistency is acceptable. + +- **Presentation of competition concepts** + - League homepages present branding, schedule, standings, rosters and links as described in [`ADMINS.md`](docs/concept/ADMINS.md) and [`CONCEPT.md`](docs/concept/CONCEPT.md). + - Season pages combine calendar, results, standings and stats into a coherent narrative. + - Driver profiles show season and all-time stats, rating progression, team history and complaint/penalty context consistent with [`DRIVERS.md`](docs/concept/DRIVERS.md) and [`STATS.md`](docs/concept/STATS.md). + - Team pages present constructors-style standings, driver contribution and historical performance in line with [`TEAMS.md`](docs/concept/TEAMS.md). + - Rating views explain changes over time and per-race contributions in a way that matches the principles described in [`RATING.md`](docs/concept/RATING.md). + +- **APIs consumed by the website** + - The website calls platform APIs (HTTP/REST or GraphQL) to: + - Read league, season, event, driver and team data. + - Read standings, stats snapshots and rating values. + - Submit registrations, complaints and admin actions. + - Trigger or schedule result imports and (where allowed) automation runs. + - These APIs are implemented by the platform application layer and are conceptually separate from the automation-specific ports in `packages/application`. + +### 2.4 Platform Integration with Automation + +The web platform uses the automation & companion subsystem as a hosted-session engine, without coupling to Playwright or Electron details: + +- **From league/season data to automation intent** + - The platform aggregates league configuration (series settings, car/track choices, schedule, server settings) and derives a hosted-session configuration structure conceptually equivalent to [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1). + - This configuration captures the intent of a single hosted session (or a batch of sessions across a season) in a platform-neutral format. + +- **Hand-off to automation** + - The platform stores these hosted-session configurations and exposes them via APIs to operator-facing tools: + - The companion app retrieves configurations and invokes automation use-cases using the shared application ports. + - A future headless orchestrator service could call the same ports directly, as long as it respects iRacing ToS and keeps an admin in the loop. + - The automation subsystem executes the configurations against iRacing’s web UI and reports back status and results. + +- **Ingesting results back into the platform** + - Once automation completes, race results and any derived classification (including incident counts and penalties applied later) are ingested by platform services. + - The stats and scoring engine described in [`STATS.md`](docs/concept/STATS.md) processes these inputs to update standings and rich analytics. + - The rating engine described in [`RATING.md`](docs/concept/RATING.md) uses the same inputs (plus broader season context) to update driver ratings and team evaluations. + +- **Separation of concerns** + - Automation is explicitly a **helper** for admins and the platform: it creates predictable, repeatable hosted sessions and reduces workload, but it does not own competition identity, standings, stats or rating. + - The platform retains ownership of all long-term competition data and presents it through the website under [`apps/website`](apps/website:1). --- -## 1. Overview +## 2. System Context and Boundaries -### Clean Architecture Principles +This section describes how the automation engine and companion in this repo sit alongside the GridPilot Web App / Website, future core platform services, and iRacing itself. -GridPilot follows **Clean Architecture** (also known as Hexagonal Architecture or Ports and Adapters) to ensure: +### 2.1 Main components -- **Independence**: Business logic remains independent of frameworks, UI, databases, and external services -- **Testability**: Core domain logic can be tested without UI, database, or external dependencies -- **Flexibility**: Infrastructure components (database, API clients) can be replaced without affecting business rules -- **Maintainability**: Clear boundaries prevent coupling and make the codebase easier to understand +- **GridPilot Web App / Website** + - Browser-based UI for admins, drivers and teams. + - Focused on league management, stats, rating, discovery and social features, as described in the concept docs. + - Lives in a separate codebase as a **presentation & API consumer** that talks to core platform services over HTTP or GraphQL. -### The Dependency Rule +- **GridPilot Companion App (Electron, this repo)** + - Desktop operator console running on the admin machine. + - Provides the UI for configuring and supervising hosted-session automation. + - Integrates directly with the automation engine in this repo and owns the relationship with Playwright and the local browser. -**Dependencies point inward.** Outer layers depend on inner layers, never the reverse. +- **Automation Engine & Domain (this repo)** + - Clean Architecture core for iRacing hosted-session automation: + - Domain and value objects under [`packages/domain`](packages/domain/entities/AutomationSession.ts:1). + - Application use-cases and ports under [`packages/application`](packages/application/use-cases/StartAutomationSessionUseCase.ts:1). + - Infrastructure adapters under [`packages/infrastructure`](packages/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1). + - Exposes automation capabilities via application ports that can be used both by the companion and by external orchestrators. -``` -┌─────────────────────────────────────────┐ -│ Presentation (Web API, Web Client, │ -│ Companion App) │ -│ ┌─────────────────────────────────┐ │ -│ │ Infrastructure (PostgreSQL, │ │ -│ │ Redis, S3, iRacing Client) │ │ -│ │ ┌──────────────────────────┐ │ │ -│ │ │ Application │ │ │ -│ │ │ (Use Cases, Ports/DTOs) │ │ │ -│ │ │ ┌───────────────────┐ │ │ │ -│ │ │ │ Domain │ │ │ │ -│ │ │ │ (Entities, VOs, │ │ │ │ -│ │ │ │ Business Rules) │ │ │ │ -│ │ │ └───────────────────┘ │ │ │ -│ │ └──────────────────────────┘ │ │ -│ └─────────────────────────────────┘ │ -└─────────────────────────────────────────┘ -``` +- **iRacing Web (members.iracing.com)** + - External system whose hosted-session wizard is automated via Playwright running in a standard browser on the admin machine. + - Treated strictly as a browser-automation target to remain within iRacing Terms of Service. -**Key Rules:** -- Domain layer has **zero external dependencies** (no imports from application, infrastructure, or presentation) -- Application layer depends only on domain layer -- Infrastructure and presentation layers depend on application and domain layers -- Infrastructure adapters implement interfaces (ports) defined in the application layer +- **Core Platform / Backend Services (future/external)** + - Own competition domain concerns such as leagues, schedules, results, stats, rating and social graph as described in [`STATS.md`](docs/concept/STATS.md) and [`RATING.md`](docs/concept/RATING.md). + - Consume the automation engine as a hosted-session execution backend. -### Why Clean Architecture for GridPilot +### 2.2 Side-by-side presentation layers -GridPilot's complexity demands architectural discipline: +- The **GridPilot Web App / Website** and the **GridPilot Companion App** are sibling **presentation layers**: + - Both serve admins and drivers, but with different roles. + - Both live outside the automation core and depend **inward** on shared domain and application concepts. -1. **Multiple Clients**: Web API, Web Client, and Companion App all access the same business logic without duplication -2. **External Service Integration**: iRacing OAuth, iRacing data API, potential future integrations (Discord, Stripe) remain isolated in infrastructure layer -3. **Testing**: Core league management logic (scoring, standings, validation) can be tested independently of databases or external APIs -4. **Evolution**: As monetization features (payment processing) and new racing platforms are added, changes remain localized to appropriate layers +- Shared concept space: + - Both surfaces operate on the competition concepts described in [`CONCEPT.md`](docs/concept/CONCEPT.md), [`ADMINS.md`](docs/concept/ADMINS.md), [`DRIVERS.md`](docs/concept/DRIVERS.md), [`COMPETITION.md`](docs/concept/COMPETITION.md), [`RACING.md`](docs/concept/RACING.md), [`SOCIAL.md`](docs/concept/SOCIAL.md), [`TEAMS.md`](docs/concept/TEAMS.md). + - The automation engine uses [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1) as the canonical representation of a hosted-session setup derived from those higher-level concepts. ---- +- Division of responsibilities: + - The **website** is the primary UI for league management, discovery, stats and rating (core platform concerns). + - The **companion** is the trusted operator console for ToS-safe automation on the admin’s machine. -## 2. Layer Diagram +### 2.3 Communication model -### Conceptual Flow +At a high level, communication flows are designed around the automation engine as a reusable core: -``` -User Request (HTTP/UI Event) - ↓ -┌──────────────────────┐ -│ PRESENTATION LAYER │ Controllers, React Components, Electron IPC Handlers -└──────────────────────┘ - ↓ (calls use case) -┌──────────────────────┐ -│ APPLICATION LAYER │ Use Cases, Port Interfaces (IRepository, IClient) -└──────────────────────┘ - ↓ (enforces) -┌──────────────────────┐ -│ DOMAIN LAYER │ Entities, Value Objects, Business Rules -└──────────────────────┘ - ↑ (implements ports) -┌──────────────────────┐ -│ INFRASTRUCTURE LAYER │ PostgresLeagueRepository, IRacingOAuthClient, S3AssetStorage -└──────────────────────┘ -``` +- Typical platform-driven flow: + - Website UI → Core Platform APIs → Automation Orchestrator → Automation Engine (this repo) → Companion App (operator and overlay) → iRacing Web. -### Dependency Inversion in Practice +- Typical companion-driven flow: + - Companion App → Application Use-cases → Automation Engine → Playwright Adapter → iRacing Web. -**Bad (Direct Coupling):** -```typescript -// Use case directly depends on concrete implementation -class CreateLeagueUseCase { - constructor(private db: PostgresLeagueRepository) {} // ❌ Coupled to Postgres -} -``` +- Hand-off of automation intent: + - Core platform services or the web app can derive a [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1) from league data and store it server-side. + - The companion app or a headless orchestrator retrieves these configs via platform APIs and invokes the automation engine using the same application ports used in this repo. -**Good (Dependency Inversion):** -```typescript -// Use case depends on abstraction (port interface) -class CreateLeagueUseCase { - constructor(private leagueRepo: ILeagueRepository) {} // ✅ Depends on abstraction -} +### 2.4 System context diagram -// Infrastructure layer provides concrete implementation -class PostgresLeagueRepository implements ILeagueRepository { - // Implementation details hidden from use case -} +```mermaid +flowchart LR + Website[GridPilot Web App] + Platform[Core Platform Services] + Companion[GridPilot Companion App] + Engine[Automation Engine and Domain this repo] + IRacing[iRacing Web members.iracing com] + + Website -- HTTPS APIs --> Platform + Platform -- Automation requests --> Engine + Companion -- Local IPC and function calls --> Engine + Engine -- Browser automation via Playwright --> IRacing + Platform -- Results events and stats inputs --> Platform ``` --- -## 3. Package Boundaries +## 3. iRacing Automation Constraints -GridPilot's monorepo structure enforces layer boundaries through directory organization: +The automation strategy is shaped by iRacing’s platform boundaries and Terms of Service. -``` -/src -├── /packages # Shared domain and application logic -│ ├── /domain # Core business entities and rules -│ │ ├── /entities # League, Team, Driver, Event, Result, ScoringRule -│ │ ├── /value-objects # Email, IRacingId, PositionPoints, TeamName -│ │ └── /errors # Domain-specific exceptions -│ │ -│ ├── /application # Use cases and port interfaces -│ │ ├── /use-cases # CreateLeagueUseCase, ImportRaceResultsUseCase -│ │ ├── /ports # ILeagueRepository, IRacingClient, INotificationService -│ │ └── /dtos # Data Transfer Objects for layer communication -│ │ -│ ├── /contracts # TypeScript interfaces for cross-layer contracts -│ │ ├── /api # HTTP request/response shapes -│ │ └── /events # Domain event schemas -│ │ -│ └── /shared # Cross-cutting concerns (no domain logic) -│ ├── /logger # Winston logger adapter -│ ├── /validator # Zod schema utilities -│ └── /crypto # Encryption/hashing utilities -│ -├── /infrastructure # External service adapters -│ ├── /repositories # PostgresLeagueRepository, PostgresTeamRepository -│ ├── /clients # IRacingOAuthClient, IRacingDataClient -│ ├── /storage # S3AssetStorage, RedisCache -│ └── /queue # BullJobQueue for background tasks -│ -└── /apps # Presentation layer (framework-specific) - ├── /web-api # REST/GraphQL API (Express/Fastify/Hono) - │ ├── /controllers # HTTP request handlers - │ ├── /middleware # Auth, validation, error handling - │ └── /di-container.ts # Dependency injection wiring - │ - ├── /web-client # React SPA (Vite/Next.js) - │ ├── /components # UI components - │ ├── /hooks # React hooks for API calls - │ └── /state # TanStack Query, Zustand stores - │ - └── /companion # Electron desktop app - ├── /main # Electron main process - ├── /renderer # Electron renderer (React) - └── /automation # Playwright browser automation scripts -``` +### 2.1 Two iRacing surfaces -### Import Rules (Enforced via ESLint) +1. **Web site (members.iracing.com)** + - Standard HTML/DOM web app at `https://members-ng.iracing.com/`. + - Hosted session creation wizard lives here. + - This repo automates this surface using Playwright, treating it like any other web UI. + - All automation happens **inside a normal browser**, launched on the admin’s machine. -```typescript -// ✅ ALLOWED: Inner layer imports -// domain → nothing (zero external dependencies) -// application → domain only -import { League } from '@gridpilot/domain/entities/League'; +2. **Desktop app (iRacing launcher / sim)** + - Electron-based desktop launcher and the racing sim itself. + - DOM and process are **off-limits**; modifying or injecting into this client is ToS-violating. + - This repo does **not** automate or modify the desktop app or the sim. -// ✅ ALLOWED: Outer layer imports inner layers -// infrastructure → application + domain -import { ILeagueRepository } from '@gridpilot/application/ports/ILeagueRepository'; -import { League } from '@gridpilot/domain/entities/League'; +### 2.2 Allowed vs forbidden approaches -// ❌ FORBIDDEN: Inner layer importing outer layer -// domain → application (violates dependency rule) -import { CreateLeagueDTO } from '@gridpilot/application/dtos'; // ❌ Domain can't import application +**Allowed (and used here)** -// ❌ FORBIDDEN: Application importing infrastructure -// application → infrastructure (violates dependency inversion) -import { PostgresLeagueRepository } from '@gridpilot/infrastructure'; // ❌ Application can't know about Postgres -``` +- Browser automation of the iRacing website via Playwright. +- Navigating, filling forms, clicking DOM elements in members.iracing.com. +- Using local HTML/JSON fixtures to test selectors and flows. +- Running everything on the admin’s machine, under their control. + +**Forbidden (and explicitly avoided)** + +- Injecting scripts into the official iRacing desktop client. +- Reading or manipulating the Electron app’s internal DOM. +- Hooking the sim process or game memory. +- Running any automation that is not clearly aligned with “browser automation of a public web UI”. + +The [`PlaywrightAutomationAdapter`](packages/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1) contains additional safeguards, such as: + +- Guardrails around checkout/payment-like buttons using iRacing-specific selector maps. +- Explicit “blocked selector” detection before clicks. +- Separate mock/fixture mode for tests vs “real” mode for live sessions. + +This aligns with the safety and trust constraints in [`CONCEPT.md`](docs/concept/CONCEPT.md) and [`RACING.md`](docs/concept/RACING.md): automation is a **helper**, not a cheat, and always runs transparently on the admin’s machine. --- -## 4. Domain Layer +## 4. Clean Architecture Overview (Automation Slice) -The domain layer contains the **core business logic** with **zero framework dependencies**. It answers: "What are the fundamental rules of league racing?" +The automation & companion code follows a layered, Clean Architecture style where **core domain and application logic are shared across multiple presentation and infrastructure options**. -### Core Entities +### 4.1 Conceptual layering -**[`League`](../src/packages/domain/entities/League.ts)** -- Properties: `id`, `name`, `ownerId`, `seasons`, `settings` -- Business Rules: - - League names must be unique within an organization - - Cannot delete a league with active seasons - - Owner must be a verified iRacing user -- Invariants: `seasons` array cannot have overlapping date ranges +```text +Outer layers: + Presentation (Web App, Companion, APIs) + Infrastructure (Playwright, logging, persistence, IPC) -**[`Team`](../src/packages/domain/entities/Team.ts)** -- Properties: `id`, `name`, `captainId`, `members`, `leagueId` -- Business Rules: - - Team names unique per league - - Captain must be a team member - - Maximum roster size enforced (default: 10 members) - - Cannot add member already registered with another team in same league -- Invariants: `members` includes `captainId` - -**[`Event`](../src/packages/domain/entities/Event.ts)** (Represents a single race) -- Properties: `id`, `seasonId`, `trackId`, `scheduledAt`, `sessionId` -- Business Rules: - - Events cannot overlap with other events in the same season - - Scheduled time must be in the future (at creation) - - Track must be from iRacing's current season lineup - -**[`Driver`](../src/packages/domain/entities/Driver.ts)** -- Properties: `id`, `iracingId`, `name`, `email`, `licenseClass` -- Business Rules: - - iRacing ID uniqueness globally enforced - - Email verified via iRacing OAuth -- Validation: `iracingId` matches pattern `^\d{5,7}$` - -**[`Result`](../src/packages/domain/entities/Result.ts)** -- Properties: `id`, `eventId`, `driverId`, `position`, `points`, `fastestLap` -- Business Rules: - - Position must match finishing order (no duplicates) - - Points calculated via `ScoringRule` entity - - Cannot modify results after season finalized - -**[`ScoringRule`](../src/packages/domain/entities/ScoringRule.ts)** -- Properties: `id`, `name`, `positionPoints`, `bonusPoints` -- Business Rules: - - Position points must be descending (1st > 2nd > 3rd...) - - Bonus points (fastest lap, pole position) are optional -- Example: F1-style scoring (25, 18, 15, 12, 10, 8, 6, 4, 2, 1) with fastest lap bonus - -### Value Objects - -Value objects are **immutable** and defined by their attributes (no identity). - -**[`Email`](../src/packages/domain/value-objects/Email.ts)** -```typescript -class Email { - constructor(private readonly value: string) { - if (!this.isValid(value)) throw new InvalidEmailError(value); - } - - private isValid(email: string): boolean { - return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); - } - - toString(): string { return this.value; } -} +Inner layers: + Application (Use-cases, Ports) + Domain (Entities, Value Objects, Validators) ``` -**[`IRacingId`](../src/packages/domain/value-objects/IRacingId.ts)** -```typescript -class IRacingId { - constructor(private readonly value: number) { - if (value < 10000 || value > 9999999) { - throw new InvalidIRacingIdError(value); - } - } +**Dependency rule** - toNumber(): number { return this.value; } -} -``` +- Code may only depend **inwards**: + - Presentation → Application → Domain. + - Infrastructure → Application → Domain. +- Inner layers have **no knowledge** of specific UIs, frameworks or transport. -**[`TeamName`](../src/packages/domain/value-objects/TeamName.ts)** -```typescript -class TeamName { - constructor(private readonly value: string) { - if (value.trim().length < 3 || value.length > 50) { - throw new InvalidTeamNameError(value); - } - } +**Layer responsibilities** - toString(): string { return this.value; } -} -``` +- **Domain** + - Competition-neutral automation concepts such as [`AutomationSession`](packages/domain/entities/AutomationSession.ts:1), [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1), session and step invariants and validators. + - Independent of Electron, Playwright, HTTP or persistence. -### Business Invariants (Enforced in Domain) +- **Application** + - Use-cases that express admin intent (start automation, verify auth, confirm checkout) and orchestrate domain entities via ports. + - Defines the automation API that both the companion and any future platform orchestrators use. -1. **No Overlapping Seasons**: A league cannot have two seasons with overlapping start/end dates -2. **Team Roster Limits**: Teams cannot exceed configured maximum members (default: 10) -3. **Driver Uniqueness**: A driver cannot be registered with multiple teams in the same league season -4. **Result Immutability**: Results cannot be modified after season status changes to `FINALIZED` -5. **Scoring Consistency**: All events in a season must use the same scoring rule +- **Infrastructure** + - Adapters that implement ports using concrete technologies such as Playwright, Pino, IPC and in-memory repositories. + - Contains browser automation, DOM helpers, logging and configuration. + +- **Presentation** + - UIs and process hosts that call application use-cases: + - Today: the Electron-based companion app under [`apps/companion`](apps/companion/main/index.ts:1). + - Future: a web app or backend services that invoke the same use-cases via HTTP, IPC or other transports. + +**Key rules in this repo** + +- `packages/domain/*` has no dependencies on application, infrastructure, or Electron. +- `packages/application/*` depends only on `packages/domain/*` (plus shared types). +- `packages/infrastructure/*` depends on domain and application ports, and on external libraries (Playwright, Pino, etc.). +- `apps/companion/*` depends on inner layers and glues application use-cases to Electron UI and IPC. + +This keeps the **automation engine** stable and reusable across presentation surfaces (companion today, web or backend orchestrators later), while allowing infrastructure and UI details to evolve without rewriting business logic. --- -## 5. Application Layer +## 4. Layer-by-Layer Mapping -The application layer orchestrates **use cases** (user intentions) by coordinating domain entities and external services via **port interfaces**. +### 4.1 Domain Layer (`packages/domain/*`) -### Use Case Pattern +The domain models what an “automation session” is, how it progresses, and which invariants must hold regardless of UI or tooling. -All use cases follow a consistent structure: +**Core entities** -```typescript -// Example: CreateLeagueUseCase -export class CreateLeagueUseCase { - constructor( - private readonly leagueRepo: ILeagueRepository, // Port interface - private readonly eventPublisher: IEventPublisher // Port interface - ) {} +- [`AutomationSession`](packages/domain/entities/AutomationSession.ts:1) + - Represents a single hosted-session automation run. + - Tracks: + - `id` + - `currentStep` (wizard step, via [`StepId`](packages/domain/value-objects/StepId.ts:1)) + - `state` (via [`SessionState`](packages/domain/value-objects/SessionState.ts:1)) + - immutable [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1) + - timestamps (`startedAt`, `completedAt`) + - failure information (`errorMessage`). + - Enforces invariants: + - Cannot start unless config is valid and state is pending. + - Steps can only move forward and must progress sequentially (`n → n+1`, no skipping or going backwards). + - Terminal states (e.g. `STOPPED_AT_STEP_18`, `FAILED`) cannot be mutated further. - async execute(input: CreateLeagueDTO): Promise { - // 1. Validation (DTO-level, not domain-level) - this.validateInput(input); +- [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1) + - Describes the admin’s intended hosted session: + - Required: `sessionName`, `trackId`, `carIds`. + - Optional QoL fields: `serverName`, `password`, `adminPassword`, `maxDrivers`, weather/time-of-day, lengths, damage model, track state, etc. + - Acts as the **canonical session description** that higher layers use to map into iRacing’s wizard fields. - // 2. Create domain entity - const league = League.create({ - name: input.name, - ownerId: input.ownerId, - settings: input.settings - }); +- [`StepExecution`](packages/domain/entities/StepExecution.ts:1) + - Encapsulates the result of executing a single wizard step, including timing and error data. - // 3. Persist via repository port - await this.leagueRepo.save(league); +**Value objects** - // 4. Publish domain event (optional) - await this.eventPublisher.publish(new LeagueCreatedEvent(league.id)); +- Session lifecycle and step identity: + - [`SessionState`](packages/domain/value-objects/SessionState.ts:1) + - [`SessionLifetime`](packages/domain/value-objects/SessionLifetime.ts:1) + - [`StepId`](packages/domain/value-objects/StepId.ts:1) - // 5. Return DTO (not domain entity) - return LeagueDTO.fromEntity(league); - } +- Authentication and browser state: + - [`AuthenticationState`](packages/domain/value-objects/AuthenticationState.ts:1) + - [`BrowserAuthenticationState`](packages/domain/value-objects/BrowserAuthenticationState.ts:1) + - [`CookieConfiguration`](packages/domain/value-objects/CookieConfiguration.ts:1) - private validateInput(input: CreateLeagueDTO): void { - // DTO validation (Zod schemas) - if (!input.name || input.name.length < 3) { - throw new ValidationError('League name must be at least 3 characters'); - } - } -} -``` +- Checkout and pricing safety: + - [`CheckoutPrice`](packages/domain/value-objects/CheckoutPrice.ts:1) + - [`CheckoutState`](packages/domain/value-objects/CheckoutState.ts:1) + - [`CheckoutConfirmation`](packages/domain/value-objects/CheckoutConfirmation.ts:1) -### Port Interfaces (Dependency Inversion) +- Overlay/visual integration: + - [`ScreenRegion`](packages/domain/value-objects/ScreenRegion.ts:1) + - [`RaceCreationResult`](packages/domain/value-objects/RaceCreationResult.ts:1) -Ports define **contracts** for external dependencies without specifying implementation. +**Domain services** -**[`ILeagueRepository`](../src/packages/application/ports/ILeagueRepository.ts)** -```typescript -export interface ILeagueRepository { - save(league: League): Promise; - findById(id: string): Promise; - findByOwnerId(ownerId: string): Promise; - delete(id: string): Promise; -} -``` +- [`PageStateValidator`](packages/domain/services/PageStateValidator.ts:1) + - Declaratively describes what it means for the automation to be “on the right wizard page”. + - Evaluates combinations of required/forbidden selectors into a structured result. + - Used by [`PlaywrightAutomationAdapter`](packages/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1) to avoid driving the wrong UI state. -**[`IRacingClient`](../src/packages/application/ports/IRacingClient.ts)** -```typescript -export interface IRacingClient { - authenticate(authCode: string): Promise; - getDriverProfile(iracingId: number): Promise; - getSessionResults(sessionId: string): Promise; -} -``` +- [`StepTransitionValidator`](packages/domain/services/StepTransitionValidator.ts:1) + - Enforces legal transitions between wizard steps and session states. + - Centralizes the rules for when it is safe to move to the next step or finalize. -**[`INotificationService`](../src/packages/application/ports/INotificationService.ts)** -```typescript -export interface INotificationService { - sendEmail(to: string, subject: string, body: string): Promise; - sendPushNotification(userId: string, message: string): Promise; -} -``` +**How this ties back to the concept docs** -### Key Use Cases - -**League Management** -- [`CreateLeagueUseCase`](../src/packages/application/use-cases/CreateLeagueUseCase.ts): Create new league with validation -- [`UpdateLeagueSettingsUseCase`](../src/packages/application/use-cases/UpdateLeagueSettingsUseCase.ts): Modify league configuration -- [`DeleteLeagueUseCase`](../src/packages/application/use-cases/DeleteLeagueUseCase.ts): Archive league (soft delete) - -**Season Management** -- [`CreateSeasonUseCase`](../src/packages/application/use-cases/CreateSeasonUseCase.ts): Define new season with schedule -- [`RegisterDriverForSeasonUseCase`](../src/packages/application/use-cases/RegisterDriverForSeasonUseCase.ts): Handle driver sign-ups -- [`RegisterTeamForSeasonUseCase`](../src/packages/application/use-cases/RegisterTeamForSeasonUseCase.ts): Handle team sign-ups - -**Race Management** -- [`ImportRaceResultsUseCase`](../src/packages/application/use-cases/ImportRaceResultsUseCase.ts): Fetch results from iRacing, calculate points -- [`GenerateStandingsUseCase`](../src/packages/application/use-cases/GenerateStandingsUseCase.ts): Compute driver/team standings -- [`CreateRaceSessionUseCase`](../src/packages/application/use-cases/CreateRaceSessionUseCase.ts): Initiate session creation workflow - -### Dependency Injection Approach - -Use cases receive dependencies via constructor injection: - -```typescript -// DI Container (apps/web-api/di-container.ts) -const container = { - // Infrastructure implementations - leagueRepo: new PostgresLeagueRepository(dbConnection), - iracingClient: new IRacingOAuthClient(config.iracing), - notificationService: new SendGridNotificationService(config.sendgrid), - - // Use cases (dependencies injected) - createLeague: new CreateLeagueUseCase( - container.leagueRepo, - container.eventPublisher - ), - importRaceResults: new ImportRaceResultsUseCase( - container.iracingClient, - container.leagueRepo, - container.eventRepo - ) -}; -``` +- Admin workload reduction: the domain ensures that once configured, the automation moves deterministically through the iRacing wizard (no skipped or repeated steps). +- Safety and trust: session states and validators make failures explicit and traceable, aligning with the transparency goals in [`ADMINS.md`](docs/concept/ADMINS.md) and [`RACING.md`](docs/concept/RACING.md). --- -## 6. Infrastructure Layer +### 4.2 Application Layer (`packages/application/*`) -The infrastructure layer provides **concrete implementations** of port interfaces, handling external service communication. +The application layer orchestrates use-cases by coordinating domain entities and calling ports that are implemented in infrastructure. -### Repository Implementations +**Key ports** -**[`PostgresLeagueRepository`](../src/infrastructure/repositories/PostgresLeagueRepository.ts)** -```typescript -export class PostgresLeagueRepository implements ILeagueRepository { - constructor(private readonly db: Prisma.Client) {} +- [`IAutomationEngine`](packages/application/ports/IAutomationEngine.ts:1) + - `validateConfiguration(config)` → checks whether the intended hosted session is structurally valid and automatable. + - `executeStep(stepId, config)` → drives a single wizard step using the underlying browser automation. + - `stopAutomation()` → halts the current run. - async save(league: League): Promise { - await this.db.league.upsert({ - where: { id: league.id }, - create: this.toDbModel(league), - update: this.toDbModel(league) - }); - } +- [`ISessionRepository`](packages/application/ports/ISessionRepository.ts:1) + - Persists [`AutomationSession`](packages/domain/entities/AutomationSession.ts:1) instances for tracking and later inspection. - async findById(id: string): Promise { - const dbLeague = await this.db.league.findUnique({ where: { id } }); - return dbLeague ? this.toDomainEntity(dbLeague) : null; - } +- Screen & browser integration: + - [`IScreenAutomation`](packages/application/ports/IScreenAutomation.ts:1) (implemented by [`PlaywrightAutomationAdapter`](packages/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1)). + - [`IAuthenticationService`](packages/application/ports/IAuthenticationService.ts:1) for login/session management. + - [`ICheckoutService`](packages/application/ports/ICheckoutService.ts:1) and [`ICheckoutConfirmationPort`](packages/application/ports/ICheckoutConfirmationPort.ts:1) for safe credits/checkout flows. - // Mapping methods (DB model ↔ Domain entity) - private toDbModel(league: League): Prisma.LeagueCreateInput { /* ... */ } - private toDomainEntity(dbLeague: DbLeague): League { /* ... */ } -} -``` +- Cross-cutting: + - [`ILogger`](packages/application/ports/ILogger.ts:1) + - [`IOverlaySyncPort`](packages/application/ports/IOverlaySyncPort.ts:1) + - [`IAutomationEventPublisher`](packages/application/ports/IAutomationEventPublisher.ts:1) + - [`IUserConfirmationPort`](packages/application/ports/IUserConfirmationPort.ts:1) -### External Service Adapters +**Key services** -**[`IRacingOAuthClient`](../src/infrastructure/clients/IRacingOAuthClient.ts)** -```typescript -export class IRacingOAuthClient implements IRacingClient { - constructor(private readonly config: IRacingConfig) {} +- [`OverlaySyncService`](packages/application/services/OverlaySyncService.ts:1) + - Bridges the automation lifecycle (from infrastructure) to overlay/presentation consumers (companion renderer). + - Consumes an [`IAutomationLifecycleEmitter`](packages/infrastructure/adapters/IAutomationLifecycleEmitter.ts:1) (implemented by the Playwright adapter) and publishes state to an overlay sync port. - async authenticate(authCode: string): Promise { - const response = await fetch(this.config.tokenUrl, { - method: 'POST', - body: new URLSearchParams({ - grant_type: 'authorization_code', - code: authCode, - client_id: this.config.clientId, - client_secret: this.config.clientSecret - }) - }); +**Key use-cases** - const data = await response.json(); - return { - accessToken: data.access_token, - refreshToken: data.refresh_token, - expiresIn: data.expires_in - }; - } +- [`StartAutomationSessionUseCase`](packages/application/use-cases/StartAutomationSessionUseCase.ts:1) + - Creates a new [`AutomationSession`](packages/domain/entities/AutomationSession.ts:1) from a [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1). + - Calls [`IAutomationEngine`](packages/application/ports/IAutomationEngine.ts:1) to perform validation before any browser work starts. + - Persists the session via [`ISessionRepository`](packages/application/ports/ISessionRepository.ts:1). + - Returns a DTO that the companion app tracks as progress state. - async getSessionResults(sessionId: string): Promise { - // Fetch from iRacing Data API - const response = await this.authenticatedFetch( - `${this.config.dataApiUrl}/results/${sessionId}` - ); - return this.mapToSessionResults(await response.json()); - } -} -``` +- Authentication: + - [`CheckAuthenticationUseCase`](packages/application/use-cases/CheckAuthenticationUseCase.ts:1) + - [`InitiateLoginUseCase`](packages/application/use-cases/InitiateLoginUseCase.ts:1) + - [`ClearSessionUseCase`](packages/application/use-cases/ClearSessionUseCase.ts:1) + - These map the ToS-compliant login flow described in [`CONCEPT.md`](docs/concept/CONCEPT.md) and [`ADMINS.md`](docs/concept/ADMINS.md) into explicit use-cases (no password handling by GridPilot). -**[`S3AssetStorage`](../src/infrastructure/storage/S3AssetStorage.ts)** -```typescript -export class S3AssetStorage implements IAssetStorage { - constructor(private readonly s3Client: S3Client) {} +- Automation completion & checkout: + - [`CompleteRaceCreationUseCase`](packages/application/use-cases/CompleteRaceCreationUseCase.ts:1) + - [`ConfirmCheckoutUseCase`](packages/application/use-cases/ConfirmCheckoutUseCase.ts:1) + - Uses [`ICheckoutService`](packages/application/ports/ICheckoutService.ts:1) from the Playwright layer to inspect price/state and defers to a UI-side confirmation port to comply with “never surprise-charge the admin”. - async uploadLeagueLogo(leagueId: string, file: Buffer): Promise { - const key = `leagues/${leagueId}/logo.png`; - await this.s3Client.send(new PutObjectCommand({ - Bucket: this.config.bucketName, - Key: key, - Body: file, - ContentType: 'image/png' - })); +- Guarding against stale auth: + - [`VerifyAuthenticatedPageUseCase`](packages/application/use-cases/VerifyAuthenticatedPageUseCase.ts:1) ensures the current page is still authenticated before continuing automation. - return `https://${this.config.cdnUrl}/${key}`; - } -} -``` - -**[`RedisCache`](../src/infrastructure/storage/RedisCache.ts)** -```typescript -export class RedisCache implements ICacheService { - constructor(private readonly redis: Redis) {} - - async get(key: string): Promise { - const value = await this.redis.get(key); - return value ? JSON.parse(value) : null; - } - - async set(key: string, value: T, ttlSeconds: number): Promise { - await this.redis.setex(key, ttlSeconds, JSON.stringify(value)); - } -} -``` - -### Database Schema Considerations - -**PostgreSQL Tables (Simplified)** -```sql --- Leagues table -CREATE TABLE leagues ( - id UUID PRIMARY KEY, - name VARCHAR(255) NOT NULL, - owner_id UUID NOT NULL REFERENCES drivers(id), - settings JSONB, - created_at TIMESTAMPTZ DEFAULT NOW(), - updated_at TIMESTAMPTZ DEFAULT NOW() -); - --- Seasons table (belongs to league) -CREATE TABLE seasons ( - id UUID PRIMARY KEY, - league_id UUID NOT NULL REFERENCES leagues(id) ON DELETE CASCADE, - name VARCHAR(255) NOT NULL, - start_date DATE NOT NULL, - end_date DATE NOT NULL, - status VARCHAR(50) DEFAULT 'UPCOMING', - CONSTRAINT no_overlapping_seasons EXCLUDE USING gist ( - league_id WITH =, - daterange(start_date, end_date, '[]') WITH && - ) -); - --- Teams table -CREATE TABLE teams ( - id UUID PRIMARY KEY, - name VARCHAR(100) NOT NULL, - captain_id UUID NOT NULL REFERENCES drivers(id), - league_id UUID NOT NULL REFERENCES leagues(id), - UNIQUE(league_id, name) -); -``` - -**JSONB Usage** -- `leagues.settings`: Flexible league configuration (scoring rules, roster limits) -- `events.session_metadata`: iRacing session details (weather, track conditions) +The application layer is where “admin intent” (start automation, confirm checkout, verify login) is encoded and exposed to the companion, without exposing Playwright or DOM specifics. --- -## 7. Presentation Layer +### 4.3 Infrastructure Layer (`packages/infrastructure/*`) -The presentation layer exposes the system to users via **Web API**, **Web Client**, and **Companion App**. +The infrastructure layer implements the ports using concrete tools and services. -### Web API (REST/GraphQL) +**Automation adapters** -**HTTP Controllers** translate HTTP requests into use case calls. +- [`PlaywrightAutomationAdapter`](packages/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1) + - Implements: + - [`IScreenAutomation`](packages/application/ports/IScreenAutomation.ts:1) + - [`IAuthenticationService`](packages/application/ports/IAuthenticationService.ts:1) + - [`IAutomationLifecycleEmitter`](packages/infrastructure/adapters/IAutomationLifecycleEmitter.ts:1) used by [`OverlaySyncService`](packages/application/services/OverlaySyncService.ts:1). + - Responsibilities: + - Managing Playwright browser, context, and page lifecycle (headed/headless, fixture vs real mode). + - Navigating to the iRacing hosted-session wizard, login pages and fixtures. + - Driving wizard steps through an internal [`WizardStepOrchestrator`](packages/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1). + - Injecting and updating the on-page automation overlay. + - Enforcing click safety (blocked selectors, checkout detection). + - Handling authentication flows, cookies and persistent sessions via [`SessionCookieStore`](packages/infrastructure/adapters/automation/auth/SessionCookieStore.ts:1) and [`PlaywrightAuthSessionService`](packages/infrastructure/adapters/automation/auth/PlaywrightAuthSessionService.ts:1). + - Validating page state using [`PageStateValidator`](packages/domain/services/PageStateValidator.ts:1). -**[`LeagueController`](../src/apps/web-api/controllers/LeagueController.ts)** -```typescript -export class LeagueController { - constructor( - private readonly createLeagueUseCase: CreateLeagueUseCase, - private readonly getLeagueUseCase: GetLeagueUseCase - ) {} +- [`MockBrowserAutomationAdapter`](packages/infrastructure/adapters/automation/engine/MockBrowserAutomationAdapter.ts:1) + - Lightweight adapter used in tests to simulate automation without real browser interactions. - @Post('/leagues') - @Authenticated() - async createLeague(req: Request, res: Response): Promise { - try { - const dto = CreateLeagueDTO.fromRequest(req.body); - const league = await this.createLeagueUseCase.execute(dto); - res.status(201).json(league); - } catch (error) { - this.handleError(error, res); - } - } +- [`FixtureServer`](packages/infrastructure/adapters/automation/engine/FixtureServer.ts:1) + - Serves static HTML fixtures from `html-dumps-optimized/` to exercise the automation logic deterministically in tests and local development. - @Get('/leagues/:id') - async getLeague(req: Request, res: Response): Promise { - const league = await this.getLeagueUseCase.execute(req.params.id); - if (!league) { - res.status(404).json({ error: 'League not found' }); - return; - } - res.json(league); - } -} -``` +**DOM helpers** -**Middleware Stack** -1. **Authentication**: JWT validation, iRacing OAuth token refresh -2. **Validation**: Zod schema validation for request bodies -3. **Rate Limiting**: Redis-backed throttling (100 req/min per user) -4. **Error Handling**: Centralized error formatting, logging +- [`IRacingDomNavigator`](packages/infrastructure/adapters/automation/dom/IRacingDomNavigator.ts:1) + - High-level navigation and waiting logic for wizard steps and key UI elements. -### Web Client (React SPA) +- [`IRacingDomInteractor`](packages/infrastructure/adapters/automation/dom/IRacingDomInteractor.ts:1) + - Encapsulates filling fields, clicking elements, handling modals. -**Component Architecture** -```typescript -// React component calls use case via API hook -export function CreateLeagueForm() { - const createLeague = useCreateLeague(); // TanStack Query mutation +- [`SafeClickService`](packages/infrastructure/adapters/automation/dom/SafeClickService.ts:1) + - A safer click abstraction that combines timeouts, modal-dismiss logic and safety checks. - const handleSubmit = async (data: CreateLeagueFormData) => { - await createLeague.mutateAsync(data); // Triggers API call - // TanStack Query handles cache invalidation - }; +- Selectors and constants: + - [`IRACING_SELECTORS`](packages/infrastructure/adapters/automation/dom/IRacingSelectors.ts:1) + - Additional helper types under `packages/infrastructure/adapters/automation/dom/*`. - return
{ /* ... */ }
; -} +**Checkout & pricing** -// API hook (wraps HTTP call) -function useCreateLeague() { - return useMutation({ - mutationFn: async (data: CreateLeagueFormData) => { - const response = await fetch('/api/leagues', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(data) - }); - return response.json(); - }, - onSuccess: () => { - queryClient.invalidateQueries(['leagues']); // Refresh league list - } - }); -} -``` +- [`CheckoutPriceExtractor`](packages/infrastructure/adapters/automation/CheckoutPriceExtractor.ts:1) + - Reads DOM labels and buttons in the iRacing wizard to produce: + - [`CheckoutPrice`](packages/domain/value-objects/CheckoutPrice.ts:1) (if parsable). + - [`CheckoutState`](packages/domain/value-objects/CheckoutState.ts:1) (derived from button classes). + - Raw `buttonHtml` for debugging. + - Returns results wrapped in [`Result`](packages/shared/result/Result.ts:1) to avoid throwing during automation. -**State Management** -- **TanStack Query**: Server state (league data, race results) with caching -- **Zustand**: Client state (UI preferences, selected league filter) +**Repositories & configuration** -### Companion App (Electron) +- [`InMemorySessionRepository`](packages/infrastructure/repositories/InMemorySessionRepository.ts:1) + - Simple in-process implementation of [`ISessionRepository`](packages/application/ports/ISessionRepository.ts:1), used in the companion app and tests. -**Main Process (Node.js)** -- Handles IPC from renderer process -- Invokes use cases (directly calls application layer, not via HTTP) -- Manages Playwright browser automation workflows +- Config: + - [`AutomationConfig`](packages/infrastructure/config/AutomationConfig.ts:1) + - Mode selection: `production`, `development`, `test`. + - Default timeouts and base URLs for fixtures vs live. + - [`BrowserModeConfig`](packages/infrastructure/config/BrowserModeConfig.ts:1) + - Controls headed/headless behavior, with [`BrowserModeConfigLoader`](packages/infrastructure/config/BrowserModeConfig.ts:1) used heavily in [`di-container`](apps/companion/main/di-container.ts:1). + - [`LoggingConfig`](packages/infrastructure/config/LoggingConfig.ts:1) -```typescript -// Electron IPC handler -ipcMain.handle('create-iracing-session', async (event, sessionData) => { - const useCase = container.createRaceSessionUseCase; - const result = await useCase.execute(sessionData); +- Logging: + - [`PinoLogAdapter`](packages/infrastructure/adapters/logging/PinoLogAdapter.ts:1) + - [`NoOpLogAdapter`](packages/infrastructure/adapters/logging/NoOpLogAdapter.ts:1) (used in tests to keep noise low). - if (result.requiresAutomation) { - // Trigger Playwright browser automation - await automationService.createSessionInBrowser(result.sessionDetails); - } +**IPC / UI integration** - return result; -}); -``` +- [`ElectronCheckoutConfirmationAdapter`](packages/infrastructure/adapters/ipc/ElectronCheckoutConfirmationAdapter.ts:1) + - Bridges application-level checkout confirmation flows to the companion renderer via IPC. -**Renderer Process (React)** -- UI for session creation, result monitoring -- IPC communication with main process - -**Playwright Browser Automation** ([`PlaywrightAutomationAdapter`](../packages/infrastructure/adapters/automation/PlaywrightAutomationAdapter.ts)) - -GridPilot uses Playwright for all automation tasks. This is the only automation approach—there is no OS-level automation or fallback. - -```typescript -import { chromium, Browser, Page } from 'playwright'; - -export class PlaywrightAutomationAdapter { - private browser: Browser | null = null; - private page: Page | null = null; - - async createSessionInBrowser(details: SessionDetails): Promise { - // 1. Launch browser via Playwright - this.browser = await chromium.launch({ headless: false }); - this.page = await this.browser.newPage(); - - // 2. Navigate to iRacing session creation page - await this.page.goto('https://members-ng.iracing.com/web/racing/hosted/create'); - - // 3. Fill form fields using DOM selectors - await this.page.fill('[data-testid="session-name"]', details.name); - await this.page.selectOption('[data-testid="track-select"]', details.track); - - // 4. Submit form - await this.page.click('[data-testid="create-session-button"]'); - - // 5. Wait for confirmation - await this.page.waitForSelector('[data-testid="session-created-confirmation"]'); - } - - async cleanup(): Promise { - await this.browser?.close(); - } -} -``` - -### Dependency Injection Wiring (Per App) - -Each app wires dependencies in its `di-container.ts`: - -```typescript -// apps/web-api/di-container.ts -export function createDIContainer(config: Config) { - const db = new PrismaClient(); - const redis = new Redis(config.redis); - - // Infrastructure layer - const leagueRepo = new PostgresLeagueRepository(db); - const iracingClient = new IRacingOAuthClient(config.iracing); - - // Application layer - const createLeague = new CreateLeagueUseCase(leagueRepo); - const importResults = new ImportRaceResultsUseCase(iracingClient, leagueRepo); - - return { createLeague, importResults }; -} -``` +This layer is where the “messy reality” of Playwright, Electron, file paths and selectors is implemented, while honoring the constraints defined at the domain/application layers. --- -## 8. Data Flow Example +### 4.4 Presentation Layer (`apps/companion/*`) -### Complete Flow: User Requests League Standings +The presentation layer in this repo is currently a **single Electron app** that provides UI and IPC for admins, but the architecture explicitly anticipates additional presentation surfaces (web app, backend APIs) that reuse the same application and domain layers. -**Step-by-Step:** +**Electron main** -1. **User Action**: Driver navigates to `/leagues/123/standings` in Web Client -2. **React Component**: `` component mounts, triggers TanStack Query -3. **HTTP Request**: `GET /api/leagues/123/standings` -4. **Web API Controller**: `StandingsController.getStandings()` -5. **Use Case Invocation**: `GenerateStandingsUseCase.execute(leagueId: '123')` -6. **Repository Call**: `ILeagueRepository.findById('123')` (port interface) -7. **PostgreSQL Query**: `PostgresLeagueRepository` executes SQL via Prisma -8. **Domain Entity**: Repository returns `League` domain entity -9. **Standings Calculation**: Use case computes standings from `League.events` and `ScoringRule` -10. **DTO Conversion**: Use case returns `StandingsDTO` (serializable) -11. **HTTP Response**: Controller sends JSON to client -12. **React Update**: TanStack Query caches data, component re-renders with standings +- [`index`](apps/companion/main/index.ts) + - Bootstraps the Electron app. + - Creates windows for the renderer. + - Wires process-level lifecycle events and shutdown behavior. -**Sequence Diagram:** -``` -User → Web Client → Web API → Use Case → Repository → PostgreSQL - ↓ - Domain Entity (League) - ↓ - Compute Standings - ↓ - Return DTO - ↓ -User ← Web Client ← Web API ← Use Case -``` +- [`di-container`](apps/companion/main/di-container.ts) + - Central wiring for: + - Logger ([`ILogger`](packages/application/ports/ILogger.ts:1)) via `createLogger()` and [`LoggingConfig`](packages/infrastructure/config/LoggingConfig.ts:1). + - Session repository ([`ISessionRepository`](packages/application/ports/ISessionRepository.ts:1)) via [`InMemorySessionRepository`](packages/infrastructure/repositories/InMemorySessionRepository.ts:1). + - Browser automation adapter via `createBrowserAutomationAdapter()` using [`PlaywrightAutomationAdapter`](packages/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1) or [`MockBrowserAutomationAdapter`](packages/infrastructure/adapters/automation/engine/MockBrowserAutomationAdapter.ts:1) depending on mode. + - [`IAutomationEngine`](packages/application/ports/IAutomationEngine.ts:1) via [`AutomationEngineAdapter`](packages/infrastructure/adapters/automation/engine/AutomationEngineAdapter.ts:1) / [`MockAutomationEngineAdapter`](packages/infrastructure/adapters/automation/engine/MockAutomationEngineAdapter.ts:1). + - Application use-cases: + - [`StartAutomationSessionUseCase`](packages/application/use-cases/StartAutomationSessionUseCase.ts:1) + - [`CheckAuthenticationUseCase`](packages/application/use-cases/CheckAuthenticationUseCase.ts:1) + - [`InitiateLoginUseCase`](packages/application/use-cases/InitiateLoginUseCase.ts:1) + - [`ClearSessionUseCase`](packages/application/use-cases/ClearSessionUseCase.ts:1) + - [`ConfirmCheckoutUseCase`](packages/application/use-cases/ConfirmCheckoutUseCase.ts:1) + - Overlay sync orchestration via [`OverlaySyncService`](packages/application/services/OverlaySyncService.ts:1) and [`IAutomationLifecycleEmitter`](packages/infrastructure/adapters/IAutomationLifecycleEmitter.ts:1). + - Exposes methods like `getStartAutomationUseCase()`, `initializeBrowserConnection()`, `getBrowserModeConfigLoader()`, and `refreshBrowserAutomation()` that are used by IPC handlers and tests. + +- [`ipc-handlers`](apps/companion/main/ipc-handlers.ts) + - Translates renderer IPC calls into use-case invocations: + - `startAutomation` → [`StartAutomationSessionUseCase`](packages/application/use-cases/StartAutomationSessionUseCase.ts:1). + - `checkAuth` / `initiateLogin` / `clearSession` → authentication use-cases. + - Browser connection initialization and overlay sync wiring. + +- [`preload`](apps/companion/main/preload.ts) + - Exposes a typed `window.electronAPI` bridge to the renderer with functions like `startAutomation`, `checkAuth`, `initiateLogin`, `onSessionProgress`, `onCheckoutConfirmationRequest`. + +**React renderer** + +- Root: + - [`App`](apps/companion/renderer/App.tsx) + - Controls high-level UI state: + - Authentication state (`AuthState`) and login flow. + - Current [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1) passed from the session creation form. + - Session progress tracking (step, state, errors). + - Checkout confirmation dialog and race creation success views. + - Talks to Electron via `window.electronAPI`: + - `checkAuth()` and `initiateLogin()` for auth. + - `startAutomation()` and `stopAutomation()` for sessions. + - `onSessionProgress()` to update the right-hand progress monitor. + - `onCheckoutConfirmationRequest()` to show [`CheckoutConfirmationDialog`](apps/companion/renderer/components/CheckoutConfirmationDialog.tsx). + +- Components (selection): + - [`SessionCreationForm`](apps/companion/renderer/components/SessionCreationForm.tsx) + - Collects the data that becomes [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1). + - [`SessionProgressMonitor`](apps/companion/renderer/components/SessionProgressMonitor.tsx) + - Visualizes the current step, completed steps, and error state of the [`AutomationSession`](packages/domain/entities/AutomationSession.ts:1). + - [`LoginPrompt`](apps/companion/renderer/components/LoginPrompt.tsx) + - Renders different messages for `AUTHENTICATED`, `EXPIRED`, `LOGGED_OUT`, `UNKNOWN` states and wires `onLogin`/`onRetry`. + - [`BrowserModeToggle`](apps/companion/renderer/components/BrowserModeToggle.tsx) + - Allows switching between headed/headless behavior in development, talking to the [`BrowserModeConfigLoader`](packages/infrastructure/config/BrowserModeConfig.ts:1) through IPC. + - [`CheckoutConfirmationDialog`](apps/companion/renderer/components/CheckoutConfirmationDialog.tsx) + - Presents a clear, time-bounded dialog where the admin must confirm or cancel a credit purchase-like action. + - [`RaceCreationSuccessScreen`](apps/companion/renderer/components/RaceCreationSuccessScreen.tsx) + - Displays the final result of a successful race creation as modeled by [`RaceCreationResult`](packages/domain/value-objects/RaceCreationResult.ts:1). + +The companion app is intentionally a **single presentation layer implemented in this repo**, but the application and domain layers are structured so that other presentation layers (web client, backend orchestration service) can reuse the same automation engine via the same ports. --- -## 9. Cross-Cutting Concerns +### 4.5 Presentation Surfaces: Web App and Companion -### Logging Strategy +This subsection summarizes how the GridPilot Web App / Website and the companion app relate to the automation engine. -**[`Logger`](../src/packages/shared/logger/Logger.ts)** (Winston wrapper) -```typescript -export class Logger { - log(level: LogLevel, message: string, context?: Record): void { - winston.log(level, message, { ...context, timestamp: new Date() }); - } +**GridPilot Web App / Website (external presentation)** - error(message: string, error: Error, context?: Record): void { - this.log('error', message, { ...context, stack: error.stack }); - } -} -``` +- Primary UI for: + - League and season management, scheduling and calendars. + - Driver and team views, stats, rating, discovery and social features as described in the concept docs. +- Runs in browsers, talking to core platform services over HTTP or GraphQL. +- Produces configuration artifacts such as hosted-session definitions that can be mapped into [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1) on the platform side. -**Usage Across Layers:** -- **Domain**: Logs business rule violations (e.g., "League season overlap detected") -- **Application**: Logs use case execution (e.g., "CreateLeagueUseCase executed for user 123") -- **Infrastructure**: Logs external service calls (e.g., "iRacing API request failed, retrying...") +**GridPilot Companion App (desktop presentation, this repo)** -### Error Handling Patterns +- Operator UI running on the admin PC. +- Owns: + - The relationship with Playwright and the local browser. + - Local configuration, browser mode, fixture usage and overlay rendering. +- Integrates with the automation engine via application use-cases and ports that are **shared** with other potential callers (for example, a core platform automation orchestrator). -**Domain Errors** (Business Rule Violations) -```typescript -export class LeagueSeasonOverlapError extends DomainError { - constructor(leagueId: string) { - super(`League ${leagueId} has overlapping seasons`); - } -} -``` +**Communication pattern between website, platform and companion** -**Application Errors** (Use Case Failures) -```typescript -export class LeagueNotFoundError extends ApplicationError { - constructor(leagueId: string) { - super(`League ${leagueId} not found`, 404); - } -} -``` +- A typical flow for a scheduled league race: + - The web app and core platform services use competition data (league, calendar, formats) to derive a [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1) or equivalent structure server-side. + - The core platform stores and schedules these configs and can expose them via APIs. + - The companion app fetches or receives the relevant configs, calls into the application layer (for example [`StartAutomationSessionUseCase`](packages/application/use-cases/StartAutomationSessionUseCase.ts:1)) and executes them against iRacing through Playwright. + - Status and results flow back through overlays and events, and can be consumed both by the companion UI and by platform-side stats and rating services. -**Infrastructure Errors** (External Service Failures) -```typescript -export class IRacingAPIError extends InfrastructureError { - constructor(message: string, statusCode: number) { - super(`iRacing API error: ${message}`, statusCode); - } -} -``` +- This repo provides: + - The **domain**, **application**, and **infrastructure** for safe, repeatable hosted-session automation. + - A concrete **desktop presentation** in the form of the companion app. -**Error Propagation:** -- Errors bubble up from domain → application → infrastructure/presentation -- Presentation layer translates errors into HTTP status codes or UI messages - -### Validation - -**Two-Level Validation:** - -1. **Domain Validation** (Entity Invariants) -```typescript -class League { - constructor(name: string) { - if (name.length < 3) { - throw new InvalidLeagueNameError(name); // Domain error - } - } -} -``` - -2. **DTO Validation** (Input Sanitization) -```typescript -const CreateLeagueDTOSchema = z.object({ - name: z.string().min(3).max(100), - ownerId: z.string().uuid() -}); - -export class CreateLeagueDTO { - static fromRequest(body: unknown): CreateLeagueDTO { - return CreateLeagueDTOSchema.parse(body); // Throws if invalid - } -} -``` +- The web app and core platform are **separate outer layers** that integrate with this automation slice via APIs and must respect the same dependency direction (only depend inwards on application and domain, not vice versa). --- -## 10. Monorepo Inter-Package Dependencies +## 5. Automation Flow: From Admin Click to iRacing Wizard -### Dependency Rules (DAG Enforcement) +This section describes how a typical hosted-session automation run flows through the layers. -**Directed Acyclic Graph:** -``` -apps/web-api ────┐ -apps/web-client ─┤ -apps/companion ──┘ - ↓ -infrastructure ──┐ - ↓ │ -application ─────┘ - ↓ -domain (no dependencies) - ↑ -shared (utilities only, no domain logic) -``` +### 5.1 High-level sequence -**Import Restrictions (ESLint Plugin):** -```javascript -// .eslintrc.js -module.exports = { - rules: { - 'import/no-restricted-paths': [ - 'error', - { - zones: [ - { target: './src/packages/domain', from: './src/packages/application' }, - { target: './src/packages/domain', from: './src/infrastructure' }, - { target: './src/packages/application', from: './src/infrastructure' }, - { target: './src/packages/application', from: './src/apps' } - ] - } - ] - } -}; -``` +1. **Admin configures a session** -### Build Order Implications + - In the companion UI, the admin fills out [`SessionCreationForm`](apps/companion/renderer/components/SessionCreationForm.tsx). + - The form builds a [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1)-compatible object and calls `window.electronAPI.startAutomation(config)`. -**Compilation Order:** -1. `domain` (no dependencies) -2. `application` (depends on `domain`) -3. `infrastructure` (depends on `application`, `domain`) -4. `apps/*` (depends on all above) +2. **IPC → main process** -**Monorepo Tool Integration:** -- **npm workspaces**: Automatically hoists shared dependencies -- **Turborepo/Nx**: Caches builds, parallelizes independent packages + - The preload script forwards this call to an IPC handler in [`ipc-handlers`](apps/companion/main/ipc-handlers.ts). + - The handler resolves the singleton [`DIContainer`](apps/companion/main/di-container.ts:1) and obtains: + - [`StartAutomationSessionUseCase`](packages/application/use-cases/StartAutomationSessionUseCase.ts:1). + - [`IAutomationEngine`](packages/application/ports/IAutomationEngine.ts:1) and [`ISessionRepository`](packages/application/ports/ISessionRepository.ts:1) behind the scenes. + +3. **Use-case and domain** + + - [`StartAutomationSessionUseCase`](packages/application/use-cases/StartAutomationSessionUseCase.ts:1): + - Creates a new [`AutomationSession`](packages/domain/entities/AutomationSession.ts:1) by calling `AutomationSession.create(config)`, which validates the core invariants (non-empty session name, track, cars). + - Calls `automationEngine.validateConfiguration(config)` to perform more detailed checks (e.g. compatibility with supported wizard flows). + - Persists the new session via [`ISessionRepository`](packages/application/ports/ISessionRepository.ts:1) (implemented by [`InMemorySessionRepository`](packages/infrastructure/repositories/InMemorySessionRepository.ts:1)). + - Returns a DTO to the IPC handler which is forwarded to the renderer. + +4. **Browser and Playwright setup** + + - The [`DIContainer`](apps/companion/main/di-container.ts:1) uses [`AutomationConfig`](packages/infrastructure/config/AutomationConfig.ts:1) and [`BrowserModeConfig`](packages/infrastructure/config/BrowserModeConfig.ts:1) to: + - Decide between `production`, `development`, `test` modes. + - Decide headed vs headless rendering. + - Potentially start a [`FixtureServer`](packages/infrastructure/adapters/automation/engine/FixtureServer.ts:1) in test/fixture modes. + - [`PlaywrightAutomationAdapter`](packages/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1) connects to a browser and ensures the context is ready via `connect()` / `ensureBrowserContext()`. + +5. **Authentication and login** + + - For real iRacing usage: + - [`CheckAuthenticationUseCase`](packages/application/use-cases/CheckAuthenticationUseCase.ts:1) and [`VerifyAuthenticatedPageUseCase`](packages/application/use-cases/VerifyAuthenticatedPageUseCase.ts:1) verify whether the session is still valid. + - If not, the renderer triggers [`InitiateLoginUseCase`](packages/application/use-cases/InitiateLoginUseCase.ts:1) via IPC. + - The Playwright adapter opens a headed browser window, navigates to the login page, and lets the admin log in manually. + - Once login is complete, [`PlaywrightAuthSessionService`](packages/infrastructure/adapters/automation/auth/PlaywrightAuthSessionService.ts:1) stores cookies in [`SessionCookieStore`](packages/infrastructure/adapters/automation/auth/SessionCookieStore.ts:1), and future runs reuse them. + +6. **Step-by-step wizard automation** + + - The automation engine (implemented by [`AutomationEngineAdapter`](packages/infrastructure/adapters/automation/engine/AutomationEngineAdapter.ts:1) and backed by [`PlaywrightAutomationAdapter`](packages/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1)) proceeds through steps: + + - Navigate to hosted sessions. + - Open “Create a Race” and the hosted-session wizard. + - For each step (race information, server details, admins, cars, tracks, weather, race options, conditions): + - Ensure the correct page is active using [`PageStateValidator`](packages/domain/services/PageStateValidator.ts:1) and selectors from [`IRACING_SELECTORS`](packages/infrastructure/adapters/automation/dom/IRacingSelectors.ts:1). + - Fill fields and toggles using [`IRacingDomInteractor`](packages/infrastructure/adapters/automation/dom/IRacingDomInteractor.ts:1). + - Click the correct “Next” / “Create Race” / “New Race” buttons, guarded by [`SafeClickService`](packages/infrastructure/adapters/automation/dom/SafeClickService.ts:1) and blocked-selector logic. + + - At each step, the Playwright adapter: + - Updates the overlay via `updateOverlay(step, message)`. + - Emits lifecycle events consumed by [`OverlaySyncService`](packages/application/services/OverlaySyncService.ts:1), which the companion uses to update [`SessionProgressMonitor`](apps/companion/renderer/components/SessionProgressMonitor.tsx). + +7. **Checkout and confirmation** + + - For flows that involve iRacing credits or non-zero prices: + - [`CheckoutPriceExtractor`](packages/infrastructure/adapters/automation/CheckoutPriceExtractor.ts:1) is used by the Playwright adapter to parse price and state from the wizard UI. + - [`ConfirmCheckoutUseCase`](packages/application/use-cases/ConfirmCheckoutUseCase.ts:1) sends a confirmation request to the renderer via [`ElectronCheckoutConfirmationAdapter`](packages/infrastructure/adapters/ipc/ElectronCheckoutConfirmationAdapter.ts:1). + - [`CheckoutConfirmationDialog`](apps/companion/renderer/components/CheckoutConfirmationDialog.tsx) shows the admin the price and state, with an explicit timeout and cancel path. + - Only after explicit confirmation does the adapter simulate the checkout sequence (and in real mode, additional safeguards ensure no ToS-violating clicks occur). + +8. **Completion and result** + + - Once the final step is reached, [`AutomationSession`](packages/domain/entities/AutomationSession.ts:1) transitions into a terminal state (`STOPPED_AT_STEP_18`, `FAILED`, etc.). + - The companion renderer may present a [`RaceCreationResult`](packages/domain/value-objects/RaceCreationResult.ts:1) via [`RaceCreationSuccessScreen`](apps/companion/renderer/components/RaceCreationSuccessScreen.tsx). + - The browser context is closed or re-used based on mode and configuration; debug artifacts may be written by the Playwright adapter for failed runs. --- -## 11. Extension Points +## 6. How This Serves Admins, Drivers, Teams -### Adding New Scoring Rules (Plugin Architecture) +Although this repo focuses on automation and companion, it is deliberately shaped to support the value promised in the concept docs. -**Current Approach:** -```typescript -class ScoringRule { - calculatePoints(position: number): number { - return this.positionPoints[position] || 0; - } -} -``` +**For admins (see [`ADMINS.md`](docs/concept/ADMINS.md))** -**Future Plugin System:** -```typescript -interface IScoringPlugin { - name: string; - calculatePoints(position: number, context: RaceContext): number; -} +- **Less repetitive work**: + - Hosted-session creation for a league calendar can be scripted once and reused. + - Validation catches inconsistent configs before iRacing errors do. +- **Reliability and safety**: + - Playwright runs in a controlled, tested way, with domain validators and guardrails. + - Checkout logic always goes through a confirmation dialog and blocked selectors. +- **Transparency**: + - Session state (pending, in progress, terminal) and step progress are visible in the companion UI. + - Debug artifacts for failures are generated in a controlled way, enabling root-cause analysis. -// Plugin example: Dynamic scoring based on field size -class DynamicScoringPlugin implements IScoringPlugin { - calculatePoints(position: number, context: RaceContext): number { - const fieldSize = context.numberOfDrivers; - return Math.max(0, fieldSize - position + 1); - } -} +**For drivers and teams (via future platform integration)** -// Register plugin -scoringRegistry.register(new DynamicScoringPlugin()); -``` +- The automation engine is designed so that a future league platform can: -### Integrating Third-Party APIs (New Infrastructure Adapters) + - Generate [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1) from league and calendar data (cars, tracks, formats) defined in the core competition services. + - Trigger automation runs at scheduled times, with the admin supervising in the companion app. + - Use consistent, error-free sessions as the basis for reliable result capture and stats, supporting the narratives in [`DRIVERS.md`](docs/concept/DRIVERS.md), [`TEAMS.md`](docs/concept/TEAMS.md), [`RACING.md`](docs/concept/RACING.md) and [`STATS.md`](docs/concept/STATS.md). -**Example: Discord Notifications** -```typescript -// 1. Define port interface (application layer) -interface IDiscordNotificationService { - sendMessage(webhookUrl: string, message: string): Promise; -} - -// 2. Implement adapter (infrastructure layer) -class DiscordWebhookAdapter implements IDiscordNotificationService { - async sendMessage(webhookUrl: string, message: string): Promise { - await fetch(webhookUrl, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ content: message }) - }); - } -} - -// 3. Wire in DI container -const discordService = new DiscordWebhookAdapter(); -const notifyOnRaceComplete = new NotifyOnRaceCompleteUseCase(discordService); -``` - -### Extending Monetization Features - -**Example: Stripe Payment Integration** -```typescript -// 1. Define port (application layer) -interface IPaymentProcessor { - createPaymentIntent(amount: number, currency: string): Promise; - confirmPayment(paymentIntentId: string): Promise; -} - -// 2. Implement adapter (infrastructure layer) -class StripeAdapter implements IPaymentProcessor { - async createPaymentIntent(amount: number, currency: string): Promise { - const intent = await this.stripe.paymentIntents.create({ amount, currency }); - return intent.id; - } -} - -// 3. Use in use case (application layer) -class ChargeSeasonEntryFeeUseCase { - constructor(private readonly paymentProcessor: IPaymentProcessor) {} - - async execute(seasonId: string, driverId: string): Promise { - const season = await this.seasonRepo.findById(seasonId); - const paymentIntentId = await this.paymentProcessor.createPaymentIntent( - season.entryFee, - 'usd' - ); - // Store payment intent ID, await webhook confirmation - } -} -``` - -### How Clean Architecture Supports Evolution - -1. **New Features**: Add use cases without modifying existing code -2. **Technology Changes**: Swap infrastructure adapters (e.g., Postgres → MongoDB) without touching domain/application layers -3. **Multi-Tenancy**: Introduce tenant isolation at infrastructure layer (row-level security) -4. **API Versioning**: Create new controllers/DTOs while preserving old endpoints +The architecture is intentionally **narrow** but **solid**, so that when the broader competition platform arrives, it can treat this automation module as a trustworthy, ToS-compliant “session engine” for many leagues. --- -## 12. Testing Strategy Integration +## 7. Testing and Validation -For detailed testing strategy, see [`TESTS.md`](./TESTS.md). +Testing is structured to mirror the Clean Architecture layering and the automation flow, without enumerating every individual test file. -### How Architecture Enables Testing +- See [`TESTS.md`](docs/TESTS.md) for detailed structure and conventions; this section summarizes how tests align with the architecture. -**Unit Tests (Domain & Application)** -```typescript -// No mocks needed for pure domain logic -test('League prevents overlapping seasons', () => { - const league = new League({ name: 'Test League' }); - league.addSeason({ startDate: '2025-01-01', endDate: '2025-06-01' }); +**Unit tests** - expect(() => { - league.addSeason({ startDate: '2025-05-01', endDate: '2025-12-01' }); - }).toThrow(LeagueSeasonOverlapError); -}); +- Domain: + - Focus on entities, value objects and validators such as [`AutomationSession`](tests/unit/domain/entities/AutomationSession.test.ts:1) and [`PageStateValidator`](tests/unit/domain/services/PageStateValidator.test.ts:1). + - Ensure that hosted-session invariants and page transition rules hold independently of any UI or automation adapter. -// Mock ports for use case tests -test('CreateLeagueUseCase saves league', async () => { - const mockRepo = mock(); - const useCase = new CreateLeagueUseCase(mockRepo); +- Application: + - Exercise use-cases such as [`StartAutomationSessionUseCase`](tests/unit/application/use-cases/StartAutomationSession.test.ts:1), authentication flows and checkout confirmation coordination. + - Verify that ports are invoked correctly and that domain state transitions are orchestrated as intended. - await useCase.execute({ name: 'Test League', ownerId: '123' }); +- Infrastructure: + - Cover config, logging and key adapters such as [`AutomationConfig`](tests/unit/infrastructure/AutomationConfig.test.ts:1) and [`ElectronCheckoutConfirmationAdapter`](tests/unit/infrastructure/adapters/ElectronCheckoutConfirmationAdapter.test.ts:1). + - Validate Playwright auth/session helpers and DOM-level utilities in isolation. - expect(mockRepo.save).toHaveBeenCalledWith(expect.any(League)); -}); -``` +**Integration tests** -**Integration Tests (Infrastructure)** -```typescript -// Test with real PostgreSQL (Docker Testcontainers) -test('PostgresLeagueRepository persists league', async () => { - const db = await setupTestDatabase(); - const repo = new PostgresLeagueRepository(db); +- Automation flows: + - Validate that Playwright-based adapters, DOM helpers, overlay emitters and repositories work together across the iRacing wizard steps using fixtures. - const league = new League({ name: 'Integration Test League' }); - await repo.save(league); +- Repositories and browser modes: + - Ensure that session persistence and browser mode configuration behave consistently across environments. - const retrieved = await repo.findById(league.id); - expect(retrieved?.name).toBe('Integration Test League'); -}); -``` +- Interface-level flows: + - Exercise IPC wiring and renderer integration to ensure the companion presentation layer correctly drives use-cases and reacts to lifecycle events. -**E2E Tests (Full Stack)** -```typescript -// Test with Playwright (browser automation) -test('User creates league via Web Client', async ({ page }) => { - await page.goto('/leagues/new'); - await page.fill('input[name="name"]', 'E2E Test League'); - await page.click('button[type="submit"]'); +**E2E and smoke tests** - await expect(page).toHaveURL(/\/leagues\/[a-z0-9-]+/); - await expect(page.locator('h1')).toContainText('E2E Test League'); -}); -``` +- E2E: + - Drive full hosted-session workflows end-to-end (fixtures and, when configured, real iRacing) through the companion UI and automation engine. + - Validate that the system behaves correctly from admin input through to iRacing wizard completion. + +- Smoke: + - Provide fast feedback that critical wiring (Electron boot, Playwright initialization, browser-mode toggle) is intact. + +This layered testing approach mirrors the architecture and makes it safe to evolve selectors, overlay UX or Electron wiring without regressing the core admin value of safe, repeatable automation. --- -## Cross-References +## 8. Evolution and Integration with the Competition Platform -- **Business Context**: See [CONCEPT.md](./CONCEPT.md) for problem statement and user journeys -- **Technology Stack**: See [TECH.md](./TECH.md) for framework choices and tooling -- **Testing Details**: See [TESTS.md](./TESTS.md) for comprehensive testing strategy (coming soon) +The concept docs describe a much broader platform: leagues, teams, seasons, stats, rating, social features and structured protests. This section explains how the current automation & companion architecture is designed to plug into that future. + +### 8.1 Integration points + +In a future GridPilot platform: + +- A **core competition service** would own: + - League, season, and race schedules. + - Team and driver registration. + - Points, standings, penalties, stats and rating. + - The competition identity and rating model described in [`STATS.md`](docs/concept/STATS.md) and [`RATING.md`](docs/concept/RATING.md). + +- The **automation engine** in this repo would be used as a **hosted-session execution backend**: + + - The platform would derive [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1) objects from league/season metadata (cars, tracks, formats) defined in the platform. + - It would call into the automation layer via explicit use-cases or IPC/API, using the same ports used by the companion app today. + - The Electron companion may remain the primary operator UI, or an additional headless orchestration mode could be added (still respecting ToS and admin control). + +- A **future rating service** (part of the core platform, not this repo) would: + - Compute GridPilot Rating using league results, incidents, team points and attendance as outlined in [`RATING.md`](docs/concept/RATING.md). + - Treat automation outputs (trusted hosted-session configs, results, and penalty-aware outcomes) as structured inputs from this repo, not as rating logic implemented here. + - Depend on the platform’s stats and competition data model from [`STATS.md`](docs/concept/STATS.md) rather than any in-repo class or service. + +### 8.2 Design decisions that support this evolution + +- **Clean separation of domain vs adapters**: + The automation domain (sessions, steps, validation) is free of Electron/Playwright details, making it straightforward to embed into other hosts. + +- **Ports for automation & auth**: + Using [`IAutomationEngine`](packages/application/ports/IAutomationEngine.ts:1), [`IScreenAutomation`](packages/application/ports/IScreenAutomation.ts:1), [`IAuthenticationService`](packages/application/ports/IAuthenticationService.ts:1) means new drivers (or alternative browser runtimes) can be introduced without changing the use-cases. + +- **Explicit checkout/confirmation path**: + The combination of [`CheckoutPrice`](packages/domain/value-objects/CheckoutPrice.ts:1), [`CheckoutState`](packages/domain/value-objects/CheckoutState.ts:1), [`CheckoutConfirmation`](packages/domain/value-objects/CheckoutConfirmation.ts:1) and the UI-side confirmation port aligns with the transparency and fairness requirements in [`ADMINS.md`](docs/concept/ADMINS.md) and [`RACING.md`](docs/concept/RACING.md). + +- **Overlay and lifecycle emitter**: + The overlay and [`IAutomationLifecycleEmitter`](packages/infrastructure/adapters/IAutomationLifecycleEmitter.ts:1) abstraction make it easy to: + - Drive visual overlays (like those described in admin QoL features) across different frontends. + - Feed telemetry into a central stats/ops UI for debugging, while keeping the engine itself small. + +### 8.3 What this document intentionally does *not* specify + +To stay honest to the current repo and avoid overpromising: + +- There is **no** web API, web client or full league database in this repo. Any such components described in older docs were removed from this architecture description. +- There is **no** implementation of the full complaints/penalties engine, team scoring or cross-league stats here – only the automation slice that can support them later. +- Any mention of those features should be read as **future integration context**, not current implementation. + +When those broader services exist (likely as separate apps/services), they should define their own architecture documents and link back here when describing how they use the hosted-session automation engine. --- -*This architecture documentation will evolve as GridPilot matures. All changes must maintain Clean Architecture principles and the dependency rule.* +## 9. Cross-References -*Last Updated: 2025-11-23* \ No newline at end of file +- Concept and product vision: + - [`CONCEPT.md`](docs/concept/CONCEPT.md) + - [`ADMINS.md`](docs/concept/ADMINS.md) + - [`DRIVERS.md`](docs/concept/DRIVERS.md) + - [`COMPETITION.md`](docs/concept/COMPETITION.md) + - [`RACING.md`](docs/concept/RACING.md) + - [`STATS.md`](docs/concept/STATS.md) + - [`RATING.md`](docs/concept/RATING.md) + - [`SOCIAL.md`](docs/concept/SOCIAL.md) + - [`TEAMS.md`](docs/concept/TEAMS.md) + +- Technology and tests: + - [`TECH.md`](docs/TECH.md) + - [`TESTS.md`](docs/TESTS.md) + +This [`ARCHITECTURE.md`](docs/ARCHITECTURE.md) is the source of truth for how the current repo implements the **automation engine + companion app**, and how that slice is intended to underpin the broader GridPilot competition platform over time. \ No newline at end of file diff --git a/docs/CONCEPT.md b/docs/CONCEPT.md deleted file mode 100644 index af786b420..000000000 --- a/docs/CONCEPT.md +++ /dev/null @@ -1,402 +0,0 @@ -# GridPilot Concept - -## Problem Statement - -iRacing league management today is fragmented and manual: - -- **Communication Chaos**: League organizers juggle Discord channels, Google Sheets, and manual messaging to coordinate everything -- **No Visibility**: Leagues operate in isolation without a central platform for discovery or branding -- **Manual Burden**: Admins spend hours manually entering race results, managing registrations, and creating sessions in iRacing -- **Team Racing Limitations**: No native support for team-based racing with parallel scoring (one driver per car slot, but team accumulates points) -- **Session Creation Pain**: Creating race sessions in iRacing requires tedious browser navigation and form filling -- **Migration Challenges**: Existing leagues can't easily migrate historical data or preserve their identity - -Based on feedback from Reddit and Discord communities, league organizers are overwhelmed by administrative tasks when they'd rather focus on building community and running great racing events. - -## Target Users - -### League Organizers & Admins -**What they need:** -- Automated race result processing -- Easy session creation without manual browser work -- Tools to manage seasons, sign-ups, and standings -- Professional branding and identity for their league -- Custom domains to strengthen league identity -- Migration support to bring existing league history - -### Team Captains -**What they need:** -- Create and manage racing teams -- Register teams for league seasons -- Track team standings alongside driver standings -- Coordinate with team drivers -- View team performance history - -### Solo Drivers -**What they need:** -- Browse and discover active leagues -- Easy registration and sign-up flows -- Personal statistics and race history -- Track standings and points -- Connect with the racing community - -## Core Features - -### For Solo Drivers - -**League Discovery** -- Browse active leagues across different series and skill levels -- Filter by time zones, competitiveness, and racing format -- Join leagues with simple registration flows - -**Personal Racing Stats** -- Automatic race result tracking from iRacing -- Historical performance data -- Personal standings in each league -- Progress tracking across seasons - -### For Teams - -**Team Management** -- Create and name racing teams -- Invite and manage team drivers -- Register teams for league seasons - -**Parallel Racing Format** -- One driver per car slot in each race -- Team points accumulate from all drivers' results -- Both team standings and individual driver standings -- Flexibility for different drivers each race - -**Team Identity** -- Team branding and profiles -- Historical team performance tracking -- Team communication tools - -### For League Organizers - -**League Identity & Branding** -- Professional league pages with custom branding -- Custom domain support (e.g., your-league.racing) -- League logos, colors, and identity -- Public-facing presence for member recruitment - -**Race Management** -- Automated result importing from iRacing -- No manual CSV uploads or data entry -- Session result processing tied to league structure -- Point calculations handled automatically - -**Season Administration** -- Create and manage racing seasons -- Define scoring rules and formats -- Handle sign-ups and registrations -- Configure team vs solo racing formats - -**Authentication & Security** -- Zero-knowledge login: GridPilot never sees or stores your password -- User performs login in visible browser window -- Persistent sessions: login once, stay logged in -- Secure access control for league admin functions - -### Migration Support - -**For Existing Leagues** -- Import historical season data -- Preserve league identity and history -- Maintain continuity for established communities -- Smooth transition without losing context - -## User Journeys - -### Admin Creating a League - -1. Sign in with iRacing credentials -2. Create new league with name and branding -3. Choose racing series and car/track combinations -4. Configure season format (team vs solo, point system) -5. Set up custom domain (optional) -6. Open registration for drivers/teams -7. Publish league page for discovery - -### Team Registering for a Season - -1. Team captain browses available leagues -2. Reviews league format and schedule -3. Registers team for upcoming season -4. Invites or confirms team drivers -5. Receives confirmation and season details -6. Team appears in league roster - -### Driver Viewing Standings - -1. Driver logs into GridPilot -2. Navigates to their league dashboard -3. Views current season standings (team and driver) -4. Reviews recent race results -5. Checks upcoming race schedule -6. Accesses historical performance data - -### Organizer Managing Race Day - -1. Admin creates race session through GridPilot -2. Session automatically appears in iRacing -3. Drivers join and race in iRacing -4. Race completes in iRacing -5. GridPilot automatically imports results -6. Points calculated and standings updated -7. Admin reviews and publishes results -8. Drivers see updated standings immediately - -## Automation Vision - -### Why Browser Automation? - -iRacing doesn't provide public APIs for session creation or comprehensive result data. League admins currently face: -- Repetitive browser navigation to create each race session -- Manual form filling for every session detail -- Time-consuming workflows that scale poorly with league size -- Error-prone manual processes - -### What Automation Solves - -**Session Creation Pain** -- Eliminate manual browser work -- Create sessions from GridPilot with one click -- Batch session creation for full seasons -- Consistent configuration without human error - -**Result Processing** -- Automatic result imports from iRacing -- No manual CSV downloads or uploads -- Real-time standings updates -- Accurate point calculations - -### Assistant-Style Approach - -GridPilot acts as an admin assistant, not a bot: -- Automation runs on admin's behalf with their authorization -- Clear opt-in for automation features -- Admin maintains full control and visibility -- Automation handles tedious tasks, not gameplay - -### Important Boundary - -**We automate admin tasks, not gameplay.** - -GridPilot automates league management workflows - creating sessions, processing results, managing registrations. We never touch actual racing gameplay, driver behavior, or in-race activities. This is administrative automation to free organizers from manual work. - -### iRacing Automation Rules - -Understanding the distinction between iRacing's interfaces is critical: - -**Two iRacing Interfaces:** - -1. **iRacing Website (members.iracing.com)**: This is a standard HTML/DOM web application where hosted session management lives. Being a standard web application, it can be automated with browser automation tools like Playwright. This is 100% legal. - -2. **iRacing Desktop App (Electron)**: The racing simulation itself runs in a sandboxed Electron application. Its DOM is inaccessible, and any modification violates iRacing's Terms of Service. This is why tools like iRefined were shut down. - -**Our Approach:** -- ✅ **Browser automation of the website** - Playwright automates members.iracing.com for session creation, form filling, and data extraction -- ❌ **Never modify the desktop client** - No DOM injection, no script injection, no client modification - -**Why This Matters:** -- iRacing explicitly allows third-party tools that interact with their website -- Client modification (like iRefined did) violates TOS and risks account bans -- Browser automation is reliable, testable, and fully compliant - -## Security & Authentication - -### Zero-Knowledge Login Design - -GridPilot requires access to your iRacing account to automate session creation, but **we never see, store, or transmit your password**. This section explains how we achieve secure authentication while maintaining complete transparency. - -### The Trust Problem - -When an app asks for your credentials, you're trusting that app with your identity. Many apps: -- Store passwords in databases (risk of breach) -- Transmit credentials through their servers (man-in-the-middle risk) -- Have access to your login data internally (insider risk) - -GridPilot takes a fundamentally different approach: **we never have access to your credentials at all**. - -### How GridPilot Authentication Works - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ USER'S COMPUTER │ -│ ┌───────────────┐ ┌─────────────────────────────────┐ │ -│ │ GridPilot │ │ Playwright Browser Window │ │ -│ │ Companion │────────▶│ (Visible to User) │ │ -│ │ App │ │ │ │ -│ │ │ │ ┌─────────────────────────┐ │ │ -│ │ Cannot read │ │ │ iRacing Login Page │ │ │ -│ │ form inputs │ │ │ members.iracing.com │ │ │ -│ │ │ │ │ │ │ │ -│ │ Only detects │ │ │ [Email: *********** ] │ │ │ -│ │ URL changes │ │ │ [Password: ******** ] │ │ │ -│ │ │ │ │ [ Sign In ] │ │ │ -│ └───────────────┘ │ └─────────────────────────┘ │ │ -│ └─────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────┘ - │ - │ HTTPS - ▼ - ┌─────────────────────┐ - │ iRacing Servers │ - │ (Direct connection │ - │ from browser) │ - └─────────────────────┘ -``` - -**Step-by-Step Flow:** - -1. **User clicks "Connect to iRacing"** in GridPilot -2. **A real browser window opens** showing the iRacing login page -3. **User verifies the URL** - it's the real `members.iracing.com` -4. **User enters credentials directly** - GridPilot cannot read input fields -5. **iRacing authenticates the user** - credentials go directly to iRacing -6. **GridPilot detects success** - only by observing the URL change -7. **Browser session is saved locally** - for future automation runs - -### What GridPilot CAN and CANNOT Do - -| Capability | GridPilot Access | -|------------|------------------| -| Read your password | ❌ **Never** | -| See what you type | ❌ **Never** | -| Intercept credentials | ❌ **Never** | -| Know if login succeeded | ✅ By URL change only | -| Use authenticated session | ✅ For automation only | -| Clear your session | ✅ User-initiated logout | - -### Session Persistence - -After your first login, GridPilot saves the browser session locally on your computer: - -- **Location**: Your user data folder (not transmitted anywhere) -- **Contents**: Browser cookies and state (encrypted by OS) -- **Benefit**: You don't need to login every time you use GridPilot -- **Control**: You can sign out anytime to clear the saved session - -**Session Lifecycle:** - -``` -First Use Return Visit Session Expired - │ │ │ - ▼ ▼ ▼ -┌─────────┐ ┌─────────┐ ┌─────────┐ -│ Login │ │ Check │ │ Detect │ -│ Required│ │ Session │ │ Expiry │ -└────┬────┘ └────┬────┘ └────┬────┘ - │ │ │ - ▼ ▼ ▼ -┌─────────┐ ┌─────────┐ ┌─────────┐ -│ Browser │ │ Valid? │───Yes────▶ │ Ready │ -│ Login │ └────┬────┘ │ to Use │ -└────┬────┘ │ No └─────────┘ - │ │ │ - ▼ ▼ ▼ -┌─────────┐ ┌─────────┐ ┌─────────┐ -│ Save │ │ Re-auth │ │ Pause │ -│ Session │ │ Prompt │ │ & Prompt│ -└────┬────┘ └─────────┘ └─────────┘ - │ - ▼ -┌─────────┐ -│ Ready │ -│ to Use │ -└─────────┘ -``` - -### User Verification Points - -At every step, you can verify GridPilot's behavior: - -1. **Browser Window**: You see the real browser - not an embedded frame -2. **URL Bar**: Verify you're on `members.iracing.com` (look for HTTPS lock) -3. **Network Traffic**: Your credentials go directly to iRacing's servers -4. **Input Fields**: GridPilot cannot inject JavaScript to read form values -5. **Logout Control**: You can sign out from GridPilot at any time - -### Session Expiry During Automation - -If your iRacing session expires while automation is running: - -1. **Automation pauses** - no data loss -2. **You're prompted to re-authenticate** - same secure flow -3. **Automation resumes** - from where it left off - -GridPilot never stores credentials to "auto-retry" - you always maintain control. - -### Security Guarantees - -| Guarantee | How We Ensure It | -|-----------|------------------| -| Zero-knowledge | Playwright browser is sandboxed; we cannot inject credential-reading code | -| No transmission | Your login happens directly with iRacing; GridPilot servers never see traffic | -| Local storage only | Session data stays on your computer, encrypted by your OS | -| User control | You can logout, clear sessions, or revoke access anytime | -| Transparency | Browser window is visible; you see exactly what's happening | - -### Comparison to Other Approaches - -| Approach | Password Exposure | Risk Level | -|----------|-------------------|------------| -| **GridPilot (Zero-Knowledge)** | Never exposed | ✅ Minimal | -| OAuth (if iRacing offered it) | Never exposed | ✅ Minimal | -| Password stored in app | App has access | ⚠️ Moderate | -| Password in config file | File has plaintext | ❌ High | - -### Technical Implementation - -For developers interested in the implementation details, see [ARCHITECTURE.md](./ARCHITECTURE.md). Key points: - -- **Playwright BrowserContext**: Manages browser state including cookies -- **Persistent Context**: Saved to `app.getPath('userData')` in Electron -- **Session Validation**: Navigates to protected page; detects login redirects -- **No Credential Ports**: Application layer has no interfaces for password handling - -## Future Vision - -### Monetization Approach - -GridPilot will introduce optional monetization features after the core platform is stable: - -**League Operation Fees** -- Organizers can charge season entry fees -- Both one-time and per-race payment options -- Revenue split between league and GridPilot platform -- Support for league sustainability and prizes - -**Platform Position** -- GridPilot takes a percentage of collected fees -- No fees for free leagues -- Transparent pricing structure -- Revenue supports platform development and hosting - -### When Monetization Arrives - -Monetization features will be added only after: -- Core functionality is proven stable -- User base is established and growing -- League organizers are successfully using the platform -- Feedback confirms value justifies pricing - -The focus now is delivering a great product that solves real problems. Monetization comes later when the platform has earned it. - -### Potential Expansion - -Beyond iRacing, GridPilot's approach could extend to: -- Other sim racing platforms -- Different racing series and formats -- Broader motorsport league management -- Cross-platform racing communities - -But first: nail the iRacing league management experience. - ---- - -GridPilot exists to make league racing accessible and professional for organizers of all sizes, eliminating manual work so communities can focus on what matters: great racing and strong communities. - -*Last Updated: 2025-11-23* \ No newline at end of file diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 54e566b54..7ca4a5628 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,466 +1,289 @@ # GridPilot Implementation Roadmap -## Overview +## 1. Big Picture and Scope -This roadmap provides a phased implementation plan for GridPilot, an automated league management platform for iRacing. Each phase builds upon the previous one, with clear success criteria and actionable todos. +GridPilot is the **competition layer** for iRacing leagues, as described in: -**Purpose:** -- Guide iterative development from technical validation to public launch and monetization -- Track progress through checkable todos -- Validate assumptions before investing in full implementation -- Ensure architectural integrity throughout each phase +- [`CONCEPT.md`](docs/concept/CONCEPT.md) +- [`ADMINS.md`](docs/concept/ADMINS.md) +- [`DRIVERS.md`](docs/concept/DRIVERS.md) +- [`COMPETITION.md`](docs/concept/COMPETITION.md) +- [`RACING.md`](docs/concept/RACING.md) +- [`STATS.md`](docs/concept/STATS.md) +- [`RATING.md`](docs/concept/RATING.md) +- [`SOCIAL.md`](docs/concept/SOCIAL.md) +- [`TEAMS.md`](docs/concept/TEAMS.md) -**How to Use:** -- Check off todos as they are completed (replace `[ ]` with `[x]`) -- Review success criteria before moving to the next phase -- Refer to [ARCHITECTURE.md](./ARCHITECTURE.md) for component boundaries and patterns -- Consult [TESTS.md](./TESTS.md) for testing approach and BDD scenario structure -- See [CONCEPT.md](./CONCEPT.md) for product vision and user needs +Those docs describe the full platform: leagues, seasons, standings, stats, rating, complaints, social, teams, discovery, monetization. + +This **repository** currently implements a **narrow, ToS-safe slice** of that vision: + +- A desktop Electron companion running on the admin’s machine. +- A hosted-session automation engine that drives the iRacing web UI with Playwright. +- Domain and application logic for: + - hosted wizard steps + - authentication and cookie/session reuse + - overlays and lifecycle events + - checkout safety and confirmation. + +For the technical slice implemented here, see: + +- [`ARCHITECTURE.md`](docs/ARCHITECTURE.md) +- [`TECH.md`](docs/TECH.md) +- [`TESTS.md`](docs/TESTS.md) + +Everything else from the concept docs (league/season management, stats, social, complaints, team identity, discovery) is **future or external** to this repo and will live in other services. + +This roadmap is therefore split into two levels: + +- **Automation & Companion Roadmap** – implementation-level, this repo. +- **Core Platform Roadmap** – high-level, future/external services guided by the concept docs. + + +## 2. How to Use This Roadmap + +- Treat Automation & Companion items as work **inside this repo**. +- Treat Core Platform items as **future/external services** that will integrate with this automation slice later. +- Use checklists for near-term Automation & Companion work only. +- Use the concept docs plus [`ARCHITECTURE.md`](docs/ARCHITECTURE.md) as the source of truth for scope boundaries. +- Keep success criteria **testable**, using patterns in [`TESTS.md`](docs/TESTS.md). -**Relationship to MVP:** -- **Phase 0-1:** Pre-MVP validation (technical feasibility, market validation) -- **Phase 2:** MVP (core league management with automated results) -- **Phase 3-4:** Enhanced MVP (automation layer, branding) -- **Phase 5-6:** Public launch and monetization --- -## Phase 0: Foundation (Automation Testing - Internal) +## 3. Automation & Companion Roadmap (This Repo) -**Goal:** Validate technical feasibility of browser automation and establish testing infrastructure. +This track is grounded in the existing code and architecture: -### Infrastructure Setup -- [ ] Initialize monorepo with npm workspaces (`/src/apps`, `/src/packages`) -- [ ] Set up TypeScript configuration (strict mode, path aliases) -- [ ] Configure ESLint and Prettier (no warnings tolerated) -- [ ] Create basic domain models (`League`, `Team`, `Event`, `Driver`, `Result`) -- [ ] Set up test harness (Vitest for unit/integration tests) -- [ ] Configure Docker Compose for E2E testing environment -- [ ] Document development setup in README.md +- Hosted wizard flow and step orchestration (see `tests/e2e/steps/*` and `tests/e2e/workflows/*`). +- Auth and cookie/session management. +- Overlay lifecycle via [`IAutomationLifecycleEmitter`](packages/infrastructure/adapters/IAutomationLifecycleEmitter.ts:1) and [`OverlaySyncService`](packages/application/services/OverlaySyncService.ts:1). +- Checkout safety via [`CheckoutPriceExtractor`](packages/infrastructure/adapters/automation/CheckoutPriceExtractor.ts:1), [`ConfirmCheckoutUseCase`](packages/application/use-cases/ConfirmCheckoutUseCase.ts:1), [`ElectronCheckoutConfirmationAdapter`](packages/infrastructure/adapters/ipc/ElectronCheckoutConfirmationAdapter.ts:1) and the renderer dialog. +- Electron companion UI and IPC wiring. -### Automation Validation -- [ ] Install and configure Playwright for browser automation -- [ ] Test iRacing website navigation and authentication flow -- [ ] Test session creation page detection on members.iracing.com -- [ ] Test session ID extraction from URL or page elements -- [ ] Validate server-side result polling from iRacing API -- [ ] Create proof-of-concept automation script using Playwright -- [ ] Document browser automation approach and iRacing automation rules -- [ ] Identify automation failure modes and mitigation strategies +### Phase A: Solid Hosted-Session Engine & Companion Baseline -### Testing Foundation -- [ ] Write example BDD scenarios (Given/When/Then format) -- [ ] Set up Dockerized E2E test environment -- [ ] Create fixture data for test scenarios -- [ ] Validate test isolation and repeatability -- [ ] Document testing strategy in [TESTS.md](./TESTS.md) +**Goal:** Make the existing hosted-session automation and Electron companion reliable, observable, and easy to run on an admin’s machine. -**Success Criteria:** -- Technical feasibility confirmed (browser automation reliable) -- Test infrastructure operational (unit, integration, E2E) -- Development environment documented and reproducible -- No blockers identified for MVP implementation +**Automation (this repo)** + +- [ ] Stabilize wizard step orchestration: + - [ ] Review and align wizard-step domain rules with [`StepTransitionValidator`](packages/domain/services/StepTransitionValidator.ts:1). + - [ ] Ensure `tests/e2e/steps/*` cover all 18 hosted wizard steps end to end. + - [ ] Harden [`WizardStepOrchestrator`](packages/infrastructure/adapters/automation/core/PlaywrightAutomationAdapter.ts:1) behavior for retries and timeouts. +- [ ] Strengthen page validation: + - [ ] Extend [`PageStateValidator`](packages/domain/services/PageStateValidator.ts:1) to cover edge cases found in real-hosted tests. + - [ ] Ensure selector sets in `packages/infrastructure/adapters/automation/dom/*` match current iRacing UI. +- [ ] Tighten auth/session flows: + - [ ] Verify [`CheckAuthenticationUseCase`](packages/application/use-cases/CheckAuthenticationUseCase.ts:1), [`InitiateLoginUseCase`](packages/application/use-cases/InitiateLoginUseCase.ts:1), and [`VerifyAuthenticatedPageUseCase`](packages/application/use-cases/VerifyAuthenticatedPageUseCase.ts:1) match the constraints in [`CONCEPT.md`](docs/concept/CONCEPT.md) and [`RACING.md`](docs/concept/RACING.md). + - [ ] Confirm cookie handling in `automation/auth/*` matches the lifecycle described in [`ARCHITECTURE.md`](docs/ARCHITECTURE.md). +- [ ] Companion baseline: + - [ ] Ensure the Electron app boots and connects reliably on supported platforms (see smoke tests in `tests/smoke/*`). + - [ ] Keep the renderer minimal but clear: session creation, auth state, progress, checkout confirmation. + +**Success criteria** + +- All unit, integration and E2E tests for existing flows are green (see [`TESTS.md`](docs/TESTS.md)). +- Full hosted-session workflows (fixture-based and real-hosted where enabled) complete without intermittent failures. +- Auth/login flow is ToS-safe, matches the “helper, not cheat” model in [`CONCEPT.md`](docs/concept/CONCEPT.md), and remains visible to the admin. +- Companion can run a full hosted-session creation with no manual DOM clicks beyond login. + + +### Phase B: Overlay & Lifecycle Clarity + +**Goal:** Make the automation lifecycle and overlay behavior predictable and trustworthy for admins. + +**Automation (this repo)** + +- [ ] Lifecycle events: + - [ ] Review events emitted by [`IAutomationLifecycleEmitter`](packages/infrastructure/adapters/IAutomationLifecycleEmitter.ts:1) and consumed by [`OverlaySyncService`](packages/application/services/OverlaySyncService.ts:1). + - [ ] Ensure all critical state transitions of [`AutomationSession`](packages/domain/entities/AutomationSession.ts:1) are reflected in overlay events. +- [ ] Overlay UX: + - [ ] Ensure [`SessionProgressMonitor`](apps/companion/renderer/components/SessionProgressMonitor.tsx:1) clearly maps steps 1–18 to admin-understandable labels. + - [ ] Align overlay messaging with admin QoL themes in [`ADMINS.md`](docs/concept/ADMINS.md) (less repetitive work, more transparency). +- [ ] Error surfacing: + - [ ] Standardize how validation and runtime errors are propagated from domain → application → companion UI. + - [ ] Ensure failures are actionable (what failed, where, and what the admin can retry). + +**Success criteria** + +- Overlay and progress UI always reflect the underlying session state without lag or missing steps. +- Admin can see where automation stopped and why, without reading logs. +- Lifecycle behavior is fully covered in tests (overlay integration, companion workflow E2E), as referenced from [`TESTS.md`](docs/TESTS.md). + + +### Phase C: Checkout Safety Path + +**Goal:** Make every credit/checkout-like action go through an explicit, traceable confirmation path that admins can trust. + +**Automation (this repo)** + +- [ ] Enrich checkout detection: + - [ ] Validate selector logic and price parsing in [`CheckoutPriceExtractor`](packages/infrastructure/adapters/automation/CheckoutPriceExtractor.ts:1) against current iRacing UI. + - [ ] Ensure [`CheckoutState`](packages/domain/value-objects/CheckoutState.ts:1) covers all relevant button states. +- [ ] Harden confirmation logic: + - [ ] Confirm [`ConfirmCheckoutUseCase`](packages/application/use-cases/ConfirmCheckoutUseCase.ts:1) is the *only* entry point for automation that proceeds past a non-zero price. + - [ ] Ensure [`ElectronCheckoutConfirmationAdapter`](packages/infrastructure/adapters/ipc/ElectronCheckoutConfirmationAdapter.ts:1) and [`CheckoutConfirmationDialog`](apps/companion/renderer/components/CheckoutConfirmationDialog.tsx:1) enforce explicit admin confirmation and timeouts. +- [ ] Failure paths: + - [ ] Verify that any parsing failure or ambiguous state results in a safe stop, not a blind click. + - [ ] Add tests to cover “weird but possible” UI states observed via fixtures. + +**Success criteria** + +- No automation path can perform a checkout-like action without an on-screen confirmation dialog. +- All credit-related flows are covered in tests (unit, integration, and companion E2E) with failure-path assertions. +- Behavior matches the safety and trust requirements in [`ADMINS.md`](docs/concept/ADMINS.md) and [`RACING.md`](docs/concept/RACING.md). + + +### Phase D: Additional Hosted Workflows & Admin QoL + +**Goal:** Extend automation beyond the initial hosted-session wizard happy path while staying within the same ToS-safe browser automation model. + +**Automation (this repo)** + +- [ ] Map additional hosted workflows: + - [ ] Identify additional iRacing hosted flows that align with admin QoL needs from [`ADMINS.md`](docs/concept/ADMINS.md) (e.g. practice-only, league-specific hosted sessions). + - [ ] Encode them as configurations on top of [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1) where feasible. +- [ ] Workflow templates: + - [ ] Provide a small set of reusable presets (e.g. “standard league race”, “test session”) that can later be populated by external services. +- [ ] Resilience work: + - [ ] Improve behavior under partial UI changes (selectors, labels) using the validation patterns from [`PageStateValidator`](packages/domain/services/PageStateValidator.ts:1). + +**Success criteria** + +- At least one additional hosted workflow beyond the baseline wizard is supported end to end. +- Admins can choose between a small number of well-tested presets that reflect league use-cases from [`COMPETITION.md`](docs/concept/COMPETITION.md). +- Automation remains fully ToS-safe (no gameplay-affecting automation, no desktop/sim process interference), as reiterated in [`ARCHITECTURE.md`](docs/ARCHITECTURE.md). + + +### Phase E: Operationalization & Packaging + +**Goal:** Make it realistic for real league admins to install, configure and operate the companion and automation engine. + +**Automation (this repo)** + +- [ ] Packaging & configuration: + - [ ] Ensure Electron packaging, browser mode configuration and logging settings match the expectations in [`TECH.md`](docs/TECH.md). + - [ ] Provide a minimal operator-facing configuration story (environment, headless vs headed, fixture vs live). +- [ ] Observability: + - [ ] Ensure logs and failure artifacts are sufficient for debugging issues without code changes. +- [ ] Documentation: + - [ ] Keep operator-focused docs short and aligned with admin benefits from [`ADMINS.md`](docs/concept/ADMINS.md). + +**Success criteria** + +- A technically inclined admin can install the companion, configure automation mode, and run a full hosted-session workflow using only the documentation in this repo. +- Most operational issues can be diagnosed via logs and failure artifacts without code-level changes. +- Existing tests remain the primary safety net for refactors (see [`TESTS.md`](docs/TESTS.md)). -**Note:** This phase is internal validation only—no user-facing features. --- -## Phase 1: Landing Page & Market Validation +## 4. Core Platform Roadmap (Future / External Services) -**Goal:** Validate product-market fit before building the full application. +This track covers the broader GridPilot competition platform from the concept docs. It is **not** implemented in this repo and will likely live in separate services/apps that integrate with the automation engine described in [`ARCHITECTURE.md`](docs/ARCHITECTURE.md). -### Marketing Website -- [ ] Build static marketing website (Next.js or similar) -- [ ] Create compelling copy addressing league organizer pain points -- [ ] Design product mockups and fake screenshots -- [ ] Add email collection form (waitlist integration) -- [ ] Implement privacy policy and terms of service -- [ ] Set up analytics (signups, page views, engagement) +Each phase is intentionally high-level to avoid going stale; details belong in future architecture docs for those services. -### Community Engagement -- [ ] Post to r/iRacing subreddit with mockups -- [ ] Share in iRacing Discord communities -- [ ] Reach out to league organizers directly -- [ ] Collect feedback on pain points and feature requests -- [ ] Conduct user interviews with interested organizers -- [ ] Document feedback in product backlog +### Phase P1: League Identity and Seasons -### Analysis -- [ ] Analyze email signup metrics -- [ ] Review qualitative feedback themes -- [ ] Validate assumptions about organizer pain points -- [ ] Assess willingness to pay (surveys, conversations) -- [ ] Document findings and adjust roadmap if needed +**Core Platform (future/external)** -**Success Criteria:** -- 100+ email signups from target users -- Positive feedback from league organizers -- Validated demand for automated result import -- Confirmed interest in team-based scoring -- Product-market fit assumptions validated +- Provide a clean league home, as described in: + - [`CONCEPT.md`](docs/concept/CONCEPT.md) + - [`ADMINS.md`](docs/concept/ADMINS.md) +- Implement league identity, schedules and season configuration: + - public league pages, schedules, rules, rosters (see sections 3 and 4 in [`CONCEPT.md`](docs/concept/CONCEPT.md)). + - admin tools for creating seasons, calendars, formats (mirroring [`RACING.md`](docs/concept/RACING.md)). +- Model leagues, seasons and events as first-class entities that can later produce [`HostedSessionConfig`](packages/domain/entities/HostedSessionConfig.ts:1) instances for this repo’s automation engine. + +**Success criteria** + +- Leagues can exist, configure seasons and publish schedules independent of automation. +- Competition structure (points presets, drop weeks, team vs solo) matches the expectations in [`COMPETITION.md`](docs/concept/COMPETITION.md). +- There is a clear integration point for calling the automation engine with derived hosted-session configurations (described in the future platform’s own architecture docs). + + +### Phase P2: Results, Stats, Rating v1, and Team Competition + +**Core Platform (future/external)** + +- Result ingestion & standings: + - Implement automated result import and standings as described in [`CONCEPT.md`](docs/concept/CONCEPT.md) and [`STATS.md`](docs/concept/STATS.md). + - Combine imported results into per-season standings for drivers and teams. +- Team system: + - Implement team profiles and constructors-style championships as in [`TEAMS.md`](docs/concept/TEAMS.md) and team sections of [`RACING.md`](docs/concept/RACING.md). +- Stats and inputs for rating: + - Structure league and season stats so that league results, incidents, team points and attendance are reliably captured as described in [`STATS.md`](docs/concept/STATS.md). +- GridPilot Rating v1 (platform-side service): + - Deliver a first usable GridPilot Rating capability consistent with [`RATING.md`](docs/concept/RATING.md), computed entirely in core platform services. + - Treat this repo’s automation slice as a producer of trusted, structured session configs and results; do not move any rating logic into the automation engine. + +**Success criteria** + +- For a league connected to GridPilot, standings and stats update automatically based on iRacing results and provide the inputs required by the rating model in [`RATING.md`](docs/concept/RATING.md). +- Teams and drivers have persistent identity and history across seasons, matching the narratives in [`DRIVERS.md`](docs/concept/DRIVERS.md) and [`TEAMS.md`](docs/concept/TEAMS.md). +- Automation engine in this repo can be treated as a “session executor” feeding reliable results into the platform’s scoring and rating engines, while rating computation remains in Core Platform services. + + +### Phase P3: Complaints, Penalties, Transparency, and Rating Fairness + +**Core Platform (future/external)** + +- Complaint intake: + - Structured complaint flows as defined in [`CONCEPT.md`](docs/concept/CONCEPT.md) and [`RACING.md`](docs/concept/RACING.md) (race, drivers, timestamps, evidence). +- Penalty tools: + - Admin-facing review and decision tools described in [`ADMINS.md`](docs/concept/ADMINS.md) and [`RACING.md`](docs/concept/RACING.md). +- Classification updates: + - Automatic recalculation of results and standings after penalties, aligned with the classification and penalty handling in [`STATS.md`](docs/concept/STATS.md). +- Rating dependencies: + - Ensure that penalty-aware classification, incident handling and transparency from this phase directly feed into GridPilot Rating as incident and season factors, consistent with [`RATING.md`](docs/concept/RATING.md). + - Keep rating computation fully within Core Platform services; this repo continues to provide only the structured competition data that rating consumes. + +**Success criteria** + +- Complaints and penalties are no longer handled via ad-hoc Discord and spreadsheets. +- Standings, stats, histories and rating signals remain consistent and penalty-aware. +- The platform exposes a transparent, auditable record of decisions, supporting the fairness and rating trust goals from the concept docs. + + +### Phase P4: Social, Discovery, and Monetization + +**Core Platform (future/external)** + +- Social and discovery: + - Implement the lightweight social and discovery features from [`SOCIAL.md`](docs/concept/SOCIAL.md) and league/team profile extensions in [`TEAMS.md`](docs/concept/TEAMS.md). +- League and driver discovery: + - Make it easy for drivers to find leagues and teams, and for leagues to find drivers, as described in [`DRIVERS.md`](docs/concept/DRIVERS.md) and [`COMPETITION.md`](docs/concept/COMPETITION.md). +- Monetization (later phase): + - Add monetization and premium features only after the core competition and trust layers are stable, following the MVP philosophy in [`CONCEPT.md`](docs/concept/CONCEPT.md). + +**Success criteria** + +- Drivers, teams and leagues can discover each other through GridPilot, with identity and history driving trust. +- Social features remain lightweight and purpose-driven, complementing community tools like Discord instead of replacing them. +- Any monetization respects the “clarity, fairness, and admin control” principles in the concept docs. -**Note:** No application built in this phase—validation only. Pivot or proceed based on feedback. --- -## Phase 2: MVP (League-Focused) - -**Goal:** Build a functional league management platform with automated result import (no fees, no companion app yet). - -### Authentication & User Management -- [ ] Implement iRacing OAuth authentication flow -- [ ] Create user registration and profile system -- [ ] Build user role system (organizer, driver, spectator) -- [ ] Implement session management and token refresh -- [ ] Write BDD scenarios for authentication flows -- [ ] Achieve test coverage for auth domain - -### League Management (Core Domain) -- [ ] Implement `CreateLeagueUseCase` (see [ARCHITECTURE.md](./ARCHITECTURE.md#application-layer)) -- [ ] Create league CRUD operations (update, delete, archive) -- [ ] Build season setup (tracks, cars, rules configuration) -- [ ] Implement points system configuration (customizable) -- [ ] Create race event scheduling system -- [ ] Write BDD scenarios for league lifecycle -- [ ] Achieve >90% test coverage for `League` aggregate - -### Driver & Team Registration -- [ ] Build driver registration system (join league/season) -- [ ] Implement team registration system (optional parallel scoring) -- [ ] Create team roster management (add/remove drivers) -- [ ] Build approval workflow for registrations -- [ ] Write BDD scenarios for registration flows -- [ ] Test team scoring calculation logic - -### Automated Result Import -- [ ] Implement PostgreSQL schema (repositories pattern) -- [ ] Create server-side iRacing API integration -- [ ] Build automated result polling service -- [ ] Implement result parsing and validation -- [ ] Create `ImportResultUseCase` (see [ARCHITECTURE.md](./ARCHITECTURE.md#application-layer)) -- [ ] Handle edge cases (DNS, penalties, disconnects) -- [ ] Write BDD scenarios for result import -- [ ] Test result import reliability and error handling - -### Standings & Results -- [ ] Generate driver standings (individual points calculation) -- [ ] Generate team standings (parallel scoring model) -- [ ] Build race result pages (lap times, incidents, finishing position) -- [ ] Implement historical standings view (by race) -- [ ] Create standings export functionality (CSV) -- [ ] Write BDD scenarios for standings calculation -- [ ] Test edge cases (ties, dropped races, penalties) - -### League Identity & Admin -- [ ] Build league identity pages (public view) -- [ ] Create basic admin dashboard (organizer tools) -- [ ] Implement league settings management -- [ ] Build schedule and calendar view -- [ ] Create notifications system (race reminders) -- [ ] Write BDD scenarios for admin workflows - -### Quality Assurance -- [ ] Run full test suite (unit, integration, E2E) -- [ ] Achieve >90% test coverage for domain/application layers -- [ ] Perform manual testing with real iRacing data -- [ ] Fix all critical bugs and edge cases -- [ ] Document known limitations - -**Success Criteria:** -- Functional platform for league management -- Automated result import working reliably -- Driver and team standings calculated correctly -- No manual result uploads required -- Test coverage >90% for core domain -- Ready for closed beta testing - -**Note:** No fees, no payouts, no companion app in this phase. Focus on core league management. - -**Cross-References:** -- See [ARCHITECTURE.md](./ARCHITECTURE.md) for component boundaries -- See [TESTS.md](./TESTS.md) for BDD scenario examples - ---- - -## Phase 3: Companion App (Automation Layer) - -**Goal:** Build an Electron companion app to automate session creation and reduce organizer workload. - -### Companion App Foundation -- [ ] Set up Electron application structure -- [ ] Implement Playwright browser automation framework -- [ ] Create IPC bridge for backend communication -- [ ] Build auto-updater mechanism -- [ ] Set up application signing and packaging -- [ ] Document installation and setup process - -### Session Creation Automation -- [ ] Build session creation assistance workflow -- [ ] Implement iRacing session page detection -- [ ] Create session ID extraction mechanism -- [ ] Build form auto-fill functionality (track, cars, rules) -- [ ] Implement session URL capture and sync -- [ ] Handle automation failure cases gracefully -- [ ] Write E2E tests for automation flows - -### OAuth & Credential Handoff -- [ ] Implement OAuth handoff from companion to web -- [ ] Create secure credential storage (encrypted) -- [ ] Build IPC bridge for authentication state -- [ ] Handle token refresh in companion app -- [ ] Write E2E tests for OAuth handoff flow -- [ ] Test cross-process credential security - -### Organizer Utilities -- [ ] Create session creation guidance (step-by-step) -- [ ] Build pre-race checklist functionality -- [ ] Implement session status monitoring -- [ ] Add quick access to league settings -- [ ] Create notifications for upcoming races - -### Testing & Reliability -- [ ] Test session creation automation reliability (>95% success rate) -- [ ] Validate automation across different iRacing UI versions -- [ ] Handle iRacing website changes gracefully -- [ ] Create fallback mechanisms for automation failures -- [ ] Document troubleshooting guide - -**Success Criteria:** -- Companion app reduces session creation time by 80%+ -- Automation success rate >95% -- OAuth handoff secure and seamless -- Auto-updater working reliably -- Comprehensive E2E test coverage - -**Note:** Companion app is optional but highly valuable for organizers. Focus on reliability over features. - ---- - -## Phase 4: Branding & Public Pages - -**Goal:** Enable professional league identity and public discoverability. - -### Asset Management -- [ ] Implement S3-compatible asset storage (logos, images) -- [ ] Add league logo upload functionality -- [ ] Create image optimization pipeline -- [ ] Implement asset CDN integration -- [ ] Build asset management UI (upload, delete, replace) - -### Custom Branding -- [ ] Create custom CSS/theming system (colors, fonts) -- [ ] Build theme preview functionality -- [ ] Implement logo placement customization -- [ ] Add custom header/footer options -- [ ] Create branding guidelines documentation - -### Public League Directory -- [ ] Build public league directory (browse and discover) -- [ ] Implement search and filtering (game type, region, skill level) -- [ ] Create league detail pages (public view) -- [ ] Add league statistics (active seasons, drivers, races) -- [ ] Implement privacy settings (public/private leagues) - -### External Integrations -- [ ] Implement optional custom domain support (CNAME) -- [ ] Create embeddable widgets (standings iframe, schedule) -- [ ] Add Discord/TeamSpeak integration links -- [ ] Implement YouTube/Twitch VOD linking (external only, no uploads) -- [ ] Build social sharing functionality (Twitter, Reddit) - -### Public Result Pages -- [ ] Create public race result pages (shareable links) -- [ ] Build driver profile pages (career statistics) -- [ ] Implement team profile pages (roster, history) -- [ ] Add historical standings archive -- [ ] Create race replay link integration (if available) - -### Testing & Documentation -- [ ] Write BDD scenarios for branding features -- [ ] Test public pages with various league configurations -- [ ] Validate custom domain setup process -- [ ] Create user guide for branding customization -- [ ] Test embeddable widgets in external sites - -**Success Criteria:** -- Leagues have professional identity and branding -- Public directory drives league discovery -- Custom domains working reliably -- Embeddable widgets functional -- External integrations (Discord, Twitch) operational - -**Note:** Branding features are optional but enhance league professionalism and discoverability. - ---- - -## Phase 5: Public Launch - -**Goal:** Launch GridPilot publicly with production-grade infrastructure and stability. - -### Security & Compliance -- [ ] Perform security audit (OAuth, credentials, API security) -- [ ] Implement rate limiting and DDoS protection -- [ ] Add CSRF and XSS protection -- [ ] Conduct penetration testing -- [ ] Review GDPR compliance (user data handling) -- [ ] Implement data export functionality (user request) -- [ ] Create incident response plan - -### Performance & Scalability -- [ ] Load testing and performance optimization -- [ ] Implement database query optimization -- [ ] Add caching layers (Redis for sessions, API responses) -- [ ] Configure CDN for static assets -- [ ] Optimize Docker images for production -- [ ] Set up horizontal scaling strategy - -### Production Infrastructure -- [ ] Set up production hosting (AWS/GCP/Azure) -- [ ] Configure production database (PostgreSQL with replication) -- [ ] Implement database backup strategy (automated, tested) -- [ ] Set up monitoring and alerting (logs, errors, uptime) -- [ ] Configure error tracking (Sentry or similar) -- [ ] Implement log aggregation and analysis -- [ ] Create disaster recovery plan - -### Documentation & Support -- [ ] Write comprehensive user documentation -- [ ] Create organizer onboarding guide -- [ ] Build driver user guide -- [ ] Document API endpoints (if public) -- [ ] Create FAQ and troubleshooting guide -- [ ] Set up support system (email, Discord) - -### Launch Preparation -- [ ] Prepare launch marketing materials -- [ ] Coordinate Reddit/Discord announcements -- [ ] Create launch video/demo -- [ ] Set up social media presence -- [ ] Prepare press kit (if applicable) -- [ ] Plan launch timeline and milestones - -### Beta Onboarding -- [ ] Onboard first 10 beta leagues (closed beta) -- [ ] Collect feedback from beta users -- [ ] Fix critical bugs identified in beta -- [ ] Validate production stability under real load -- [ ] Document lessons learned - -**Success Criteria:** -- Platform publicly available and stable -- Security audit passed with no critical issues -- Production infrastructure operational -- Monitoring and alerting functional -- User documentation complete -- First 10+ leagues successfully onboarded -- Platform stable under real-world load - -**Note:** Public launch is a major milestone. Ensure stability and security before opening access. - ---- - -## Phase 6: Monetization & Expansion - -**Goal:** Generate revenue and expand platform capabilities. - -### Monetization Features -- [ ] Implement league creation fee system -- [ ] Add optional driver entry fee per season -- [ ] Build revenue split mechanism (organizer/GridPilot) -- [ ] Create billing and invoicing system -- [ ] Implement payment processing (Stripe or similar) -- [ ] Add subscription management (for premium features) -- [ ] Create payout system for organizers -- [ ] Implement refund and dispute handling - -### Premium Features -- [ ] Create premium league features (advanced analytics) -- [ ] Build driver/team performance metrics over time -- [ ] Implement historical trend analysis -- [ ] Add advanced race strategy tools -- [ ] Create custom report generation -- [ ] Build league comparison and benchmarking - -### Analytics & Insights -- [ ] Add analytics dashboards for leagues -- [ ] Implement driver consistency metrics -- [ ] Create incident rate analysis -- [ ] Build lap time comparison tools -- [ ] Add race pace analysis -- [ ] Implement predictive performance modeling - -### Platform Expansion -- [ ] Explore expansion to other simulators (ACC, rFactor 2) -- [ ] Evaluate additional automation features -- [ ] Research multi-game league support -- [ ] Investigate community-requested features -- [ ] Assess partnership opportunities (teams, sponsors) - -### Business Intelligence -- [ ] Implement revenue tracking and reporting -- [ ] Create user engagement metrics -- [ ] Build churn analysis and retention tools -- [ ] Add A/B testing framework -- [ ] Implement feature adoption tracking - -**Success Criteria:** -- Revenue generation active and growing -- Premium features adopted by target segment -- Payment processing reliable and secure -- Organizer payouts working correctly -- Platform expansion feasibility validated -- Positive unit economics demonstrated - -**Note:** Monetization should not compromise core user experience. Ensure value delivery justifies pricing. - ---- - -## Dependencies & Sequencing - -**Critical Path:** -1. Phase 0 must be completed before any development begins -2. Phase 1 validation should gate investment in Phase 2 -3. Phase 2 MVP is required before Phase 3 (companion app depends on API) -4. Phase 4 can be developed in parallel with Phase 3 (independent features) -5. Phase 5 (public launch) requires Phases 2-4 to be complete and stable -6. Phase 6 (monetization) requires Phase 5 (public user base) - -**Optional Paths:** -- Phase 3 (companion app) can be delayed if organizers tolerate manual session creation -- Phase 4 (branding) can be simplified for MVP launch -- Phase 6 features can be prioritized based on user demand - -**Iteration Strategy:** -- Complete each phase fully before moving to the next -- Validate success criteria before proceeding -- Adjust roadmap based on feedback and learnings -- Maintain architectural integrity throughout - ---- - -## Living Document - -This roadmap is a living document and will be updated as the project evolves. Key updates will include: -- Completed todos (checked off) -- Lessons learned from each phase -- Adjusted priorities based on user feedback -- New features discovered during development -- Changes to success criteria or scope - -**Maintenance:** -- Review and update quarterly (or after each phase) -- Archive completed phases for reference -- Document deviations from original plan -- Track velocity and estimate remaining work - -**Cross-References:** -- [CONCEPT.md](./CONCEPT.md) - Product vision and user needs -- [ARCHITECTURE.md](./ARCHITECTURE.md) - Technical design and component boundaries -- [TESTS.md](./TESTS.md) - Testing strategy and BDD scenarios -- [TECH.md](./TECH.md) - Technology decisions and rationale - ---- - -**Last Updated:** 2025-11-23 -**Current Phase:** Phase 0 (Foundation) -**Overall Progress:** In Progress (browser automation implemented) \ No newline at end of file +## 5. Dependencies and Evolution + +- Automation & Companion phases (A–E) are largely **independent** of Core Platform phases and can be completed first. +- Core Platform phases (P1–P4) depend on: + - A stable automation engine and companion (this repo). + - Clear APIs/IPC or integration contracts that should be documented in future platform services, referencing [`ARCHITECTURE.md`](docs/ARCHITECTURE.md). +- The automation slice should remain **small and robust**, so that multiple future services can treat it as a shared “session engine.” + +Use this roadmap as a **living, checkable** guide: + +- Update checklists under Automation & Companion as work lands. +- Keep Core Platform phases at the level of concept alignment, not implementation detail. +- When new services are built, they should introduce their own roadmaps and link back to: + - [`CONCEPT.md`](docs/concept/CONCEPT.md) and related concept docs. + - [`ARCHITECTURE.md`](docs/ARCHITECTURE.md) for the automation slice. + - [`TESTS.md`](docs/TESTS.md) for testing strategy and coverage expectations. + +**Last Updated:** 2025-12-01 +**Tracks:** Automation & Companion (this repo) / Core Platform (future/external) +**Current Focus:** Phase A (Solid hosted-session engine & companion baseline) \ No newline at end of file diff --git a/docs/concept/ADMINS.md b/docs/concept/ADMINS.md new file mode 100644 index 000000000..46b96f1cc --- /dev/null +++ b/docs/concept/ADMINS.md @@ -0,0 +1,236 @@ +# GridPilot for Admins +*Professional tools that eliminate the chaos of running an iRacing league — without changing how your community works.* + +--- + +## 1. Why Admins Need GridPilot + +Running a league today means juggling: +- Discord threads +- Google Sheets +- CSVs +- DMs +- PDFs +- manual points +- manual sign-ups +- manual session creation +- messy protest handling + +Meanwhile, admins just want to: +- run good races +- keep drivers happy +- keep things fair +- and not drown in repetitive work + +GridPilot gives admins the structure and tools iRacing never provided. + +--- + +## 2. League Identity & Presentation + +Admins get a clean, modern league homepage — instantly. + +### Includes: +- league logo + colors +- season schedule +- standings (driver + team) +- team list +- driver roster +- results archive +- links to Discord, YouTube, Twitch + +### Optional: +- custom subdomain (e.g. *yourleague.racing*) + +This makes small leagues feel **professional** without any technical effort. + +--- + +## 3. Season Management + +A complete toolkit to organize seasons without spreadsheets. + +### Admins can: +- define the league format +- create calendars (tracks, car classes) +- configure points systems +- define drop weeks +- choose team vs solo format +- set rules & info pages +- open/close registration +- manage rosters + +Everything stays in one clean UI. + +--- + +## 4. Driver & Team Sign-ups + +Forget Discord forms and DMs. + +### GridPilot handles: +- driver sign-ups +- team registrations +- automatic roster generation +- driver availability status +- team composition +- waitlist management + +Admins no longer need to chase people for info. + +--- + +## 5. Automatic Results Import + +One of the biggest pains in league racing — **gone**. + +GridPilot automatically imports results from iRacing: + +### Extracts: +- finishing positions +- incident count +- best laps +- lap-by-lap data +- gained/lost positions +- SoF (if available) + +### Updates: +- driver standings +- team standings +- season points +- drop-week applied points + +Admins don’t upload CSVs or calculate anything manually again. + +--- + +## 6. Complaints & Penalties + +A structured workflow to replace protest chaos. + +### Drivers can submit complaints: +- select the race +- select involved drivers +- add timestamps +- add explanations +- optionally add short clips + +### Admins get: +- a review dashboard +- quick decision tools: + - no action + - warning + - time penalty + - points deduction + - DQ for the race + +### GridPilot updates everything automatically: +- recalculates standings +- adjusts team points +- logs penalties visibly + +Admins stay in full control — GridPilot just removes the manual work. + +--- + +## 7. Optional: Session Creation QoL + +Automation is *optional* — but powerful for admins who want it. + +### One-click creation of: +- practice sessions +- race sessions +- full season batches + +### Benefits: +- no repetitive form filling +- consistent settings +- saves hours every season +- always under admin control + +GridPilot acts as an **assistant**, not a bot. +Admins keep full responsibility. + +--- + +## 8. Roster & Driver Management + +Admins can: +- view complete roster +- assign drivers to teams +- manage substitutes +- approve/reject sign-ups +- track attendance +- see driver stats for performance evaluation + +Helps maintain a healthy community and balanced competition. + +--- + +## 9. Transparency & Trust + +GridPilot creates structure admins normally have to enforce manually: + +- clear point systems +- visible penalties +- visible rules +- logged decisions +- consistent standings +- predictable schedules +- no lost messages or hidden complaints + +This removes accusations of favoritism or unfairness. + +Admins gain **presence**, not stress. + +--- + +## 10. Zero Technical Overhead + +No coding. +No servers. +No spreadsheets. +No HTML. +No spreadsheets. +No setup. + +Admins simply: +1. create a league +2. configure the season +3. share the league link +4. run the races +5. GridPilot handles the rest + +--- + +## 11. What GridPilot Is *Not* for Admins + +- not a replacement for your Discord community +- not a stewarding system (no live stewarding) +- not intrusive automation +- not a tool that changes racing behavior +- not a way to manipulate results + +We respect the league → we just make it manageable. + +--- + +## 12. Summary + +GridPilot gives admins: + +- **professional presentation** +- **zero manual points work** +- **automatic standings** +- **clean sign-ups** +- **structured complaints** +- **quick penalties** +- **optional session automation** +- **roster tools** +- **driver & team identity** +- **season organization** + +It lets admins focus on racing, not paperwork. + +GridPilot = +**less chaos, more competition.** \ No newline at end of file diff --git a/docs/concept/COMPETITION.md b/docs/concept/COMPETITION.md new file mode 100644 index 000000000..f3be4a65c --- /dev/null +++ b/docs/concept/COMPETITION.md @@ -0,0 +1,283 @@ +# GridPilot Competition & Unique Selling Points +*Why GridPilot matters — and why it fills the biggest gap in iRacing league racing.* + +--- + +# 1. What iRacing Leagues Are Missing + +iRacing does many things extremely well: +- great cars +- great physics +- huge official participation +- excellent netcode + +But iRacing does **not** provide: +- long-term driver identity +- league structure +- team championships +- personalized stats +- season history +- league discovery +- automated standings +- protest/penalty workflows +- a centralized competition ecosystem + +Everything around league racing is **outsourced to Discord chaos**. + +GridPilot fixes the layer iRacing ignores. + +--- + +# 2. The Competition Layer iRacing Never Built + +GridPilot becomes the competition universe for league racing: + +### **Drivers get** +- a racing identity +- a real profile that spans leagues +- season standings that matter +- team championships +- points that persist +- stats that make progress visible + +### **Admins get** +- reliable standings +- structured schedules +- automated results +- clean sign-up flows +- real competition formats + +### **Leagues get** +- a professional home +- discoverability +- branding +- stable structure + +This is something **no existing tool** provides in a unified way. + +--- + +# 3. What Others Do (and Why It’s Not Enough) + +### **SimRacerHub** +- ancient UI +- manual CSV uploads +- no identity +- no automation +- no team competition +- no discovery +- no long-term profiles + +### **Discord + Google Sheets** +- disorganized +- non-scalable +- everything is manual +- error-prone +- no identity +- no automation +- no trust system +- no visibility + +### **iRacing Official Series** +- random grids +- no personal connection +- no ladder +- no league-style competition +- no teams (beyond 1-car teams) +- no season identity + +### **SimGrid (ACC)** +- not for iRacing +- no automation +- not deeply connected to semiprofessional league structures + +### **LFM** +- matchmaking, not league management +- no season identity +- not for iRacing +- no team championship +- no admin tooling + +**Conclusion:** +There is *no* product that unifies the league racing ecosystem for iRacing. + +GridPilot is literally the **first** to do this properly. + +--- + +# 4. What GridPilot Does That Nobody Else Can + +## **1. Clean, automatic league infrastructure** +- standings +- results +- rosters +- seasons +- scoring +- team formats + +Nobody else automates this end-to-end. + +--- + +## **2. Real Constructors-Style Team Championships** +iRacing has: +- only one driver per car +- no parallel team scoring +- no true team competition + +GridPilot: +- multiple drivers race for one team +- all points count +- teams gain prestige across seasons + +This completely changes how league racing feels. + +--- + +## **3. Unified Driver Identity Across Leagues** +Drivers currently: +- disappear between seasons +- have no home +- no stats +- no history + +GridPilot gives them: +- permanent stats +- history +- standings +- cross-league progression +- consistent profile + +This becomes the **center of their virtual racing career**. + +--- + +## **4. Structured Complaints & Penalties** +Other platforms: +- use DMs +- use Discord +- lose messages +- inconsistent punishment +- emotional chaos + +GridPilot: +- structured forms +- timestamps +- clear penalty tools +- automatic standings update + +This makes leagues feel *fair* — a huge USP. + +--- + +## **5. Season creation without busywork** +Admins normally spend hours on: +- session creation +- spreadsheets +- messaging + +GridPilot: +- automates results +- simplifies sessions +- cleans up registration +- centralizes everything + +It’s the **ultimate league admin QoL platform**. + +--- + +# 5. Why Drivers Should Sign Up + +**Because racing finally means something.** + +GridPilot gives drivers: +- season standings +- long-term stats +- team championships +- identity +- clarity +- professionalism +- an organized home for their racing + +No more guessing, no more chaos — a place to grow and compete. + +--- + +# 6. Why Admins Should Use GridPilot + +**Because running a league shouldn’t be a full-time job.** + +GridPilot: +- removes the boring work +- automates the repetitive tasks +- enforces structure +- keeps standings clean +- removes spreadsheet pain +- prevents drama +- makes their league look professional + +Admins feel supported instead of overwhelmed. + +--- + +# 7. Why GridPilot Will Become THE Platform + +GridPilot combines: + +- **admin tools** +- **driver identity** +- **team championships** +- **competition structure** +- **automated results** +- **clear season organization** +- **discovery & visibility** + +This creates a **complete ecosystem**, not just a tool. + +When multiple leagues use it → drivers collect universal stats. +When drivers collect stats → teams form around them. +When teams form → prestige and competition grow. +When prestige grows → more leagues join. + +This creates a **network effect** no other platform has. + +--- + +# 8. Simple Value Summary (ideal for landing page) + +### For Drivers: +- real competition +- season identity +- long-term stats +- team championships +- one place for all leagues + +### For Admins: +- less work +- no spreadsheets +- clean sign-ups +- automatic standings +- proper penalty tools +- professional league branding + +### For the Community: +- a central home +- simple discovery +- fair competition +- consistent rules +- real history + +--- + +# 9. Why You Should Sign Up Early + +Because: +- early leagues define the meta +- early drivers get full season history +- early admins help shape the platform +- you get modern tools for free during beta +- you’re part of the first proper iRacing league ecosystem +- you want league racing with meaning, structure, and clarity + +GridPilot is the **competition layer iRacing never built — +and the league ecosystem drivers always wanted.** \ No newline at end of file diff --git a/docs/concept/CONCEPT.md b/docs/concept/CONCEPT.md new file mode 100644 index 000000000..e01183f7f --- /dev/null +++ b/docs/concept/CONCEPT.md @@ -0,0 +1,232 @@ +# GridPilot — The Competition Platform for iRacing Leagues +*A modern, lightweight platform that gives iRacing league racing structure, identity, and long-term meaning — without taking control away from communities.* + +--- + +# 1. What GridPilot Solves + +iRacing’s strength is racing quality, not league infrastructure. +Today, league racing suffers from: + +- scattered tools (Discord, Sheets, Forms, DMs) +- zero unified identity for leagues +- heavy admin workload for every race +- no automatic standings or result sync +- no long-term driver identity or career stats +- no proper team-based competition +- unstructured protest handling (DMs, screenshots, chaos) + +GridPilot becomes the **competition layer** above iRacing. +We don’t change racing — we fix everything around it. + +--- + +# 2. Who GridPilot Is For + +## League Admins +Need: +- easy season setup +- automatic result handling +- clean standings +- a structured complaints/penalty workflow +- predictable points systems +- modern league presentation +- simple driver/team signups +- optional session-creation QoL automation + +## Drivers +Need: +- easy league discovery +- simple registration +- clean standings +- reliable schedule +- personal stats and history +- long-term progression +- team + individual competition +- structured way to file complaints + +Admin + Driver = **GridPilot’s only focus for MVP**. + +--- + +# 3. Core Features (MVP) + +## 3.1 League Identity +- clean public league page +- schedule, standings, roster, rules +- league branding (logo, colors) +- optional custom domain +- links to Discord / YouTube / Twitch + +## 3.2 Season Management +- full calendar for each season +- point system presets +- drop weeks +- team or solo mode +- automatic standings + +## 3.3 Driver & Team System +### Drivers: +- league join flow +- personal stats (per race + lifetime) +- progression + history +- cross-league identity (v1 basic) + +### Teams: +- create/manage teams +- team roster management +- real “Constructors Championship” parallel scoring +- team standings + history + +(This is a unique selling point: **real team racing in iRacing leagues**.) + +--- + +# 4. Complaints & Penalties (Essential) + +A structured system to replace Discord DMs and random drama. + +## 4.1 Driver Complaints +Drivers can file a complaint with: +- race +- involved drivers +- timestamp(s) +- description +- optional replay clip / link + +Keeps everything organized and searchable. + +## 4.2 Admin Penalty Tools +Admins get a review panel: +- complaint list +- timestamps + notes +- direct links to replays +- quick actions: + - no action + - warning + - time penalty + - points deduction + - DQ for race + +## 4.3 Automatic Standings Update +When a penalty is assigned: +- driver points update +- team points update +- classification updates with time penalties +- everything logged transparently + +Admins stay in control. +Drivers understand the decision. +Chaos disappears. + +--- + +# 5. Result Automation (Essential QoL) + +- automatically sync race results from iRacing +- parse positions, laps, incidents, fastest lap +- update standings instantly +- eliminate manual CSV uploads or spreadsheets + +This is **core** for both admins and drivers +(but not the “main feature” — just feels like magic). + +--- + +# 6. Optional: Session Creation QoL (Not required) + +GridPilot can assist admins: +- create sessions via browser automation +- batch-create entire season +- consistent settings without errors + +Important: +**Automation is a helper, not the product.** +We never touch gameplay. + +--- + +# 7. Competition Layer + +This is what makes GridPilot more than “tools”. + +## 7.1 Individual Progression +Drivers earn: +- points +- standings +- history +- personal stats +- performance trends + +Racing finally has **long-term meaning**. + +## 7.2 Team Competition +Teams accumulate points across races and seasons. +Drivers contribute to team success even when racing individually. + +The result: +**iRacing Ligen finally feel like real motorsport championships.** + +--- + +# 8. Safety, Security & Trust +- zero-knowledge login +- we never see or store passwords +- browser window visible for login +- automation always runs *on the admin’s machine* +- no client modification (unlike iRefined) + +Trust and legality are fundamental. + +--- + +# 9. MVP Philosophy + +### What we include: +- Admin QoL tools +- Driver identity +- Team competition +- Standings + automation +- Complaints + penalties +- League identity + presentation +- Session creation helper (optional) + +### What we leave out for now: +- stewards marketplace +- streaming tools +- monetization +- complex global ladder +- rating systems +- cross-sim functionality +- esports organization + +GridPilot starts **simple and solid**. + +--- + +# 10. Long-Term Vision (kept small but real) + +GridPilot becomes the central place where: +- drivers build a career +- teams compete across seasons +- leagues run without chaos +- complaints/penalties stay structured +- standings stay consistent +- automation reduces work +- identity and progression matter + +This is the part that makes us **THE platform**, +but we don’t overpromise it on day one. + +--- + +# Summary + +**GridPilot simplifies league management — +and elevates it from scattered spreadsheets +to a modern competition platform.** + +Admins get less work. +Drivers get more meaning. +Teams get real championships. +Everyone gets structure instead of chaos. \ No newline at end of file diff --git a/docs/concept/DRIVERS.md b/docs/concept/DRIVERS.md new file mode 100644 index 000000000..4059aeeb0 --- /dev/null +++ b/docs/concept/DRIVERS.md @@ -0,0 +1,236 @@ +# GridPilot for Drivers +*A clean competition platform that gives iRacing drivers structure, clarity, and long-term identity — without the chaos of Discord, spreadsheets, or scattered tools.* + +--- + +## 1. Why GridPilot Matters for Drivers + +iRacing’s official races are fun, but they’re anonymous. +Community leagues are exciting, but they’re chaotic and inconsistent. + +Drivers want: +- clear schedules +- consistent standings +- honest competition +- long-term stats +- team identity +- simple sign-ups +- structured complaints +- a place to follow their own racing journey + +GridPilot gives drivers the **missing competitive layer** on top of iRacing leagues. + +--- + +## 2. Your Driver Profile + +Every GridPilot driver gets a modern profile that shows who they are across leagues. + +### **Profile includes** +- name, number, country +- leagues you’re currently racing in +- season standings (driver + team) +- race history +- long-term point totals +- finishing positions +- incident trends +- performance stats (e.g., avg finish, win rate) +- optional team membership + +### **Why it’s different** +For the first time, your results aren’t stuck in individual Discords or spreadsheets. + +Your **racing identity lives in one place**. + +--- + +## 3. League Discovery — Find Where You Belong + +GridPilot makes it easy to find leagues that fit your schedule, skill level, and car preferences. + +### **Search filters** +- car class +- time of day +- weekday/weekend +- competition level +- league size +- region + +### **League pages show** +- schedule +- standings +- rules & format +- car/track list +- roster +- sign-ups + +No more scrolling through dead Discord ads or random forum posts. +You actually *see* what you’re joining. + +--- + +## 4. Joining a League — Simple & Fast + +A driver-first flow: + +1. Pick a league +2. Review schedule & format +3. Join solo or select a team +4. See upcoming races instantly +5. Start collecting points + +No forms. +No spreadsheets. +No “talk to the admin.” +Just click and race. + +--- + +## 5. Race Day Clarity + +GridPilot gives drivers everything they need to show up ready: + +- upcoming race timer +- track, car, session settings +- lobby info (password, link, format) +- previous race results +- standings impact preview +- weather / dynamic info (if available) + +Drivers stop asking the admin: +> “Hey, what’s the track this week?” + +--- + +## 6. Automatic Race Results + +After the race, GridPilot automatically pulls iRacing’s results: + +### **You get** +- final classification +- gained/lost positions +- incidents +- fastest laps +- points earned +- drop-week changes + +No more: +- waiting for an admin +- refreshing Discord +- reading screenshots +- guessing standings + +Standings update themselves the moment results hit iRacing. + +--- + +## 7. Long-Term Progression + +Drivers always wanted more than “just racing.” +GridPilot gives: + +### **Season history** +- every race +- every point +- every position change + +### **Career overview** +- races completed +- podiums +- wins +- average finish +- trendline performance + +### **Multi-league identity** +Your GridPilot profile grows as you race in multiple leagues and seasons. + +This makes league racing finally feel **meaningful**. + +--- + +## 8. Team Racing — Real Constructors-Style Competition + +GridPilot supports **real team racing**, not the one-driver-per-team limitation of most leagues. + +### **How it works** +- each team registers multiple drivers +- drivers race in parallel +- all points contribute to the team championship +- teams gain prestige over multiple seasons + +### **For drivers** +You can: +- join a team +- help your team climb the standings +- show your contribution in your profile +- build real rivalries over time + +This creates a feeling of *real motorsport* instead of isolated races. + +--- + +## 9. Complaints & Penalties Made Simple + +Drivers finally get a **structured** way to submit complaints. + +### **Submit complaint** +- pick the race +- pick involved drivers +- add timestamps +- add description +- optional replay clip / YouTube link + +### **Admin sees a simple review panel.** +No lost DMs, no arguments across Discord, no chaos. + +### **Standings update automatically after penalties.** +No manual math. +No spreadsheet edits. + +Drivers get transparency. +Admins get speed. + +--- + +## 10. The Experience for Drivers + +GridPilot gives drivers: + +- fewer distractions +- less confusion +- no spreadsheet hell +- proper standings +- identity that grows +- team fantasy + real competition +- simple joining +- long-term motivation +- reliable information +- structured dispute resolution + +League racing finally feels like a **proper season**, not a patchwork process. + +--- + +## 11. What GridPilot *does not* do (important) + +- doesn’t manage your Discord social life +- doesn’t auto-join races for you +- doesn’t replace iRacing +- doesn’t enforce drama rules +- doesn’t simulate or interfere with gameplay + +GridPilot strengthens what drivers already enjoy — +and eliminates everything that frustrates them. + +--- + +# Summary + +GridPilot for Drivers is about three things: + +1. **Clarity** — know where, when, and what you’re racing. +2. **Progression** — see your improvement and career. +3. **Competition** — battle in leagues and teams that feel like real motorsport. + +The platform gives drivers exactly what they’re missing today: +a central, modern home for their league racing identity. \ No newline at end of file diff --git a/docs/concept/LANDING.md b/docs/concept/LANDING.md new file mode 100644 index 000000000..5ecf0f811 --- /dev/null +++ b/docs/concept/LANDING.md @@ -0,0 +1,192 @@ +# GridPilot — Where iRacing League Racing Finally Comes Together +*A modern platform that makes league racing clearer, cleaner, and actually fun — for drivers, admins, and teams.* + +--- + +## League racing deserves better. + +Right now, running or joining a league means: +- Discord chaos +- scattered spreadsheets +- unclear standings +- manual data entry +- lost messages +- confusing sign-ups +- penalties handled in DMs +- no global identity +- no long-term stats + +The racing in iRacing is amazing. +**Everything around it shouldn't be this messy.** + +GridPilot fixes the layer iRacing leaves behind. + +--- + +# What GridPilot gives you + +## 📅 A real home for your league +Every league gets: +- a clean schedule +- standings +- results +- driver roster +- rules +- branding +- links (Discord, Twitch, YouTube, etc.) + +No setup. +No technical pain. +Just a proper league homepage that looks professional. + +--- + +## 🏁 Automatic results & standings +You race → GridPilot handles the rest. + +We track: +- positions +- incidents +- points +- drop weeks +- driver stats +- team points +- performance trends + +Standings update instantly after results hit iRacing. +Admins never touch spreadsheets again. + +--- + +## 👥 Real team racing — finally done right +Not just “tags next to names.” + +GridPilot supports true constructors-style racing: +- multiple drivers contribute points +- flexible scoring rules +- dynamic lineups (different drivers each race) +- team standings +- season + all-time history +- driver contribution breakdown + +League racing finally feels like **real motorsport**. + +--- + +## 👤 Driver identity that actually matters +Your racing history follows you across leagues and seasons. + +Your driver profile includes: +- race results +- season standings +- incident trends +- consistency metrics +- points over time +- team membership +- personal progress + +iRacing gives you physics. +**GridPilot gives you a racing career.** + +--- + +## ⚖ Clean protests & quick penalties +No more chaotic DM chains or missing evidence. + +Drivers can submit structured complaints with: +- timestamps +- drivers involved +- description +- optional replay clips + +Admins get: +- a proper review panel +- quick penalty tools (warning, time penalty, points deduction, DQ) +- auto-updated standings + +Fair. Transparent. Zero drama. + +--- + +## 🚀 Race management without headaches +Admins get: +- season builder +- roster tools +- sign-up flows +- scoring presets +- automatic result import +- penalty system +- league branding + +Everything that normally takes hours per week… +now takes minutes. + +--- + +## 🔧 One-click session creation (Quality-of-Life) +Filling out hosted session forms is repetitive as hell. +GridPilot can help: + +- generate full season sessions +- consistent configuration +- no more manual form work +- admin stays fully in control + +This is not the product — +it's just something that makes life easier. + +--- + +# Why people will use GridPilot + +### For drivers: +- clearer racing +- real identity +- competitive meaning +- team pride +- structured info +- proper stats +- no spreadsheet guessing + +### For admins: +- less work +- fewer mistakes +- clean presentation +- no nightly result updates +- no Discord chaos +- clear rules & penalties +- smooth seasons + +### For teams: +- proper constructors championships +- recruitment tools +- long-term reputation +- contribution metrics +- cross-season continuity + +--- + +# Why this exists (in plain words) + +Because league racing is the best part of iRacing +— and the worst-maintained. + +We’re building something practical, modern, and helpful. +Not trying to reinvent racing, not trying to replace leagues, +just trying to make everything around racing **less painful and more meaningful**. + +--- + +# Want early access? + +GridPilot is opening early access to **everyone** who wants smoother, more organized league racing. + +Drop your email if you want: +- updates as features go live +- a chance to shape the platform +- early access once things stabilize +- zero spam, zero BS + +[ Email field ] [ Sign Up ] + +Let’s make league racing better — together. \ No newline at end of file diff --git a/docs/concept/RACING.md b/docs/concept/RACING.md new file mode 100644 index 000000000..7e58e62ed --- /dev/null +++ b/docs/concept/RACING.md @@ -0,0 +1,250 @@ +# GridPilot Race Management +*A unified system for running modern iRacing leagues with team formats, automated series structures, and clean penalty handling.* + +--- + +# 1. Overview + +GridPilot Race Management defines **how racing works on the platform**, +independent of league size or community style. + +It consists of three pillars: + +1. **Team Leagues** — real constructors-style multi-driver competitions +2. **Automated Leagues** — structured series (e.g., F1-style) that GridPilot can mirror +3. **Penalty & Protest System** — clean dispute resolution without Discord chaos + +GridPilot's goal is to make league racing **consistent, fair, and meaningful**, +without forcing admins into complexity or drivers into mandatory social interaction. + +--- + +# 2. Team Leagues (Constructors Format) + +Most iRacing leagues treat teams as cosmetic. +GridPilot treats them as **primary competition units**, like real motorsport. + +## 2.1 Team Structure + +A team has: +- Team profile (logo, colors, description) +- Captain / co-captains +- Roster of drivers +- Recruitment status +- Participation history +- Team stats + points + +Teams exist platform-wide — not only inside one league. + +## 2.2 Team Participation in a League + +Teams can: +- register for seasons +- assign drivers to race slots +- have different drivers each week +- score parallel points from every contributing driver + +## 2.3 Constructors-Style Points + +GridPilot supports multiple team scoring methods: + +- **All Drivers Count** (every finisher scores team points) +- **Top X Drivers Count** (top 2, top 3, etc.) +- **Weighted Points** (e.g., top driver counts 60%, next counts 40%) +- **Custom Rules per League** + +Admins choose their model. +GridPilot handles the math, standings, history, and transparency. + +## 2.4 Team Rivalries & Identity + +Built-in: +- team standings +- driver contribution breakdown +- cross-season performance +- team “form” (last 5 races) +- recruitment visibility + +Team racing becomes **a core part of the platform**, not an afterthought. + +--- + +# 3. Automated Leagues (F1-Style Mirror Systems) + +Some admins want complete freedom. +Some drivers want structured, official-feeling series. + +GridPilot supports *both* by providing the option for **auto-generated leagues**. + +These are **not forced**; they are templates admins or communities can adopt. + +## 3.1 What Are Automated Leagues? + +An automated league is a **predefined series template** provided by GridPilot that mirrors real motorsport. + +Examples: +- F1 World Championship (same tracks, same order) +- F2 / F3 / F4 analogs +- GT3 European Sprint Cup +- IMSA-style multiclass +- DTM-style format + +Users don’t need to invent formats — they pick from proven structures. + +## 3.2 What an Automated League Includes + +A template defines: +- car class +- track calendar (pre-set schedule) +- race format (practice, quali, race) +- scoring system (e.g., FIA F1 points) +- bonus points (fastest lap, sprint race, etc.) +- team size (e.g., 2 drivers like Constructors) +- DNF rules +- drop rules (if any) +- penalty rules (warnings → time penalties → DSQ) + +Admins can tweak details OR run it 1:1 as provided. + +## 3.3 Session Generation + +GridPilot can automatically: +- create the full season +- populate all race settings +- optionally generate sessions via browser automation +- clearly list required content for admins + +This is still *admin-controlled* automation — +GridPilot enhances, not replaces, human oversight. + +## 3.4 Why Automated Leagues Matter + +For drivers: +- familiar formats +- real-world relevance +- stability + +For admins: +- less planning +- less overhead +- instantly “professional” structure + +For GridPilot: +- predictable competition ecosystem +- consistent feature usage +- scalable community engagement + +--- + +# 4. Penalty & Protest System + +A clean, structured way to handle disputes — core to GridPilot fairness. + +## 4.1 Driver-Protest Flow + +Drivers can submit: +- race +- involved drivers +- replay timestamp(s) +- explanation text +- optional clip/video links + +GridPilot stores everything in a clean UI. + +## 4.2 Admin Review Tools + +Admin sees: +- list of protests +- timestamps +- notes +- links +- driver details +- race context + +Actions: +- no action +- warning +- time penalty (+5s / +10s / +30s) +- points deduction +- DSQ race + +Everything updates automatically. + +## 4.3 Automatic Classification Updates + +Time penalties: +- re-sort results +- adjust finishing positions +- recalc points +- update team standings + +Penalties: +- logged in history +- visible on driver/team pages +- part of season record + +## 4.4 Consistency & Transparency + +GridPilot helps: +- keep decisions structured +- avoid DM drama +- maintain fairness +- preserve season integrity + +Admins stay in full control — GridPilot handles presentation & recalculation. + +--- + +# 5. What We Must Consider (Key Constraints) + +## 5.1 Content Ownership + +Automated leagues need: +- admins (or bot accounts) who own the tracks +- 1 owned car per allowed class +- iRacing credits to host sessions + +GridPilot cannot bypass iRacing content rules. + +## 5.2 Avoiding Splits (Very Important) + +Splits destroy community identity. +GridPilot must: +- keep leagues small (20–30 drivers) +- allow multiple parallel micro-series (F1-A, F1-B, etc.) +- unify standings across micro-series through stats (optional) + +We scale *horizontally*, not vertically. + +## 5.3 Admin Optionality + +Automation is optional. +Manual control must always be possible. + +## 5.4 Don’t Replace Community + +GridPilot provides structure — not social control. +Discord remains important; we complement it. + +## 5.5 Avoid excessive complexity + +Penalty rules, team formats, automated templates — +all must stay simple, configurable, and not overwhelming. + +--- + +# 6. GridPilot Race Management Summary + +GridPilot provides: +- professional team racing +- structured constructors scoring +- predefined (or custom) league formats +- optional auto-generated F1-style calendars +- clean protests and quick penalties +- automatic standings +- minimal admin workload +- consistent racing identity +- scalable competition model + +We don’t replace iRacing — +we give league racing the **structure, fairness, and identity** it has always lacked. \ No newline at end of file diff --git a/docs/concept/RATING.md b/docs/concept/RATING.md new file mode 100644 index 000000000..f8e8089bf --- /dev/null +++ b/docs/concept/RATING.md @@ -0,0 +1,224 @@ +# GridPilot Rating +*Our own competition rating system designed for league racing — because iRating was never made for this.* + +--- + +# 1. Why GridPilot Needs Its Own Rating + +iRating works great for official races, but it's not built for leagues. + +**League racing is fundamentally different:** +- you race against the same people each week +- racecraft matters more than raw pace +- team dynamics influence outcomes +- attendance and consistency matter +- incidents have real consequences +- formats vary massively between leagues +- results depend heavily on season structure +- small grids distort iRating logic + +iRating can’t measure: +- season performance +- team contribution +- consistency +- clean driving +- decision-making +- rivalry strength +- long-term commitment +- reliability + +So we build a rating *specifically for league racing* — +where context, progression, and human rivalry actually matter. + +--- + +# 2. What GridPilot Rating Measures + +GridPilot Rating = **league racing performance + racing behavior + reliability**. + +It captures the full picture of a driver, not only their pace. + +### **Core components** +- **Results Strength** + How well you finish relative to the field strength. + +- **Consistency** + Stability of finishing positions over a season. + +- **Clean Driving** + Incidents per race, weighted by severity. + +- **Racecraft** + Positions gained/lost vs. incident involvement. + +- **Reliability** + Attendance, DNS/DNF record. + +- **Team Contribution** + Points earned for your team; lineup efficiency. + +### **Why these matter** +A great league driver is not only fast. +A great league driver is: +- clean +- consistent +- reliable +- competitive +- predictable +- respected +- valuable to teams + +Our rating reflects **real league value**, not just raw pace. + +--- + +# 3. Principles of GridPilot Rating + +## **1. Competition over matchmaking** +iRating is designed for matchmaking. +GridPilot Rating is designed for **championships**. + +## **2. Context matters** +Beating a strong field → worth more. +Clean races → rewarded. +Incidents → penalized. +Attendance → essential. + +## **3. No punishing drivers for leagues with small grids** +In iRating, small splits cause unstable jumps. +GridPilot Rating normalizes for field size. + +## **4. No obsession with “perfection”** +This is not a chess ELO. +This is racing. +Racing has chaos. +Rating should reflect that. + +## **5. Transparent & understandable** +Drivers should always know: +> “Why did my rating change?” + +Our UI makes this clear. + +--- + +# 4. Rating Inputs (Conceptual, not equations) + +GridPilot Rating is built from weighted contributions: + +### **1. Race Placement Score** +Normalized by: +- field size +- field strength +- track difficulty (optional future) +- gaps between positions + +### **2. Incident Factor** +Scaled to: +- incident count +- impact on others +- severity +- penalty involvement + +### **3. Reliability Score** +- attendance +- quitting early +- DNS +- DNF + +### **4. Team Factor** +- points contributed relative to team total +- team finishing efficiency +- team strength + +### **5. Season Factor** +Bonus for: +- championship position +- improvement +- performance under pressure +- completing the full season + +Everything is transparent and cumulative. + +--- + +# 5. Why Teams Benefit from Rating + +Teams need a way to scout drivers that isn’t: +- “Do you have 3k iRating?” +- “Are you fast in officials?” +- “Do you join our Discord?” + +GridPilot Rating shows: +- reliability +- cleanliness +- consistency +- contribution +- teamwork +- multi-season performance + +That’s what teams care about in real league competition. + +--- + +# 6. Why Admins Benefit + +Admins get: +- balanced rosters +- fairer splits if needed +- data for assigning classes +- better recruitment +- fewer problems with unreliable drivers +- objective metrics behind decisions + +Rating turns leagues into **professional, predictable competitions**. + +--- + +# 7. Why Drivers Benefit + +Drivers finally get: +- recognition for more than pace +- a way to show reliability +- stats that show improvement +- incentives to race clean +- a reason to finish the season +- meaningful identity across multiple leagues + +GridPilot Rating makes league racing **feel like a true career**, not a collection of spreadsheets. + +--- + +# 8. How It Differs From iRating + +| iRating | GridPilot Rating | +|--------|------------------| +| Matchmaking tool | Competition identity tool | +| Random opponents | Same rivals weekly | +| No context | Context-heavy | +| Pace only | Pace + behavior + consistency + reliability | +| Does not reward team play | Team contribution included | +| Penalizes small grids | Normalized for field size | +| No season meaning | Season progression core to system | +| Binary win/loss logic | Full racing context | + +GridPilot Rating doesn’t replace iRating. +It fills the gap iRating never tried to solve. + +--- + +# 9. Why We're Building This + +Because league racing deserves: +- fair recognition +- long-term meaning +- personal progression +- team value +- traits beyond speed +- a competitive environment that feels like real motorsport + +No other platform gives league racers their own rating. +And no existing metric truly represents what makes someone great in a league. + +GridPilot Rating changes that — +**by measuring the things that actually matter in league racing.** \ No newline at end of file diff --git a/docs/concept/SOCIAL.md b/docs/concept/SOCIAL.md new file mode 100644 index 000000000..1f3db6ecd --- /dev/null +++ b/docs/concept/SOCIAL.md @@ -0,0 +1,204 @@ +# GridPilot — Social Features +*The social layer that connects drivers, admins, teams, and leagues in one unified platform.* + +--- + +## 1. The Purpose of GridPilot Social + +Sim racers today are split across: +- Discord servers +- Reddit threads +- scattered DMs +- league-specific chats +- spreadsheets with usernames + +There is **no central place** where drivers, admins, and teams discover each other or keep in touch. + +GridPilot adds a lightweight, modern social layer designed specifically for the league racing world. + +Not a full “social network,” not noisy, not chaotic — +just the tools needed to help people connect efficiently and meaningfully. + +--- + +## 2. Unified User Identity + +Every user has one identity across the entire platform: + +- profile picture +- username +- country & time zone +- teams +- leagues they race in +- bio (optional) +- links (Twitch / YouTube / Discord / social media) +- availability preferences (days/times they race) + +**Admins, drivers, team captains — all the same user type.** + +There are no separate accounts or roles. +Roles are permissions, not different identities. + +--- + +## 3. Messaging (Lightweight) + +A minimal message system designed for utility, not noise. + +### **Driver ↔ Driver** +- send quick messages (questions, coordination, friendly chatter) + +### **Driver ↔ Admin** +- ask about sign-ups +- clarify rules +- discuss incidents (if needed) +- handle roster or team questions + +### **Team Messaging** +- team chat for coordination +- optional channel for setup sharing or strategy +- not meant to replace Discord, but acts as a unified fallback + +### **League Announcements** +- admins can broadcast updates +- drivers receive notifications (web/mobile) + +This is *not* a Discord replacement — it fills the gaps Discord cannot cover. + +--- + +## 4. Discoverability Between Users + +GridPilot makes it easy for users to find the right people. + +### **Find Drivers** +Admins or team captains can filter by: +- region +- time zone +- car classes driven +- experience level +- incident trends +- availability +- open-to-team-invite flag + +### **Find Leagues** +Drivers can filter by: +- region +- event time +- car class +- competition level +- team / solo format + +### **Find Teams** +Drivers can see: +- teams recruiting +- team bios +- roster +- history +- available seats + +This enables a healthy “social marketplace” inside the platform — not chaos, but structure. + +--- + +## 5. Social Timeline (v1: Very Light) + +A simple, platform-wide activity feed such as: + +- “User X joined League Y” +- “Team Z registered for the new season” +- “Driver A scored P1 in Race 3” +- “Season standings updated” + +No memes, no fluff, no spam — just relevant racing activity. + +--- + +## 6. Profiles for Leagues & Teams (Social Extension) + +### **League Profiles** +- followers +- announcements +- pinned messages +- season previews +- open sign-ups +- links to streams + +### **Team Profiles** +- roster +- captain +- recruiting status +- achievements +- highlights +- invites/private chat + +This creates a natural social ecosystem around competition. + +--- + +## 7. Notifications (Non-Intrusive) + +Users get updates for: +- sign-up confirmations +- race day reminders +- new messages +- new followers +- schedule changes +- penalties published +- standings updates + +Always optional. +Always configurable. + +--- + +## 8. Social Safety Features + +GridPilot avoids drama-heavy social problems: + +- block/mute user +- report message or behavior +- admin moderation tools inside a league +- no open global chat (avoids toxicity) +- no group spam +- no public “wall posts” + +The platform focuses on **purposeful** communication only. + +--- + +## 9. Why GridPilot’s Social Layer Is Unique + +Current tools: +- Discord: chaotic, messy, isolated per league +- Reddit: fragmented +- Forums: outdated +- DMs: unstructured +- iRacing: no social features at all + +GridPilot combines the best parts of community interaction: +- discoverability +- messaging +- profiles +- recruitment +- structured announcements +- identity across leagues + +…without becoming a noisy distraction. + +--- + +## 10. Summary + +GridPilot’s social features give sim racers what they’re missing: + +- a unified identity +- simple ways to connect +- ways for leagues to find drivers +- ways for drivers to find leagues +- team coordination tools +- non-chaotic messaging +- structured community activity + +It’s the **social layer that sim racing has always lacked** — +lightweight, purposeful, and deeply integrated into competition. \ No newline at end of file diff --git a/docs/concept/STATS.md b/docs/concept/STATS.md new file mode 100644 index 000000000..37a55d8d7 --- /dev/null +++ b/docs/concept/STATS.md @@ -0,0 +1,270 @@ +# GridPilot — In-Depth Stats & Scoring +*A modern statistics and scoring engine that gives meaning, context, and clarity to every race, every season, and every driver.* + +--- + +# 1. Purpose of GridPilot Stats +iRacing’s official results are powerful, but extremely limited: +- no career progression +- no cross-season view +- no team context +- no advanced analytics +- no identity + +GridPilot turns league racing into a structured competitive ecosystem by adding **deep, consistent, cross-league analytics** that drivers, teams, and admins can all rely on. + +--- + +# 2. Core Data We Track + +GridPilot automatically imports race metadata from iRacing and expands it into a full statistical model. + +### **Per Race** +- finishing position +- starting position +- positions gained/lost +- incidents +- best lap +- average lap pace (optional if telemetry available) +- laps completed +- penalties applied +- classification after penalties + +### **Per Driver (Season)** +- points +- average finish +- consistency rating +- race attendance +- clean race percentage +- total incidents +- incidents per lap +- podiums +- wins +- DNF count +- performance trend + +### **Per Team (Season)** +- total team points +- points contribution per driver +- average team finish +- number of drivers scoring +- team attendance +- team success over time + +### **All-Time Stats** +- total races +- total wins +- total podiums +- historical point totals +- incident history +- class-specific stats (e.g., GT4, F4, F3) +- multi-league performance overview + +These stats give GridPilot the ability to create meaningful identity and competitive depth. + +--- + +# 3. Scoring Engine (Flexible + Transparent) + +Each league defines its scoring rules, but GridPilot standardizes them into clear, consistent structures. + +### **Supported Scoring Models** +- Formula-style points (F1, F2, F3, F4, GP2, etc.) +- Endurance-style points +- Fixed points per position +- Bonus points: + - fastest lap + - pole position + - clean race + - most positions gained + +### **Drop Weeks** +- configurable number of drops +- visual indicator for dropped results +- fully automated recalculation + +### **Team Scoring** +A true Constructors-style model: +- multiple drivers score per race +- flexible configuration (best 2 scores, all scores, custom rules) +- team standings calculated instantly +- team stats update automatically + +--- + +# 4. Driver Performance Metrics (Advanced) + +GridPilot doesn’t just store results — it analyzes them. + +### **Key Driver Metrics** +- **Consistency Score** + Statistical deviation of finishing positions over time. + Lower = more consistent. + +- **Pace Index** (optional if telemetry available) + Comparison of average lap times vs. field median. + +- **Clean Driving Score** + Incidents per race + severity weighting. + +- **Racecraft Score** + Positions gained/lost vs. incident involvement. + +- **Progression Score** + Improvement across a season, shown as a trendline. + +- **Clutch Factor** + Performance in high-pressure scenarios (late-season races, close battles). + +### **Visibility** +All metrics appear on: +- driver profile +- season detail page +- league overview +- team contribution breakdown + +Drivers finally see **how** they drive — not just where they finish. + +--- + +# 5. Team Metrics (Depth That Makes Teams Matter) + +Teams get **quantitative identity**. + +### **Team Performance Metrics** +- team points +- team pace +- average finishing position +- contribution per driver +- lineup efficiency (who delivers consistent points) +- recruitment performance (improvement after roster changes) +- team form (last 5 races) + +### **Team Contribution Breakdown** +Each driver has: +- points contributed +- percentage of team output +- consistency +- clean driving impact +- reliability (attendance) + +This creates real internal and external rivalries. + +--- + +# 6. Season Intelligence + +GridPilot gives leagues a complete analytical picture: + +### **Season Insights** +- top improvers +- most consistent drivers +- hard chargers +- clean drivers +- most valuable drivers (team scoring) +- pace distribution +- incident hotspots +- attendance map + +### **League Health Metrics** +- retention +- average field size +- finishing rate +- incident averages +- multi-class balancing + +Admins get clarity without spreadsheets. + +--- + +# 7. Classification Adjustments (Penalties Included) + +Results are never final until all penalties are processed. + +GridPilot automatically handles: + +- time penalties (+5s / +10s / +30s / custom) +- points deductions +- disqualifications (race-level) +- attendance penalties (if used) +- warning tracking + +After admin decisions: +- standings recalculate +- driver stats update +- team stats update +- classification changes are logged +- penalty history is visible + +No manual recalculation. +No mistakes. +No drama. + +--- + +# 8. Global Scoring Vision (Future-Friendly) + +GridPilot tracks: +- per-league points +- per-season points +- all-time points +- team points +- class-based points + +This allows future scaling into: +- global leaderboards +- cross-league rankings +- multi-class ladders +- team world rankings + +We don’t implement everything on day one — +but the **data model is built for it**. + +--- + +# 9. Why GridPilot Stats Matter + +For Drivers: +- proof of skill +- motivation +- long-term identity +- visible improvement +- fairness & transparency + +For Teams: +- real constructors competition +- driver scouting +- lineup planning +- prestige and pride + +For Admins: +- no spreadsheets +- no miscalculations +- clarity for the community +- instant transparency +- professionalism + +For Leagues: +- structure +- identity +- competitiveness +- return of drivers season after season + +--- + +# Summary + +GridPilot’s scoring and analytics system turns scattered league racing into a **structured competitive ecosystem**: + +- complete results +- meaningful metrics +- constructors-style team scoring +- clean & consistent standings +- penalty-aware classification +- multi-season depth +- driver identity +- team identity +- league clarity + +This is the **foundation** that makes GridPilot more than a tool — +it becomes the home of competition in iRacing league racing. \ No newline at end of file diff --git a/docs/concept/TEAMS.md b/docs/concept/TEAMS.md new file mode 100644 index 000000000..3970aa86a --- /dev/null +++ b/docs/concept/TEAMS.md @@ -0,0 +1,211 @@ +# GridPilot for Teams +*Teams are the backbone of real motorsport. GridPilot finally gives them the tools they’ve always needed in iRacing league racing.* + +--- + +## 1. Why Teams Matter + +Teams add: +- identity +- rivalry +- long-term story +- shared goals +- consistency +- progression +- fun beyond solo racing + +But in iRacing leagues today, teams are treated as: +- “a tag on a spreadsheet” +- “a logo in a Discord channel” + +GridPilot fixes that by making teams **first-class citizens** of the platform. + +--- + +## 2. Team Profiles + +Every team gets its own home on GridPilot: + +### **Team Profile Includes** +- team name +- logo & colors +- description/bio +- captain / co-captains +- full roster +- active seasons +- past seasons +- team standings +- points history +- achievements + +### Why it matters +Drivers finally have somewhere to “belong,” +and teams become part of the league’s story — not a footnote. + +--- + +## 3. Team Creation & Management + +Teams can be created by any user: + +### Team captains can: +- invite drivers +- approve join requests +- remove drivers +- assign roles (captain, strategist, reserve driver) +- manage the team profile +- mark the team as “recruiting” + +### Drivers can: +- request to join +- switch teams between seasons +- follow recruiting teams +- represent their team in multiple leagues + +Everything is clean, modern, and transparent. + +--- + +## 4. Team Registration for Seasons + +When a league supports team racing, teams can register directly: + +### Team season registration: +1. captain chooses league +2. reviews schedule & format +3. submits team +4. roster automatically populated +5. drivers assigned to specific car slots if needed +6. team appears in league standings + +No forms. +No DMs. +No spreadsheets. + +--- + +## 5. Constructors-Style Team Competition + +This is where GridPilot goes beyond what any league tool offers. + +### **How it works** +- multiple drivers from a team race in parallel +- each race week can have different participating drivers +- **all scored points contribute to the team championship** +- standings update automatically from iRacing results + +### **Why it’s superior** +- teams aren’t just “one driver per car slot” +- stronger teams can field more competitive drivers +- every driver matters +- team strategy becomes part of the championship +- it feels like real motorsport (F1: Constructors, WEC, GT teams) + +This is a massive upgrade over the usual “driver only” league structure. + +--- + +## 6. Team Standings & History + +Each team gets: +- season standings +- total points +- podiums +- wins +- member contributions +- historical performance graphs +- list of every season ever raced + +Over time, this becomes: +- reputation +- prestige +- an asset teams are proud of + +Teams become part of the GridPilot universe, not just this week’s race. + +--- + +## 7. Driver Contribution Breakdown + +Teams can see: +- which driver scored points +- how many +- consistency over time +- incidents +- average finishing position +- contribution %, similar to "driver share" + +This builds: +- internal competition +- motivation +- clarity +- lineup optimization + +It also helps captains choose who drives what event. + +--- + +## 8. Discoverability: Find & Recruit Drivers + +Teams can mark themselves as: +- **Actively Recruiting** +- **Closed** +- **Looking for reserve drivers** + +Drivers can: +- browse teams +- search by region / league / car class +- request to join +- follow teams they like + +This solves the huge problem of: +> “Where do we find new drivers for our team?” + +--- + +## 9. Communication Tools (Lightweight) + +Teams get: +- team announcements +- team chat +- direct messages +- lineup discussions +- strategy notes + +Not a full Discord replacement — +but enough to organize race days without noise. + +--- + +## 10. Why Teams Should Use GridPilot + +Teams get what iRacing never gave them: + +### ✔ Real constructors-style competition +### ✔ A permanent home +### ✔ A public identity +### ✔ A place to recruit +### ✔ A performance history +### ✔ a reason to stay long-term +### ✔ pride and prestige +### ✔ fewer spreadsheets +### ✔ clearer communication + +Teams turn league racing into something that **feels like real motorsport**. + +--- + +# Summary + +GridPilot for Teams delivers: +- professional team identity +- constructors-style points +- automatic team standings +- clear contributor stats +- season + all-time history +- recruitment tools +- team chats & coordination +- smooth season registration + +Teams finally become **true participants** in iRacing league racing — +not just a tag next to a driver name. \ No newline at end of file