Imported from duque96/RacingCoach (
AGENTS.md). Install upstream withnpx skills add duque96/RacingCoach. Copyright stays with the author.
Racing Coach - Project Conventions
Project Context
Racing Coach is an analysis and coaching platform for racing/sim racing games.
Objective: Create a system that receives telemetry data from racing games, normalizes it into a common model, analyzes sessions/laps/sectors, detects improvement areas, and generates personalized recommendations using AI/LLM.
Target games:
- Gran Turismo 7 (PS5) - supported
- F1 25 - supported
- Other simulators - later expansion
Technology stack:
- .NET 10 + Blazor Server
- SQLite with Entity Framework Core
- xUnit + FluentAssertions + NSubstitute for testing
- Local deployment
Architecture:
- Clean Architecture with 5 layers: Api, Domain, Infrastructure, Shared, Providers
- Modular provider system (each game is a separate project)
- Minimal API for endpoints
- Domain Services pattern (endpoints and UI depend on services, never on repositories directly)
- Repository Pattern
- Phased dependency injection
Language Conventions
IMPORTANT: All code, comments, documentation, and AGENTS.md files must be written in English.
- All C# code must be in English
- All comments (inline, XML docs) must be in English
- All AGENTS.md files must be in English
- All commit messages must be in English
- All variable names, method names, and class names must be in English
- All documentation (README, etc.) must be in English
Code Conventions
Clean Code
- Clean, readable, and maintainable code
- Descriptive and consistent naming
- Small methods with single responsibility
- No unnecessary comments (code should be self-explanatory)
- Strictly applied SOLID principles
Design Patterns
- Domain Services: Services in Domain/Services orchestrate repositories; endpoints and UI never access repositories directly
- Repository Pattern: Interfaces in Domain, implementations in Infrastructure
- DTO Pattern: Strongly typed DTOs for data transfer
Strong Typing
- Use specific types instead of primitives when possible
- Nullable reference types enabled
- Avoid
dynamicandobjectunless justified - Records for immutable DTOs
Layer Structure
- Domain: Domain models, repository interfaces, domain services. No external dependencies.
- Infrastructure: Concrete implementations (EF Core, database configurations). Depends on Domain.
- Api: Minimal API endpoints, Blazor components, application services. Depends on Domain.
- Shared: Shared DTOs, constants, transversal helpers. No business logic.
- Providers: Game-specific implementations (parsers, listeners). Each provider is a separate project.
Dependency Rule
- Domain does NOT depend on Api or Infrastructure
- Infrastructure depends on Domain
- Api depends on Domain
- Shared is transversal and can be referenced by all
Commit Conventions
We use standard Conventional Commits:
<type>: <description>
[optional body]
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Format changes (spaces, commas, etc.) without changing logicrefactor: Code refactoringtest: Adding or modifying testschore: Build, dependencies, configuration changes
Examples:
feat: add UDP listener for telemetry capture
fix: correct malformed UDP packet parsing
docs: update README with installation instructions
refactor: extract parsing logic to specific class
test: add tests for GT7TelemetryParser
Build and Testing
Commands
# Build entire solution
dotnet build
# Run tests
dotnet test
# Run application
dotnet run --project src/RacingCoach.Api
# Restore packages
dotnet restore
Mandatory Testing
- All new code must have tests
- Unit tests for each handler/command
- Integration tests for critical endpoints
- Telemetry parser tests with real/mock data
- Minimum coverage for critical changes: 80%
Before Commit
- Build without errors:
dotnet build - All tests pass:
dotnet test - No critical warnings
- Formatted code (if formatter is configured)
Workflow
Branches
main: Stable and tested codedevelop: Continuous integration (optional)feature/*: New featuresfix/*: Bug fixesrefactor/*: Refactoring
Pull Requests
- All changes go through PR (except urgent hotfixes)
- PR must include clear description of changes
- Tests must pass before merge
- Code review mandatory (if team exists)
Solution Structure
RacingCoach/
├── RacingCoach.sln
├── Directory.Build.props # Common build config
├── Directory.Packages.props # Central Package Management
├── src/
│ ├── RacingCoach.Api/ # Blazor Server + Endpoints
│ ├── RacingCoach.Domain/ # Models, interfaces, commands, services
│ ├── RacingCoach.Infrastructure/ # SQLite, EF Core, repositories
│ ├── RacingCoach.Shared/ # DTOs, constants, helpers
│ ├── RacingCoach.Providers.GT7/ # GT7-specific parser and listener
│ └── RacingCoach.Providers.F1/ # F1 25-specific parser and listener
└── tests/
└── RacingCoach.Tests/ # xUnit tests
Naming Conventions
C#
- Classes/Interfaces: PascalCase (
TelemetryParser,ITelemetryRepository) - Methods: PascalCase (
ParsePacket,GetSessionById) - Variables/Parameters: camelCase (
rawPacket,sessionId) - Private fields: camelCase with optional
_prefix (_repository,_logger) - Constants: PascalCase (
DefaultUdpPort,MaxPacketSize)
Files and Folders
- C# files: PascalCase (
TelemetryParser.cs,GameSession.cs) - Folders: PascalCase (
Models/,Services/,Repositories/) - Projects: PascalCase with prefix (
RacingCoach.Domain)
Dependency Injection
Registration Order in Program.cs
builder.Services.AddDomain();
builder.Services.AddInfrastructure(configuration);
builder.Services.AddApi(configuration);
builder.Services.AddGT7Provider();
builder.Services.AddF1Provider();
Extension Methods
Each layer has its own Add{Layer}() extension method:
AddDomain()in DomainAddInfrastructure(IConfiguration)in InfrastructureAddApi(IConfiguration)in ApiAddGT7Provider()in Providers.GT7AddF1Provider()in Providers.F1
Error Handling
Domain Services
- Services in
Domain/Servicesencapsulate repository access and business rules - Endpoints and Blazor components depend on domain services, never on repositories directly
- Each repository operation used from Api goes through a domain service
- Services return errors to callers; they do not throw for normal control flow
Exceptions
- Only for truly exceptional cases
- Never for normal control flow
- Catch at boundaries (endpoints, services) and convert to errors
Configuration
appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=racingcoach.db"
}
}
Options Pattern
Use IOptions<T> for typed configuration:
public class DatabaseOptions
{
public string ConnectionString { get; set; } = string.Empty;
}
Documentation
XML Comments
- Only for complex public APIs
- Don't document the obvious
- Focus on "why", not "what"
README
- Installation instructions
- How to run the application
- How to run tests
- Project structure
Roadmap
Phase 1: Packet Sniffer (Completed)
- Base project structure
- UDP Listener
- Raw packet capture
- Basic UI for visualization
- GT7 packet format analysis
Phase 2: Parser + Visualization (Completed)
- GT7 parser
- Normalized telemetry model
- Real-time visualization
- Basic session analysis
Phase 3: Modular Architecture (Completed)
- Provider abstraction layer
- GT7 provider implementation
- Session management system
- Configuration persistence
- REST API endpoints
- UI for provider/session management
Phase 3.5: F1 25 Provider (Completed)
- F1 25 provider implementation (UDP listener, stateful parser, 16 packet types)
- Extended normalized model with provider-agnostic value objects (energy, aero, damage, track, tyres)
- Multi-packet support:
ITelemetryParserreturnsResult<TelemetryData?>, parser reused per session
Phase 4: Analysis + AI (Future)
- Lap/sector detection
- Performance metrics
- LLM integration for recommendations
- Driver profile
Important Notes
- Do not modify opencode configuration - opencode automatically reads AGENTS.md
- Mandatory testing - Do not commit without passing tests
- Strict Clean Code - Readable code > "clever" code
- Strong typing - Avoid dynamic/object unless justified
- English only - All code, comments, and documentation must be in English