Skip to main content

Contributing Guide

DualMind Lab is a blind AI model comparison platform built with .NET 8, Supabase, and Cloudflare Workers. Users compare two AI models side-by-side without knowing which is which, vote on the better response, and build crowd-sourced ELO rankings.
Before contributing, make sure you’ve completed the local development setup.

Getting started

1

Fork the repository

Fork the relevant repository on GitHub:

Backend

.NET 8 API

Frontend

Vanilla JS SPA

Admin

Admin dashboard
2

Create a feature branch

git checkout -b feature/your-feature-name
Use descriptive branch names: feature/add-openai-provider, fix/jwt-expiry-handling, docs/update-api-reference.
3

Make your changes

Follow the code standards below and ensure all tests pass before submitting.
4

Submit a pull request

Push your branch and open a PR against main:
git push origin feature/your-feature-name
Include a clear description of what changed and why.

Code standards

Backend (C# / .NET 8)

  • Classes: PascalCase — ArenaController, ChatProviderFactory
  • Methods: PascalCase — SendMessageAsync, GetThreadById
  • Variables: camelCase — threadId, modelResponse
  • Constants: PascalCase — MaxRetryAttempts
  • Interfaces: Prefix with IIChatProvider, ISupabaseClient
  • Controllers handle HTTP concerns only — no business logic
  • Services contain business logic
  • Use dependency injection for all services
  • Async/await for all I/O operations
  • Return IActionResult from controllers
[HttpGet("{id:guid}")]
public async Task<IActionResult> GetById(Guid id)
{
    try
    {
        var result = await _service.GetByIdAsync(id);
        if (result == null)
            return NotFound(new { error = "NOT_FOUND", message = $"Resource {id} not found" });
        return Ok(result);
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Failed to get resource {Id}", id);
        return StatusCode(500, new { error = "API_ERROR", message = "Internal server error" });
    }
}

Frontend (JavaScript)

js/
├── api/           # API client and service modules
│   ├── config/    # Environment configuration
│   ├── core/      # Base HTTP client
│   └── services/  # Endpoint-specific services
├── pages/         # Page-specific scripts
└── utils.js       # Shared utilities
  • Use const by default, let when reassignment is needed
  • Arrow functions for callbacks
  • Template literals for string interpolation
  • Async/await over .then() chains
  • Descriptive variable names — no single-letter variables

Commit conventions

Use conventional commit messages:
PrefixUsageExample
feat:New featurefeat: add OpenAI provider integration
fix:Bug fixfix: handle expired JWT in streaming
docs:Documentationdocs: update API reference for voting
refactor:Code refactoringrefactor: extract model selection logic
test:Adding teststest: add unit tests for ArenaController
chore:Maintenancechore: update NuGet dependencies

Pull request checklist

Before submitting your PR, verify:
1

Code compiles without errors

dotnet build
No build errors or warnings
2

Tests pass

dotnet test
All existing tests still pass
3

New tests added

Add tests for any new functionality or bug fixes.
4

Documentation updated

Update relevant docs if your change affects the API, configuration, or user-facing behavior.
5

No secrets committed

Never commit API keys, connection strings, or other secrets. Use environment variables.

What to contribute

Good First Issues

Look for issues labeled good-first-issue on GitHub — these are beginner-friendly tasks.

Bug Reports

Found a bug? Open an issue with reproduction steps, expected vs actual behavior, and environment details.

New Providers

Add support for new AI providers by implementing the IChatProvider interface.

Documentation

Improve docs, fix typos, add examples, or translate content.

Need help?

If you’re stuck or unsure about an approach, open a draft PR early and ask for feedback. We’re happy to guide contributors.