# Agent Guide for WookiepediaStatusArticleData
This document provides guidelines for AI coding agents working on the WookiepediaStatusArticleData project.
## Project Overview
This is an ASP.NET Core 8.0 web application using Entity Framework Core with PostgreSQL for managing Wookieepedia status article nominations, awards, and projects.
**Tech Stack:**
- .NET 8.0 (C#)
- ASP.NET Core MVC with Razor views
- Entity Framework Core with PostgreSQL (Npgsql)
- xUnit for testing
- Auth0 for authentication
- HTMX for frontend interactivity
- DbUp for database migrations
## Build, Test, and Run Commands
Run `dotnet` commands from the `WookiepediaStatusArticleData` folder.
### Building
```bash
# Build entire solution
dotnet build
# Build specific project
dotnet build WookiepediaStatusArticleData/WookiepediaStatusArticleData.csproj
# Build in Release mode
dotnet build -c Release
```
### Testing
```bash
# Run all tests
dotnet test
# Run tests with detailed output
dotnet test -v normal
# Run a single test class
dotnet test --filter "FullyQualifiedName~ProjectsControllerTest"
# Run a specific test method
dotnet test --filter "FullyQualifiedName~ProjectsControllerTest.Add_WithValidUniqueCode_CreatesProjectSuccessfully"
# Run tests matching a name pattern
dotnet test --filter "Name~Add"
# Run with code coverage
dotnet test --collect:"XPlat Code Coverage"
```
### Running
```bash
# Run the application
dotnet run --project WookiepediaStatusArticleData/WookiepediaStatusArticleData.csproj
# Run with specific environment
dotnet run --project WookiepediaStatusArticleData/WookiepediaStatusArticleData.csproj --environment Development
```
### Database Migrations
```bash
# Create a new migration (use the helper script)
./create_migration.sh MigrationName
# Migrations are in Database/Migrate/*.sql and applied via DbUp on startup
```
## Code Style Guidelines
### File Organization
- Controllers: `WookiepediaStatusArticleData/Controllers/`
- Models (entities): `WookiepediaStatusArticleData/Nominations/{Domain}/`
- ViewModels/Forms: `WookiepediaStatusArticleData/Models/`
- Services/Actions: `WookiepediaStatusArticleData/Services/{Domain}/`
- Database: `WookiepediaStatusArticleData/Database/`
- Tests: `WookiepediaStatusArticleData.Tests/`
### Naming Conventions
- **Files**: PascalCase matching the class name (e.g., `ProjectsController.cs`)
- **Classes**: PascalCase (e.g., `ProjectsController`, `CreateProjectAction`)
- **Interfaces**: PascalCase with `I` prefix (e.g., `IProjectAwardCalculation`)
- **Methods**: PascalCase (e.g., `ExecuteAsync`, `Archive`)
- **Properties**: PascalCase (e.g., `Name`, `CreatedAt`)
- **Private fields**: camelCase with `_` prefix (e.g., `_fixture`, `_client`)
- **Parameters**: camelCase (e.g., `cancellationToken`, `form`)
- **Local variables**: camelCase (e.g., `project`, `affectedGroups`)
### Namespace and Using Directives
- One namespace per file using file-scoped namespace syntax
- Group usings: System namespaces, then third-party, then local
- Remove unused usings
- Use explicit namespace declarations (no global usings except those in csproj)
```csharp
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using WookiepediaStatusArticleData.Database;
using WookiepediaStatusArticleData.Services;
namespace WookiepediaStatusArticleData.Controllers;
[Authorize]
public class ExampleController : Controller
{
// ...
}
```
### Types and Nullability
- **Nullable reference types enabled**: `enable` is set
- Use `?` for nullable reference types (e.g., `string?`, `IList?`)
- Use `required` modifier for required properties (e.g., `public required string Name { get; set; }`)
- Avoid null-forgiving operator (`!`) unless you're certain the value is non-null
### Primary Constructors
- Use primary constructors for dependency injection in controllers and services
```csharp
public class ProjectsController(WookiepediaDbContext db) : Controller
{
// db is automatically available as a field
}
```
### Method Signatures
- Always include `CancellationToken` parameter for async database operations
- Pass `CancellationToken` to all EF Core methods
- Use `[FromServices]` attribute for action-scoped service injection
- Use `[FromRoute]`, `[FromQuery]`, `[FromForm]` attributes to be explicit
```csharp
[HttpPost("{id:int}")]
public async Task Edit(
[FromRoute] int id,
[FromForm] ProjectForm form,
[FromServices] EditProjectAction action,
CancellationToken cancellationToken
)
{
// ...
}
```
### Error Handling and Validation
- Use `ValidationException` for domain validation errors
- Add errors to `ModelState` in controllers
- Return appropriate HTTP status codes (400 for validation, 404 for not found)
- Set `Response.StatusCode` before returning views with errors
```csharp
try
{
await action.ExecuteAsync(form, cancellationToken);
await db.SaveChangesAsync(cancellationToken);
return RedirectToAction("Index");
}
catch (ValidationException validationException)
{
foreach (var issue in validationException.Issues)
{
ModelState.AddModelError(issue.Name, issue.Message);
}
Response.StatusCode = 400;
return View("FormName", form);
}
```
### Database Operations
- Use Entity Framework Core for all database access
- Always pass `CancellationToken` to async EF methods
- Use transactions for multi-step operations: `await db.Database.BeginTransactionAsync(cancellationToken)`
- Use `Include()` for eager loading related entities
- Use raw SQL sparingly, with interpolated strings for parameterization: `$"..."` in ExecuteSqlAsync
```csharp
var project = await db.Set()
.Include(it => it.HistoricalValues)
.SingleOrDefaultAsync(it => it.Id == id, cancellationToken);
```
### Testing Guidelines
- Test class naming: `{ClassUnderTest}Test` (e.g., `ProjectsControllerTest`)
- Use xUnit's `[Fact]` attribute for tests
- Use `IClassFixture` for shared test context
- Use Testcontainers for integration tests with PostgreSQL
- Test method naming: descriptive, action-oriented (e.g., `Add_WithValidUniqueCode_CreatesProjectSuccessfully`)
- Use `WebApplicationFactory` for controller integration tests
- Always clean up resources with `IAsyncLifetime`
```csharp
public class ExampleTest : IClassFixture, IAsyncLifetime
{
[Fact]
public async Task MethodName_Scenario_ExpectedOutcome()
{
// Arrange
// Act
// Assert
}
}
```
### Formatting
- Use 4 spaces for indentation (not tabs)
- Opening braces on new line for types and methods
- Use expression-bodied members for simple properties: `public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);`
- Use collection expressions: `[item1, item2]` instead of `new[] { item1, item2 }` where appropriate
- Prefer `var` for local variables when type is obvious
### Comments
- Use XML doc comments (`///`) for public APIs
- Keep inline comments minimal and explain "why", not "what"
- Add comments for complex business logic or non-obvious decisions
## Architecture Patterns
### Controllers
- Keep controllers thin; delegate business logic to action classes
- Use dependency injection for services and actions
- Return appropriate action results (`View`, `RedirectToAction`, `NotFound`, `Ok`)
### Actions (Service Layer)
- Create action classes for business operations (e.g., `CreateProjectAction`, `EditProjectAction`)
- Register actions as scoped services
- Actions should be focused on a single responsibility
### Domain Models
- Place entity classes in `Nominations/{Domain}/` folders
- Use `required` properties for non-nullable fields
- Include domain methods on entities (e.g., `Archive()`)
### Validation
- Use `ValidationException` with `ValidationIssue` for domain validation
- Perform validation in action classes before persistence
- Return specific, user-friendly error messages
## Common Pitfalls to Avoid
1. **Don't forget CancellationToken**: Always pass it to async EF Core methods
2. **Don't use blocking calls**: Use `async`/`await` consistently
3. **Don't forget transactions**: Use transactions for multi-step database operations
4. **Don't ignore ModelState**: Always check `ModelState.IsValid` in controller actions
5. **Don't hardcode status codes**: Use appropriate HTTP status code constants or set `Response.StatusCode`
6. **Don't use deprecated patterns**: Use file-scoped namespaces, primary constructors, and collection expressions
## Additional Notes
- The application uses Auth0 for authentication; controllers are decorated with `[Authorize]`
- HTMX is used for dynamic UI updates
- Database migrations are SQL files in `Database/Migrate/` and applied via DbUp
- Feature flags are managed with `Microsoft.FeatureManagement`