diff --git a/.agent/rules/backend/api-endpoints.md b/.agent/rules/backend/api-endpoints.md index 163fb7eda3..7123d4e6fd 100644 --- a/.agent/rules/backend/api-endpoints.md +++ b/.agent/rules/backend/api-endpoints.md @@ -5,7 +5,7 @@ description: Rules for ASP.NET minimal API endpoints --- # API Endpoints -Carefully follow these instructions when implementing minimal API endpoints in the backend, including structure, route conventions, and usage patterns. +Guidelines for implementing minimal API endpoints in the backend, including structure, route conventions, and usage patterns. ## Implementation diff --git a/.agent/rules/backend/api-tests.md b/.agent/rules/backend/api-tests.md index b50fc83707..1aaca6aded 100644 --- a/.agent/rules/backend/api-tests.md +++ b/.agent/rules/backend/api-tests.md @@ -5,7 +5,7 @@ description: Rules for writing backend API tests --- # Writing API Tests -Carefully follow these instructions when writing tests for the backend. By default, tests should test API endpoints to verify behavior over implementation. Only in rare cases should unit tests be used. +Guidelines for writing tests for the backend. By default, tests should test API endpoints to verify behavior over implementation. Only in rare cases should unit tests be used. ## Implementation @@ -29,7 +29,7 @@ Carefully follow these instructions when writing tests for the backend. By defau 10. Test both happy path and error cases 11. Avoid sharing fields between tests—prefer local constants or variables within each test method 12. Verify side effects like database changes and telemetry events -13. Always call `TelemetryEventsCollectorSpy.Reset()` as the last Arrange statement if API calls were used to set up state +13. Always call `TelemetryEventsCollectorSpy.Reset()` as the last Arrange statement if API calls were used to set up state (to ensure only the events from the Act phase are verified) 14. Use the `Connection` property from `EndpointBaseTest` for test data—it provides a SQLite connection with: - `Insert` to populate test data - `Update` to update test data @@ -44,7 +44,7 @@ Carefully follow these instructions when writing tests for the backend. By defau - Telemetry event collection - Proper test cleanup with the Dispose pattern -IMPORTANT: Ensure consistent ordering, naming, spacing, and line breaks. When creating SQL dummy data, ensure columns are in the exact same order as in the database. Make sure similar elements are written consistently across tests. +Ensure consistent ordering, naming, spacing, and line breaks. When creating SQL dummy data, ensure columns are in the exact same order as in the database. Make sure similar elements are written consistently across tests. ## Examples @@ -78,29 +78,45 @@ public async Task BadTest() // ❌ DON'T: Skip verifying DB or telemetry side effects } -// ✅ DO: Use SQLite helpers for test data setup, consistent column order -public GetUsersTests() +// ✅ DO: Create helper methods for test data, call them in // Arrange +private string InsertTestUser(string? email = null) { + var userId = UserId.NewId().ToString(); Connection.Insert("Users", [ ("TenantId", DatabaseSeeder.Tenant1.Id.ToString()), - ("Id", UserId.NewId().ToString()), + ("Id", userId), ("CreatedAt", TimeProvider.System.GetUtcNow().AddMinutes(-10)), // ✅ DO: Use TimeProvider for dates ("ModifiedAt", null), - ("Email", Email) + ("Email", email ?? Faker.Internet.Email()) ]); + return userId; } -// ❌ DON'T: Use Dapper in tests +[Fact] +public async Task GetUser_WhenUserExists_ShouldReturnUser() +{ + // Arrange + var userId = InsertTestUser("test@example.com"); // ✅ DO: Call helper in Arrange - makes test self-contained + + // Act + var response = await AuthenticatedOwnerHttpClient.GetAsync($"/api/users/{userId}"); + + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); +} + +// ❌ DON'T: Create test data in constructors or use Dapper public class BadTestSetup { - public BadTestSetup() // ❌ DON'T: Add setup logic to constructor + public BadTestSetup() { - // Arrange + // ❌ DON'T: Add setup logic to constructor - tests become implicit and harder to understand using var connection = new SqliteConnection(Connection.ConnectionString); // ❌ DON'T: Use Dapper connection.Open(); // Insert user // ❌ DON'T: Add comments - connection.Execute("INSERT INTO Users (Email, Id, TenantId) VALUES (@Email, @Id, @TenantId)", new { Email = "test@example.com", Id = Guid.NewGuid(), TenantId = 1 }); + connection.Execute("INSERT INTO Users ..."); + connection.Execute("INSERT INTO Users (Email, Id, TenantId) VALUES (@Email, @Id, @TenantId)", new { Email = "test@example.com", Id = Guid.NewGuid(), TenantId = 1 }); // ❌ DON'T: Use Dapper Execute } } ``` diff --git a/.agent/rules/backend/backend.md b/.agent/rules/backend/backend.md index 57078bf836..fcb204f38d 100644 --- a/.agent/rules/backend/backend.md +++ b/.agent/rules/backend/backend.md @@ -5,7 +5,7 @@ description: Core rules for C# development and tooling --- # Backend -Carefully follow these instructions for C# backend development, including code style, naming, exceptions, logging, and build/test/format workflow. +Guidelines for C# backend development, including code style, naming, exceptions, logging, and build/test/format workflow. ## Code Style @@ -104,7 +104,7 @@ Carefully follow these instructions for C# backend development, including code s ## Implementation -IMPORTANT: Always follow these steps very carefully when implementing changes: +Follow these steps when implementing changes: 1. Always start new changes by writing new test cases (or change existing tests)—consult [API Tests](/.agent/rules/backend/api-tests.md) for details 2. Build and test your changes: diff --git a/.agent/rules/backend/commands.md b/.agent/rules/backend/commands.md index cdf4e2a257..5d9eabc1fa 100644 --- a/.agent/rules/backend/commands.md +++ b/.agent/rules/backend/commands.md @@ -5,7 +5,7 @@ description: Rules for implementing CQRS commands, validation, handlers, and str --- # CQRS Commands -Carefully follow these instructions when implementing CQRS commands, including structure, validation, handlers, and MediatR pipeline behaviors. +Guidelines for implementing CQRS commands, including structure, validation, handlers, and MediatR pipeline behaviors. ## Structure diff --git a/.agent/rules/backend/database-migrations.md b/.agent/rules/backend/database-migrations.md index 8d6cfd0d14..90dabe3363 100644 --- a/.agent/rules/backend/database-migrations.md +++ b/.agent/rules/backend/database-migrations.md @@ -5,14 +5,14 @@ description: Rules for creating database migrations --- # Database Migrations -Carefully follow these instructions when creating database migrations. +Guidelines for creating database migrations. ## Implementation 1. Create migrations manually rather than using Entity Framework tooling: - Place migrations in `/[scs-name]/Core/Database/Migrations` - Name migration files with 14-digit timestamp prefix: `YYYYMMDDHHmmss_MigrationName.cs` - - Only implement the `Up` method—do NOT create `Down` migration + - Only implement the `Up` method—don't create `Down` migration 2. Follow this strict column ordering in table creation statements: - `TenantId` (if applicable) @@ -22,7 +22,7 @@ Carefully follow these instructions when creating database migrations. - All other properties in the same order as they appear in the C# Aggregate class 3. Use appropriate SQL Server data types: - - For strongly typed IDs, default to `varchar(32)` (ULID is 26 chars + underscore + max 5-char prefix) + - Use `varchar(32)` for strongly typed IDs (ULID is 26 chars + underscore + max 5-char prefix = exactly 32) - Intelligently deduce varchar vs nvarchar based on property type, validators, enum values, etc. - Use `datetimeoffset` (default), `datetime2` (timezone agnostic), or `date`—never `datetime` - Default to `varchar(10)` or `varchar(20)` for enum values diff --git a/.agent/rules/backend/domain-modeling.md b/.agent/rules/backend/domain-modeling.md index d670714f4e..8cfc35eb5b 100644 --- a/.agent/rules/backend/domain-modeling.md +++ b/.agent/rules/backend/domain-modeling.md @@ -5,7 +5,7 @@ description: Rules for creating DDD aggregates, entities, value objects, and Ent --- # Domain Modeling -Carefully follow these instructions when implementing DDD models for aggregates, entities, and value objects. +Guidelines for implementing DDD models for aggregates, entities, and value objects. ## Implementation @@ -155,7 +155,7 @@ public class BadInvoice : AggregateRoot public List InvoiceLines { get; set; } = new(); } -public sealed class BadInvoiceConfiguration : IEntityTypeConfiguration +public sealed class BadInvoiceConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { @@ -163,6 +163,17 @@ public sealed class BadInvoiceConfiguration : IEntityTypeConfiguration t.Description).HasMaxLength(255); // ❌ DON'T: Configure primitive properties builder.HasIndex(u => new { u.Email }).IsUnique(); // ❌ IsUnique is a database constraint, not needed by EF Core at runtime builder.Property(u => u.Role).HasColumnType("varchar(10)").HasConversion(); // ❌ EF is configured to convert all enums to string + + // ❌ DON'T: Configure FK relationships without OnDelete - we don't use EF Tools for migrations + builder.HasOne() + .WithMany() + .HasForeignKey(i => i.TenantId); + + // ✅ DO: Configure FK relationships only when you need cascade delete behavior at runtime + builder.HasOne() + .WithMany() + .HasForeignKey(i => i.CustomerId) + .OnDelete(DeleteBehavior.Cascade); // EF needs this to handle cascades at runtime } } ``` diff --git a/.agent/rules/backend/external-integrations.md b/.agent/rules/backend/external-integrations.md index 38238dd39b..ef135b2a64 100644 --- a/.agent/rules/backend/external-integrations.md +++ b/.agent/rules/backend/external-integrations.md @@ -5,7 +5,7 @@ description: Rules for creating external integration services --- # External Integrations -Carefully follow these instructions when implementing integrations with external services in the backend, including structure, error handling, and client conventions. +Guidelines for implementing integrations with external services in the backend, including structure, error handling, and client conventions. ## Implementation diff --git a/.agent/rules/backend/queries.md b/.agent/rules/backend/queries.md index 12e5a6d902..e284921ddc 100644 --- a/.agent/rules/backend/queries.md +++ b/.agent/rules/backend/queries.md @@ -5,7 +5,7 @@ description: Rules for CQRS queries, including structure, validation, response t --- # CQRS Queries -Carefully follow these instructions when implementing CQRS queries, including structure, validation, response types, and MediatR pipeline behaviors. +Guidelines for implementing CQRS queries, including structure, validation, response types, and MediatR pipeline behaviors. ## Implementation diff --git a/.agent/rules/backend/repositories.md b/.agent/rules/backend/repositories.md index b962557a8b..eaf2b2f840 100644 --- a/.agent/rules/backend/repositories.md +++ b/.agent/rules/backend/repositories.md @@ -5,7 +5,7 @@ description: Rules for DDD repositories, including tenant scoping, interface con --- # DDD Repositories -Carefully follow these instructions when implementing DDD repositories in the backend, including structure, interface conventions, and Entity Framework mapping. +Guidelines for implementing DDD repositories in the backend, including structure, interface conventions, and Entity Framework mapping. ## Implementation @@ -16,7 +16,7 @@ Carefully follow these instructions when implementing DDD repositories in the ba - Use `IBaseRepository` when you don't need all CRUD operations - Only include methods needed for your specific aggregate 5. Only return Aggregates or custom projections—never Entities or Value Objects -6. Never return `[PublicAPI]` response DTOs +6. Never return `[PublicAPI]` response DTOs (repositories return domain objects; mapping to DTOs happens in query handlers) 7. Keep repositories focused on persistence operations, not business logic 8. Repositories are automatically registered in the DI container 9. Aggregates with `ITenantScopedEntity` are automatically filtered by tenant using EF Core query filters: diff --git a/.agent/rules/backend/strongly-typed-ids.md b/.agent/rules/backend/strongly-typed-ids.md index 91f78a09de..319f3fecf3 100644 --- a/.agent/rules/backend/strongly-typed-ids.md +++ b/.agent/rules/backend/strongly-typed-ids.md @@ -5,7 +5,7 @@ description: Rules for creating strongly typed IDs for DDD aggregates and entiti --- # Strongly Typed IDs -Carefully follow these instructions when implementing strongly typed IDs in the backend, covering type safety, naming, serialization, and EF Core mapping. +Guidelines for implementing strongly typed IDs in the backend, covering type safety, naming, serialization, and EF Core mapping. ## Implementation diff --git a/.agent/rules/backend/telemetry-events.md b/.agent/rules/backend/telemetry-events.md index b2fa6ba657..ee1ba4846e 100644 --- a/.agent/rules/backend/telemetry-events.md +++ b/.agent/rules/backend/telemetry-events.md @@ -5,7 +5,7 @@ description: Rules for telemetry events including important rules of where to cr --- # Telemetry Events -Carefully follow these instructions when implementing telemetry events in the backend, including event structure, naming, and publishing practices. +Guidelines for implementing telemetry events in the backend, including event structure, naming, and publishing practices. ## Implementation diff --git a/.agent/rules/developer-cli/developer-cli.md b/.agent/rules/developer-cli/developer-cli.md index db4515f032..2bd00e5bac 100644 --- a/.agent/rules/developer-cli/developer-cli.md +++ b/.agent/rules/developer-cli/developer-cli.md @@ -5,7 +5,7 @@ description: Rules for implementing Developer CLI commands --- # Developer Command Line Interface Rules -Carefully follow these instructions when implementing and extending the custom Developer Command Line Interface (CLI) commands. +Guidelines for implementing and extending the custom Developer Command Line Interface (CLI) commands. ## Implementation @@ -112,9 +112,8 @@ public class BadBuildCommand : Command { public BadBuildCommand() : base("bad-build", "Bad build command") { - // ❌ DON'T: Extract options to a variable - var option = new Option(["-file-name", "--f"], "The name of the solution to process") // ❌ Inconsistent option naming, wrong use of -- and - - AddOption(option); + // ❌ Use inconsistent option naming (single dash for long names, double dash for short) + AddOption(new Option(["-file-name", "--f"], "The name of the solution to process")); Handler = CommandHandler.Create(Execute); } private static int Execute(string file) diff --git a/.agent/rules/end-to-end-tests/end-to-end-tests.md b/.agent/rules/end-to-end-tests/end-to-end-tests.md index 75d4c80358..66b21f244f 100644 --- a/.agent/rules/end-to-end-tests/end-to-end-tests.md +++ b/.agent/rules/end-to-end-tests/end-to-end-tests.md @@ -58,9 +58,9 @@ These rules outline the structure, patterns, and best practices for writing end- - Include JSDoc comments above complex tests listing all major features/scenarios covered - When adding new functionality to existing tests, update both the test description and JSDoc comments to reflect changes -6. Structure each test with step decorators and proper monitoring: +6. Structure each test with step wrappers and proper monitoring: - All tests must start with `const context = createTestContext(page);` for proper error monitoring - - Use step decorators: `await step("Complete signup & verify account creation")(async () => { /* test logic */ })();` + - Use step wrappers: `await step("Complete signup & verify account creation")(async () => { /* test logic */ })();` - Step naming conventions: - Always follow "[Business action + details] & [expected outcome]" pattern - Use business action verbs like "Sign up", "Login", "Invite", "Rename", "Update", "Delete", "Create", "Submit" @@ -76,14 +76,14 @@ These rules outline the structure, patterns, and best practices for writing end- - Form validation pattern: Use `await blurActiveElement(page);` when updating a textbox the second time before submitting a form to trigger validation 7. Timeout Configuration: - - Always use Playwright's built-in auto-waiting assertions: `toHaveURL()`, `toBeVisible()`, `toBeEnabled()`, `toHaveValue()`, `toContainText()` - - Never add timeouts to `.click()`, `.waitForSelector()`, etc. - - Global timeout configuration is handled in the shared Playwright—do not change this + - Use Playwright's built-in auto-waiting assertions: `toHaveURL()`, `toBeVisible()`, `toBeEnabled()`, `toHaveValue()`, `toContainText()` + - Don't add timeouts to `.click()`, `.waitForSelector()`, etc. + - Global timeout configuration is handled in the shared Playwright—don't change this -8. Write deterministic tests—this is critical for reliable testing: +8. Write deterministic tests for reliable testing: - Each test should have a clear, linear flow of actions and assertions - - Never use if statements, custom error handling, or try/catch blocks in tests - - Never use regular expressions in tests—use simple string matching instead + - Don't use if statements, custom error handling, or try/catch blocks in tests + - Don't use regular expressions in tests—use simple string matching instead 9. What to test: - Enter invalid values such as empty strings, only whitespace characters, long strings, negative numbers, Unicode, etc. @@ -157,7 +157,7 @@ await step("Ensure user is deleted")(async () => { // "Ensure" is assertion pref ### ✅ Complete Test Example ```typescript -import { step } from "@shared/e2e/utils/step-decorator"; +import { step } from "@shared/e2e/utils/test-step-wrapper"; import { expectValidationError, blurActiveElement, createTestContext } from "@shared/e2e/utils/test-assertions"; import { testUser } from "@shared/e2e/utils/test-data"; diff --git a/.agent/rules/frontend/form-with-validation.md b/.agent/rules/frontend/form-with-validation.md index cf813b7145..e8e9299849 100644 --- a/.agent/rules/frontend/form-with-validation.md +++ b/.agent/rules/frontend/form-with-validation.md @@ -5,7 +5,7 @@ description: Rules for forms with validation using React Aria Components --- # Form With Validation -Carefully follow these instructions when implementing forms with validation in the frontend, covering UI components, mutation handling, and validation error display. +Guidelines for implementing forms with validation in the frontend, covering UI components, mutation handling, and validation error display. ## Implementation @@ -14,8 +14,13 @@ Carefully follow these instructions when implementing forms with validation in t 3. Use the custom `mutationSubmitter` to handle form submission and data mapping 4. Handle validation errors using the `validationErrors` prop from the mutation error 5. Show loading state in submit buttons -6. Include a `FormErrorMessage` component to display validation errors -7. For complex scenarios with multiple API calls, create a custom mutation with a `mutationFn` +6. For complex scenarios with multiple API calls, create a custom mutation with a `mutationFn` + +## Anti-patterns + +- **Do NOT use ``** - This component is deprecated. Instead: + - Use `validationErrors` prop on the `
` to show field-level validation errors + - Use toast notifications to display server errors (non-validation errors) Note: All .NET API endpoints are available as strongly typed API contracts in the frontend—when compiling the .NET backend, an OpenApi.json file is generated and the frontend build uses `openapi-typescript` to generate the API contracts. @@ -27,7 +32,7 @@ Note: All .NET API endpoints are available as strongly typed API contracts in th // ✅ DO: Use mutationSubmitter and proper error handling import { api } from "@/shared/lib/api/client"; import { mutationSubmitter } from "@repo/ui/forms/mutationSubmitter"; -import { Form, FormErrorMessage, TextField, Button } from "@repo/ui/components"; +import { Form, TextField, Button } from "@repo/ui/components"; import { Trans } from "@lingui/react/macro"; export function UserProfileForm({ user }) { @@ -55,15 +60,12 @@ export function UserProfileForm({ user }) { placeholder={t`E.g., Taylor`} /> - - - {/* Error message display */} - - + @@ -103,8 +105,6 @@ function BadUserProfileForm({ user }) { - {error && } - @@ -159,8 +159,7 @@ export function UserProfileWithAvatarForm({ user, onSuccess, onClose }) { validationErrors={saveMutation.error?.errors || updateUserMutation.error?.errors} > {/* Form fields */} - - + diff --git a/.agent/rules/frontend/frontend.md b/.agent/rules/frontend/frontend.md index 811a3be8cf..304ce31a54 100644 --- a/.agent/rules/frontend/frontend.md +++ b/.agent/rules/frontend/frontend.md @@ -5,7 +5,7 @@ description: Core rules for frontend TypeScript and React development --- # Frontend -Carefully follow these instructions for frontend TypeScript and React development, including component structure, code style, architecture patterns, and build/format steps. +Guidelines for frontend TypeScript and React development, including component structure, code style, architecture patterns, and build/format steps. ## Architecture Overview @@ -19,7 +19,7 @@ Carefully follow these instructions for frontend TypeScript and React developmen - Each self-contained system has its own WebApp - Common UI exposed via federation in `federated-modules/` - Shared components in `application/shared-webapp/` - - Never import directly between self-contained systems + - Don't import directly between self-contained systems - Use `window.location.href` for navigation between systems (not TanStack Router) 3. **API Integration**: @@ -40,20 +40,20 @@ Carefully follow these instructions for frontend TypeScript and React developmen - UI elements with different functionality should be in separate components - Avoid mixing unrelated functionality in one component - Use clear, descriptive names instead of making comments - - Never use acronyms (e.g., use `errorMessage` not `errMsg`, `button` not `btn`, `authentication` not `auth`) + - Don't use acronyms (e.g., use `errorMessage` not `errMsg`, `button` not `btn`, `authentication` not `auth`) - Prioritize code readability and maintainability - - Never introduce new npm dependencies - - Always use React Aria Components instead of native HTML elements like ``, `