@atlashub/smartstack-cli 3.38.0 → 3.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/mcp-entry.mjs +62 -37
  2. package/dist/mcp-entry.mjs.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/agents/efcore/scan.md +3 -1
  5. package/templates/agents/gitflow/commit.md +74 -0
  6. package/templates/agents/gitflow/finish.md +5 -2
  7. package/templates/agents/gitflow/init-clone.md +3 -3
  8. package/templates/agents/gitflow/init-validate.md +3 -2
  9. package/templates/agents/gitflow/merge.md +5 -0
  10. package/templates/agents/gitflow/pr.md +5 -0
  11. package/templates/agents/gitflow/start.md +8 -1
  12. package/templates/hooks/hooks.json +11 -0
  13. package/templates/hooks/wsl-dotnet-cleanup.sh +24 -0
  14. package/templates/skills/apex/references/core-seed-data.md +0 -1
  15. package/templates/skills/apex/references/examine-build-validation.md +1 -6
  16. package/templates/skills/apex/references/smartstack-frontend.md +1 -1
  17. package/templates/skills/apex/steps/step-03-execute.md +4 -9
  18. package/templates/skills/apex/steps/step-08-run-tests.md +1 -2
  19. package/templates/skills/application/SKILL.md +241 -241
  20. package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +5 -5
  21. package/templates/skills/application/references/frontend-verification.md +1 -1
  22. package/templates/skills/application/references/init-parameter-detection.md +121 -120
  23. package/templates/skills/application/references/migration-checklist-troubleshooting.md +100 -100
  24. package/templates/skills/application/references/nav-fallback-procedure.md +199 -199
  25. package/templates/skills/application/steps/step-00-init.md +130 -130
  26. package/templates/skills/application/steps/step-01-navigation.md +170 -170
  27. package/templates/skills/application/steps/step-02-permissions.md +196 -196
  28. package/templates/skills/application/steps/step-03-roles.md +182 -182
  29. package/templates/skills/application/steps/step-03b-provider.md +133 -133
  30. package/templates/skills/application/steps/step-04-backend.md +174 -174
  31. package/templates/skills/application/steps/step-05-frontend.md +1 -1
  32. package/templates/skills/application/templates-frontend.md +7 -7
  33. package/templates/skills/business-analyse/react/schema.md +836 -836
  34. package/templates/skills/business-analyse/templates/tpl-progress.md +1 -1
  35. package/templates/skills/business-analyse/templates-frd.md +1 -1
  36. package/templates/skills/efcore/SKILL.md +1 -1
  37. package/templates/skills/efcore/steps/migration/step-02-create.md +1 -14
  38. package/templates/skills/gitflow/SKILL.md +27 -4
  39. package/templates/skills/gitflow/_shared.md +86 -12
  40. package/templates/skills/gitflow/phases/abort.md +4 -0
  41. package/templates/skills/gitflow/phases/cleanup.md +4 -0
  42. package/templates/skills/gitflow/references/finish-cleanup.md +4 -0
  43. package/templates/skills/gitflow/references/init-structure-creation.md +4 -0
  44. package/templates/skills/gitflow/references/start-worktree-creation.md +1 -1
  45. package/templates/skills/ralph-loop/steps/step-04-check.md +1 -2
  46. package/templates/skills/review-code/references/smartstack-conventions.md +568 -568
  47. package/templates/skills/validate-feature/steps/step-01-compile.md +1 -6
@@ -1,568 +1,568 @@
1
- <overview>
2
- SmartStack-specific conventions and patterns. This reference is used when reviewing SmartStack projects to ensure compliance with the architecture and security standards.
3
-
4
- **IMPORTANT**: All convention validation MUST be delegated to the MCP SmartStack tools. This file documents what to look for; the MCP validates automatically.
5
- </overview>
6
-
7
- <mcp_tools>
8
- ## MCP SmartStack Tools for Code Review
9
-
10
- ### Primary Tool - Unified Code Review
11
-
12
- | Tool | Purpose | When to use |
13
- |------|---------|-------------|
14
- | `mcp__smartstack__review_code` | **Unified code review** covering 9 categories | Always use first |
15
-
16
- **Parameters:**
17
- ```
18
- scope: "all" | "changed" | "staged" # Which files to review
19
- checks: ["all"] | ["security", ...] # Categories to check
20
- severity: "all" | "blocking" # Filter by severity
21
- ```
22
-
23
- **Categories covered:**
24
- | Category | ID Prefix | Severity | What it detects |
25
- |----------|-----------|----------|-----------------|
26
- | Security | SEC-xxx | blocking | Secrets, SQL injection, XSS, missing [Authorize] |
27
- | Architecture | ARCH-xxx | critical | Layer violations, DI bypass |
28
- | Hardcoded Values | HARD-xxx | warning | Magic numbers, URLs, feature flags |
29
- | Tests | TEST-xxx | critical | Missing tests, useless assertions |
30
- | AI Hallucinations | AI-xxx | blocking | Phantom imports, non-existent methods |
31
- | Performance | PERF-xxx | warning | N+1 queries, over-fetching |
32
- | Dead Code | DEAD-xxx | warning | Unused imports, commented code, TODOs |
33
- | i18n | I18N-xxx | info | Hardcoded UI text, missing translations |
34
- | Accessibility | A11Y-xxx | info | Missing alt, ARIA, focus issues |
35
-
36
- ### Additional Tools (Optional)
37
-
38
- | Tool | Purpose | When to use |
39
- |------|---------|-------------|
40
- | `mcp__smartstack__validate_conventions` | Detailed conventions validation | Deep convention checks |
41
- | `mcp__smartstack__validate_test_conventions` | Test patterns validation | Test structure review |
42
- | `mcp__smartstack__check_migrations` | EF Core migration conflicts | Migration changes |
43
- </mcp_tools>
44
-
45
- <architecture>
46
- ## SmartStack Architecture (Clean Architecture)
47
-
48
- ```
49
- SmartStack/
50
- ├── SmartStack.Domain/ # Entities, Value Objects, Domain Events
51
- ├── SmartStack.Application/ # Services, DTOs, Commands/Queries
52
- ├── SmartStack.Infrastructure/ # EF Core, External services
53
- ├── SmartStack.Api/ # Controllers, Middleware
54
- └── SmartStack.Web/ # React frontend
55
- ```
56
-
57
- **Dependency rule**: Dependencies point inward only (Api → Application → Domain).
58
- </architecture>
59
-
60
- <entities>
61
- ## Entity Conventions
62
-
63
- <tenant_aware>
64
- **Tenant-aware entities** (most business entities):
65
- ```csharp
66
- public class Order : BaseEntity, ITenantEntity
67
- {
68
- public Guid TenantId { get; private set; }
69
-
70
- private Order() { } // EF Core constructor
71
-
72
- public static Order Create(Guid tenantId, ...)
73
- {
74
- return new Order { TenantId = tenantId, ... };
75
- }
76
- }
77
- ```
78
-
79
- **Requirements:**
80
- - [ ] Implements `ITenantEntity`
81
- - [ ] Has `TenantId` property
82
- - [ ] Private parameterless constructor
83
- - [ ] Static `Create()` factory method with `tenantId` as first parameter
84
- </tenant_aware>
85
-
86
- <system_entity>
87
- **System entities** (platform-level, no tenant):
88
- ```csharp
89
- public class Permission : SystemEntity
90
- {
91
- private Permission() { }
92
-
93
- public static Permission Create(...)
94
- {
95
- return new Permission { ... };
96
- }
97
- }
98
- ```
99
-
100
- **Requirements:**
101
- - [ ] Inherits from `SystemEntity`
102
- - [ ] NO `TenantId` property
103
- - [ ] Private parameterless constructor
104
- - [ ] Static `Create()` factory method
105
- </system_entity>
106
- </entities>
107
-
108
- <value_objects>
109
- ## Value Objects (DDD Pattern)
110
-
111
- Value Objects are immutable types defined by their attributes rather than identity. Use them to replace primitive types that carry domain meaning and validation.
112
-
113
- **When to use a Value Object:**
114
- | Instead of | Use | Why |
115
- |------------|-----|-----|
116
- | `string Email` | `Email` record | Self-validating, prevents invalid state |
117
- | `decimal Amount` + `string Currency` | `Money` record | Always consistent pair |
118
- | `string Street` + `string City` + ... | `Address` record | Cohesive group |
119
- | `DateTime Start` + `DateTime End` | `DateRange` record | Enforces Start < End invariant |
120
-
121
- **Structure (C# record):**
122
- ```csharp
123
- // Domain/ValueObjects/Email.cs
124
- public sealed record Email
125
- {
126
- public string Value { get; }
127
-
128
- public Email(string value)
129
- {
130
- if (string.IsNullOrWhiteSpace(value))
131
- throw new DomainException("Email is required");
132
- if (!value.Contains('@'))
133
- throw new DomainException("Invalid email format");
134
-
135
- Value = value.Trim().ToLowerInvariant();
136
- }
137
-
138
- public static implicit operator string(Email email) => email.Value;
139
- public override string ToString() => Value;
140
- }
141
- ```
142
-
143
- **EF Core mapping:**
144
- ```csharp
145
- // Option 1: Owned type (separate columns)
146
- builder.OwnsOne(e => e.Email, email =>
147
- {
148
- email.Property(v => v.Value).HasColumnName("Email").HasMaxLength(256);
149
- });
150
-
151
- // Option 2: Value conversion (single column)
152
- builder.Property(e => e.Email)
153
- .HasConversion(
154
- v => v.Value,
155
- v => new Email(v))
156
- .HasMaxLength(256);
157
- ```
158
-
159
- **Entity vs Value Object:**
160
- | Criteria | Entity | Value Object |
161
- |----------|--------|-------------|
162
- | Identity | Has `Id` (Guid) | No identity |
163
- | Equality | By Id | By all properties |
164
- | Mutability | Mutable (behavior methods) | Immutable (records) |
165
- | Lifecycle | Independent | Owned by entity |
166
- | Example | `User`, `Order` | `Email`, `Money`, `Address` |
167
-
168
- **Code review detection:** If an entity has `string Email`, `string Phone`, `decimal Amount` + `string Currency` as separate primitives, suggest replacing with Value Objects.
169
- </value_objects>
170
-
171
- <domain_events>
172
- ## Domain Events
173
-
174
- Domain Events capture something that happened in the domain. They enable decoupled communication between aggregates and complement the existing Workflow trigger system.
175
-
176
- **Relationship with Workflow Triggers:**
177
- | Mechanism | Scope | Use case |
178
- |-----------|-------|----------|
179
- | Domain Events (MediatR) | In-process, synchronous | Cache invalidation, audit logging, read-model updates |
180
- | Workflow Triggers (`TriggerAsync`) | Cross-process, async | Emails, webhooks, multi-step business processes |
181
- | **Combined** | Domain Event handler calls `TriggerAsync()` | Best of both worlds |
182
-
183
- **Structure:**
184
- ```csharp
185
- // Domain/Events/OrderCreatedEvent.cs
186
- public sealed record OrderCreatedEvent(
187
- Guid OrderId,
188
- Guid TenantId,
189
- Guid CreatedById,
190
- DateTime OccurredAt) : IDomainEvent;
191
-
192
- // Domain/Common/IDomainEvent.cs
193
- public interface IDomainEvent : INotification { } // MediatR marker
194
- ```
195
-
196
- **Publishing from entities:**
197
- ```csharp
198
- public class Order : BaseEntity, ITenantEntity
199
- {
200
- private readonly List<IDomainEvent> _domainEvents = new();
201
- public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
202
-
203
- public static Order Create(Guid tenantId, string description, Guid createdById)
204
- {
205
- var order = new Order
206
- {
207
- Id = Guid.NewGuid(),
208
- TenantId = tenantId,
209
- Description = description
210
- };
211
-
212
- order._domainEvents.Add(new OrderCreatedEvent(
213
- order.Id, tenantId, createdById, DateTime.UtcNow));
214
-
215
- return order;
216
- }
217
-
218
- public void ClearDomainEvents() => _domainEvents.Clear();
219
- }
220
- ```
221
-
222
- **Handling events:**
223
- ```csharp
224
- // Application/EventHandlers/OrderCreatedEventHandler.cs
225
- public class OrderCreatedEventHandler : INotificationHandler<OrderCreatedEvent>
226
- {
227
- private readonly IWorkflowService _workflowService;
228
- private readonly ILogger<OrderCreatedEventHandler> _logger;
229
-
230
- public async Task Handle(OrderCreatedEvent notification, CancellationToken ct)
231
- {
232
- _logger.LogInformation("Order {OrderId} created", notification.OrderId);
233
-
234
- // Bridge to Workflow triggers for cross-process actions
235
- await _workflowService.TriggerAsync("order.created",
236
- new Dictionary<string, object>
237
- {
238
- ["orderId"] = notification.OrderId,
239
- ["tenantId"] = notification.TenantId
240
- }, ct: ct);
241
- }
242
- }
243
- ```
244
-
245
- **Dispatching (in DbContext):**
246
- ```csharp
247
- // Infrastructure/Persistence/ApplicationDbContext.cs
248
- public override async Task<int> SaveChangesAsync(CancellationToken ct = default)
249
- {
250
- var entities = ChangeTracker.Entries<BaseEntity>()
251
- .Where(e => e.Entity.DomainEvents.Any())
252
- .ToList();
253
-
254
- var events = entities.SelectMany(e => e.Entity.DomainEvents).ToList();
255
-
256
- var result = await base.SaveChangesAsync(ct);
257
-
258
- foreach (var @event in events)
259
- await _mediator.Publish(@event, ct);
260
-
261
- foreach (var entity in entities)
262
- entity.Entity.ClearDomainEvents();
263
-
264
- return result;
265
- }
266
- ```
267
-
268
- **When to add Domain Events (code review check):**
269
- - Entity has side effects in `Create()` or behavior methods → Domain Event candidate
270
- - Service calls `TriggerAsync()` right after `SaveChangesAsync()` → Should be Domain Event
271
- - Multiple services react to the same entity change → Domain Event decouples them
272
- </domain_events>
273
-
274
- <tables>
275
- ## Table Naming Conventions
276
-
277
- **Schema**: All tables MUST specify schema explicitly.
278
- ```csharp
279
- builder.ToTable("auth_users", SchemaConstants.Core);
280
- ```
281
-
282
- **Prefixes by domain:**
283
- | Prefix | Domain | Examples |
284
- |--------|--------|----------|
285
- | `auth_` | Authentication | auth_users, auth_roles |
286
- | `nav_` | Navigation | nav_menus, nav_routes |
287
- | `cfg_` | Configuration | cfg_settings, cfg_features |
288
- | `ai_` | AI/Prompts | ai_prompts, ai_models |
289
- | `ntf_` | Notifications | ntf_templates, ntf_logs |
290
- | `wkf_` | Workflows | wkf_definitions, wkf_instances |
291
- | `doc_` | Documents | doc_files, doc_versions |
292
- | `tkt_` | Tickets/Support | tkt_tickets, tkt_comments |
293
-
294
- **Custom Application Prefixes:**
295
-
296
- Business applications define their own table prefix during `/business-analyse` cadrage phase.
297
- Format: `{2-5 lowercase letters}_` — stored in `feature.json` → `metadata.tablePrefix`.
298
-
299
- | Application | Prefix | Examples |
300
- |-------------|--------|----------|
301
- | Human Resources | `rh_` | rh_Employees, rh_Contracts |
302
- | Finance | `fi_` | fi_Invoices, fi_Payments |
303
- | CRM | `crm_` | crm_Contacts, crm_Opportunities |
304
-
305
- Rules:
306
- - Must not collide with platform prefixes
307
- - Registered in MCP config via `conventions.customTablePrefixes`
308
- - All entities in the same application share the same prefix
309
-
310
- **Schemas:**
311
- - `core` (SchemaConstants.Core): SmartStack platform tables
312
- - `extensions` (SchemaConstants.Extensions): Client-specific tables
313
- </tables>
314
-
315
- <migrations>
316
- ## Migration Naming
317
-
318
- **Format**: `{context}_v{version}_{sequence}_{Description}.cs`
319
-
320
- **Examples:**
321
- - `core_v1.0.0_001_CreateAuthUsers.cs`
322
- - `core_v1.2.0_001_AddUserProfiles.cs`
323
- - `extensions_v1.0.0_001_CreateClientOrders.cs`
324
-
325
- **Rules:**
326
- - [ ] Context: `core` or `extensions`
327
- - [ ] Version: semver format (1.0.0)
328
- - [ ] Sequence: 3-digit padded (001, 002)
329
- - [ ] Description: PascalCase, descriptive
330
- </migrations>
331
-
332
- <controllers>
333
- ## Controller Conventions
334
-
335
- **NavRoute attribute** (preferred):
336
- ```csharp
337
- [ApiController]
338
- [NavRoute("administration.users")]
339
- public class UsersController : ControllerBase // MUST be ControllerBase, NOT Controller
340
- {
341
- // Routes generated from NavRoute: /api/administration/users
342
- }
343
- ```
344
-
345
- **CRITICAL: Controller Inheritance (S6934)**
346
-
347
- ```csharp
348
- // ❌ BAD - Inheriting from Controller
349
- public class UsersController : Controller
350
- {
351
- // Brings in MVC View support (Razor, ViewData, ViewBag)
352
- // Unnecessary for API controllers
353
- }
354
-
355
- // ✅ GOOD - Inheriting from ControllerBase
356
- public class UsersController : ControllerBase
357
- {
358
- // Lightweight, API-only base class
359
- // No MVC View support (cleaner, more appropriate)
360
- }
361
- ```
362
-
363
- **Why ControllerBase?**
364
- - API controllers don't need MVC View support
365
- - `Controller` adds unnecessary dependencies (Razor, ViewData, etc.)
366
- - `ControllerBase` is lighter and more focused on APIs
367
- - Better performance and smaller footprint
368
-
369
- **NavRoute format:**
370
- - Lowercase only
371
- - Dot-separated hierarchy
372
- - Minimum 2 levels: `application.module`
373
- - Full path: `application.module`
374
-
375
- **Examples:**
376
- - `administration.users`
377
- - `administration.roles`
378
- - `crm.contacts`
379
- - `myspace.dashboard`
380
- </controllers>
381
-
382
- <services>
383
- ## Service Conventions
384
-
385
- **Interface pattern:**
386
- ```csharp
387
- // Interface
388
- public interface IUserService
389
- {
390
- Task<UserDto> GetByIdAsync(Guid id, CancellationToken ct);
391
- Task<UserDto> CreateAsync(CreateUserDto dto, CancellationToken ct);
392
- }
393
-
394
- // Implementation
395
- public class UserService : IUserService
396
- {
397
- // ...
398
- }
399
- ```
400
-
401
- **Requirements:**
402
- - [ ] Interface named `I{Name}Service`
403
- - [ ] Implementation named `{Name}Service`
404
- - [ ] All methods async with CancellationToken
405
- - [ ] Return DTOs, not entities
406
- </services>
407
-
408
- <security>
409
- ## SmartStack Security Patterns
410
-
411
- <multi_tenant>
412
- **Multi-tenant isolation:**
413
- - ALL queries MUST filter by TenantId (automatic via EF Core global filters)
414
- - NEVER expose TenantId in URLs
415
- - NEVER allow cross-tenant data access
416
- - Create methods MUST require tenantId parameter
417
-
418
- **Check for violations:**
419
- ```csharp
420
- // BAD: No tenant filter
421
- var users = await _context.Users.ToListAsync();
422
-
423
- // GOOD: Tenant filter applied (via global filter or explicit)
424
- var users = await _context.Users
425
- .Where(u => u.TenantId == _tenantId)
426
- .ToListAsync();
427
- ```
428
- </multi_tenant>
429
-
430
- <authorization>
431
- **RBAC with NavRoute:**
432
- - Controllers use `[NavRoute]` for automatic permission mapping
433
- - Permissions follow NavRoute pattern: `{navroute}.{action}`
434
- - Actions: `read`, `create`, `update`, `delete`, `*` (wildcard)
435
-
436
- **Example permissions:**
437
- - `administration.users.read`
438
- - `administration.users.create`
439
- - `administration.users.*` (all actions)
440
- </authorization>
441
-
442
- <input_validation>
443
- **FluentValidation:**
444
- ```csharp
445
- public class CreateUserDtoValidator : AbstractValidator<CreateUserDto>
446
- {
447
- public CreateUserDtoValidator()
448
- {
449
- RuleFor(x => x.Email)
450
- .NotEmpty()
451
- .EmailAddress()
452
- .MaximumLength(256);
453
- }
454
- }
455
- ```
456
- </input_validation>
457
- </security>
458
-
459
- <tests>
460
- ## Test Conventions
461
-
462
- **Naming pattern:** `{Method}_When{Condition}_Should{Result}`
463
- ```csharp
464
- public class UserServiceTests
465
- {
466
- [Fact]
467
- public async Task GetById_WhenUserExists_ShouldReturnUser() { }
468
-
469
- [Fact]
470
- public async Task GetById_WhenUserNotFound_ShouldThrowNotFoundException() { }
471
-
472
- [Fact]
473
- public async Task Create_WhenValidDto_ShouldCreateAndReturnUser() { }
474
- }
475
- ```
476
-
477
- **Structure:**
478
- ```
479
- Tests/
480
- ├── Unit/
481
- │ ├── Services/
482
- │ └── Validators/
483
- └── Integration/
484
- └── Controllers/
485
- ```
486
-
487
- **Patterns:**
488
- - [ ] AAA pattern (Arrange, Act, Assert)
489
- - [ ] FluentAssertions for assertions
490
- - [ ] Moq for mocking
491
- - [ ] Tenant isolation tests for all tenant-aware services
492
- </tests>
493
-
494
- <review_checklist>
495
- ## SmartStack Code Review Checklist
496
-
497
- **Run unified code review first:**
498
- ```
499
- mcp__smartstack__review_code
500
- scope: "changed" # or "all" or "staged"
501
- checks: ["all"] # 9 categories
502
- severity: "all"
503
- ```
504
-
505
- **The tool automatically checks:**
506
-
507
- <blocking_checks>
508
- ### Blocking Issues (Must fix before merge)
509
- **Security (SEC-xxx):**
510
- - Hardcoded credentials or secrets
511
- - SQL injection patterns
512
- - XSS vulnerabilities
513
- - Missing [Authorize] attributes
514
-
515
- **AI Hallucinations (AI-xxx):**
516
- - Non-existent imports/namespaces
517
- - Phantom method calls
518
- - Undefined types
519
- </blocking_checks>
520
-
521
- <critical_checks>
522
- ### Critical Issues (Should fix ASAP)
523
- **Architecture (ARCH-xxx):**
524
- - Layer violations (Web → Infrastructure)
525
- - Direct DbContext usage in controllers
526
- - Service instantiation instead of DI
527
-
528
- **Tests (TEST-xxx):**
529
- - Missing tests for new entities/services
530
- - Tests without real assertions
531
- </critical_checks>
532
-
533
- <warning_checks>
534
- ### Warnings (Recommended fixes)
535
- **Hardcoded Values (HARD-xxx):**
536
- - Magic numbers
537
- - Hardcoded URLs
538
- - Feature flags in code
539
-
540
- **Performance (PERF-xxx):**
541
- - N+1 queries
542
- - ToList() before Where()
543
- - Multiple API calls per page
544
-
545
- **Dead Code (DEAD-xxx):**
546
- - Unused imports
547
- - Commented code
548
- - Old TODOs
549
- </warning_checks>
550
-
551
- <info_checks>
552
- ### Info (Nice to have)
553
- **i18n (I18N-xxx):**
554
- - Hardcoded UI text
555
- - Missing translations
556
-
557
- **Accessibility (A11Y-xxx):**
558
- - Missing alt attributes
559
- - Missing ARIA labels
560
- - Non-interactive click handlers
561
- </info_checks>
562
- </review_checklist>
563
-
564
- <sources>
565
- - SmartStack Architecture Documentation
566
- - SmartStack.mcp validation tools
567
- - Clean Architecture by Robert C. Martin
568
- </sources>
1
+ <overview>
2
+ SmartStack-specific conventions and patterns. This reference is used when reviewing SmartStack projects to ensure compliance with the architecture and security standards.
3
+
4
+ **IMPORTANT**: All convention validation MUST be delegated to the MCP SmartStack tools. This file documents what to look for; the MCP validates automatically.
5
+ </overview>
6
+
7
+ <mcp_tools>
8
+ ## MCP SmartStack Tools for Code Review
9
+
10
+ ### Primary Tool - Unified Code Review
11
+
12
+ | Tool | Purpose | When to use |
13
+ |------|---------|-------------|
14
+ | `mcp__smartstack__review_code` | **Unified code review** covering 9 categories | Always use first |
15
+
16
+ **Parameters:**
17
+ ```
18
+ scope: "all" | "changed" | "staged" # Which files to review
19
+ checks: ["all"] | ["security", ...] # Categories to check
20
+ severity: "all" | "blocking" # Filter by severity
21
+ ```
22
+
23
+ **Categories covered:**
24
+ | Category | ID Prefix | Severity | What it detects |
25
+ |----------|-----------|----------|-----------------|
26
+ | Security | SEC-xxx | blocking | Secrets, SQL injection, XSS, missing [Authorize] |
27
+ | Architecture | ARCH-xxx | critical | Layer violations, DI bypass |
28
+ | Hardcoded Values | HARD-xxx | warning | Magic numbers, URLs, feature flags |
29
+ | Tests | TEST-xxx | critical | Missing tests, useless assertions |
30
+ | AI Hallucinations | AI-xxx | blocking | Phantom imports, non-existent methods |
31
+ | Performance | PERF-xxx | warning | N+1 queries, over-fetching |
32
+ | Dead Code | DEAD-xxx | warning | Unused imports, commented code, TODOs |
33
+ | i18n | I18N-xxx | info | Hardcoded UI text, missing translations |
34
+ | Accessibility | A11Y-xxx | info | Missing alt, ARIA, focus issues |
35
+
36
+ ### Additional Tools (Optional)
37
+
38
+ | Tool | Purpose | When to use |
39
+ |------|---------|-------------|
40
+ | `mcp__smartstack__validate_conventions` | Detailed conventions validation | Deep convention checks |
41
+ | `mcp__smartstack__validate_test_conventions` | Test patterns validation | Test structure review |
42
+ | `mcp__smartstack__check_migrations` | EF Core migration conflicts | Migration changes |
43
+ </mcp_tools>
44
+
45
+ <architecture>
46
+ ## SmartStack Architecture (Clean Architecture)
47
+
48
+ ```
49
+ SmartStack/
50
+ ├── SmartStack.Domain/ # Entities, Value Objects, Domain Events
51
+ ├── SmartStack.Application/ # Services, DTOs, Commands/Queries
52
+ ├── SmartStack.Infrastructure/ # EF Core, External services
53
+ ├── SmartStack.Api/ # Controllers, Middleware
54
+ └── SmartStack.Web/ # React frontend
55
+ ```
56
+
57
+ **Dependency rule**: Dependencies point inward only (Api → Application → Domain).
58
+ </architecture>
59
+
60
+ <entities>
61
+ ## Entity Conventions
62
+
63
+ <tenant_aware>
64
+ **Tenant-aware entities** (most business entities):
65
+ ```csharp
66
+ public class Order : BaseEntity, ITenantEntity
67
+ {
68
+ public Guid TenantId { get; private set; }
69
+
70
+ private Order() { } // EF Core constructor
71
+
72
+ public static Order Create(Guid tenantId, ...)
73
+ {
74
+ return new Order { TenantId = tenantId, ... };
75
+ }
76
+ }
77
+ ```
78
+
79
+ **Requirements:**
80
+ - [ ] Implements `ITenantEntity`
81
+ - [ ] Has `TenantId` property
82
+ - [ ] Private parameterless constructor
83
+ - [ ] Static `Create()` factory method with `tenantId` as first parameter
84
+ </tenant_aware>
85
+
86
+ <system_entity>
87
+ **System entities** (platform-level, no tenant):
88
+ ```csharp
89
+ public class Permission : SystemEntity
90
+ {
91
+ private Permission() { }
92
+
93
+ public static Permission Create(...)
94
+ {
95
+ return new Permission { ... };
96
+ }
97
+ }
98
+ ```
99
+
100
+ **Requirements:**
101
+ - [ ] Inherits from `SystemEntity`
102
+ - [ ] NO `TenantId` property
103
+ - [ ] Private parameterless constructor
104
+ - [ ] Static `Create()` factory method
105
+ </system_entity>
106
+ </entities>
107
+
108
+ <value_objects>
109
+ ## Value Objects (DDD Pattern)
110
+
111
+ Value Objects are immutable types defined by their attributes rather than identity. Use them to replace primitive types that carry domain meaning and validation.
112
+
113
+ **When to use a Value Object:**
114
+ | Instead of | Use | Why |
115
+ |------------|-----|-----|
116
+ | `string Email` | `Email` record | Self-validating, prevents invalid state |
117
+ | `decimal Amount` + `string Currency` | `Money` record | Always consistent pair |
118
+ | `string Street` + `string City` + ... | `Address` record | Cohesive group |
119
+ | `DateTime Start` + `DateTime End` | `DateRange` record | Enforces Start < End invariant |
120
+
121
+ **Structure (C# record):**
122
+ ```csharp
123
+ // Domain/ValueObjects/Email.cs
124
+ public sealed record Email
125
+ {
126
+ public string Value { get; }
127
+
128
+ public Email(string value)
129
+ {
130
+ if (string.IsNullOrWhiteSpace(value))
131
+ throw new DomainException("Email is required");
132
+ if (!value.Contains('@'))
133
+ throw new DomainException("Invalid email format");
134
+
135
+ Value = value.Trim().ToLowerInvariant();
136
+ }
137
+
138
+ public static implicit operator string(Email email) => email.Value;
139
+ public override string ToString() => Value;
140
+ }
141
+ ```
142
+
143
+ **EF Core mapping:**
144
+ ```csharp
145
+ // Option 1: Owned type (separate columns)
146
+ builder.OwnsOne(e => e.Email, email =>
147
+ {
148
+ email.Property(v => v.Value).HasColumnName("Email").HasMaxLength(256);
149
+ });
150
+
151
+ // Option 2: Value conversion (single column)
152
+ builder.Property(e => e.Email)
153
+ .HasConversion(
154
+ v => v.Value,
155
+ v => new Email(v))
156
+ .HasMaxLength(256);
157
+ ```
158
+
159
+ **Entity vs Value Object:**
160
+ | Criteria | Entity | Value Object |
161
+ |----------|--------|-------------|
162
+ | Identity | Has `Id` (Guid) | No identity |
163
+ | Equality | By Id | By all properties |
164
+ | Mutability | Mutable (behavior methods) | Immutable (records) |
165
+ | Lifecycle | Independent | Owned by entity |
166
+ | Example | `User`, `Order` | `Email`, `Money`, `Address` |
167
+
168
+ **Code review detection:** If an entity has `string Email`, `string Phone`, `decimal Amount` + `string Currency` as separate primitives, suggest replacing with Value Objects.
169
+ </value_objects>
170
+
171
+ <domain_events>
172
+ ## Domain Events
173
+
174
+ Domain Events capture something that happened in the domain. They enable decoupled communication between aggregates and complement the existing Workflow trigger system.
175
+
176
+ **Relationship with Workflow Triggers:**
177
+ | Mechanism | Scope | Use case |
178
+ |-----------|-------|----------|
179
+ | Domain Events (MediatR) | In-process, synchronous | Cache invalidation, audit logging, read-model updates |
180
+ | Workflow Triggers (`TriggerAsync`) | Cross-process, async | Emails, webhooks, multi-step business processes |
181
+ | **Combined** | Domain Event handler calls `TriggerAsync()` | Best of both worlds |
182
+
183
+ **Structure:**
184
+ ```csharp
185
+ // Domain/Events/OrderCreatedEvent.cs
186
+ public sealed record OrderCreatedEvent(
187
+ Guid OrderId,
188
+ Guid TenantId,
189
+ Guid CreatedById,
190
+ DateTime OccurredAt) : IDomainEvent;
191
+
192
+ // Domain/Common/IDomainEvent.cs
193
+ public interface IDomainEvent : INotification { } // MediatR marker
194
+ ```
195
+
196
+ **Publishing from entities:**
197
+ ```csharp
198
+ public class Order : BaseEntity, ITenantEntity
199
+ {
200
+ private readonly List<IDomainEvent> _domainEvents = new();
201
+ public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
202
+
203
+ public static Order Create(Guid tenantId, string description, Guid createdById)
204
+ {
205
+ var order = new Order
206
+ {
207
+ Id = Guid.NewGuid(),
208
+ TenantId = tenantId,
209
+ Description = description
210
+ };
211
+
212
+ order._domainEvents.Add(new OrderCreatedEvent(
213
+ order.Id, tenantId, createdById, DateTime.UtcNow));
214
+
215
+ return order;
216
+ }
217
+
218
+ public void ClearDomainEvents() => _domainEvents.Clear();
219
+ }
220
+ ```
221
+
222
+ **Handling events:**
223
+ ```csharp
224
+ // Application/EventHandlers/OrderCreatedEventHandler.cs
225
+ public class OrderCreatedEventHandler : INotificationHandler<OrderCreatedEvent>
226
+ {
227
+ private readonly IWorkflowService _workflowService;
228
+ private readonly ILogger<OrderCreatedEventHandler> _logger;
229
+
230
+ public async Task Handle(OrderCreatedEvent notification, CancellationToken ct)
231
+ {
232
+ _logger.LogInformation("Order {OrderId} created", notification.OrderId);
233
+
234
+ // Bridge to Workflow triggers for cross-process actions
235
+ await _workflowService.TriggerAsync("order.created",
236
+ new Dictionary<string, object>
237
+ {
238
+ ["orderId"] = notification.OrderId,
239
+ ["tenantId"] = notification.TenantId
240
+ }, ct: ct);
241
+ }
242
+ }
243
+ ```
244
+
245
+ **Dispatching (in DbContext):**
246
+ ```csharp
247
+ // Infrastructure/Persistence/ApplicationDbContext.cs
248
+ public override async Task<int> SaveChangesAsync(CancellationToken ct = default)
249
+ {
250
+ var entities = ChangeTracker.Entries<BaseEntity>()
251
+ .Where(e => e.Entity.DomainEvents.Any())
252
+ .ToList();
253
+
254
+ var events = entities.SelectMany(e => e.Entity.DomainEvents).ToList();
255
+
256
+ var result = await base.SaveChangesAsync(ct);
257
+
258
+ foreach (var @event in events)
259
+ await _mediator.Publish(@event, ct);
260
+
261
+ foreach (var entity in entities)
262
+ entity.Entity.ClearDomainEvents();
263
+
264
+ return result;
265
+ }
266
+ ```
267
+
268
+ **When to add Domain Events (code review check):**
269
+ - Entity has side effects in `Create()` or behavior methods → Domain Event candidate
270
+ - Service calls `TriggerAsync()` right after `SaveChangesAsync()` → Should be Domain Event
271
+ - Multiple services react to the same entity change → Domain Event decouples them
272
+ </domain_events>
273
+
274
+ <tables>
275
+ ## Table Naming Conventions
276
+
277
+ **Schema**: All tables MUST specify schema explicitly.
278
+ ```csharp
279
+ builder.ToTable("auth_users", SchemaConstants.Core);
280
+ ```
281
+
282
+ **Prefixes by domain:**
283
+ | Prefix | Domain | Examples |
284
+ |--------|--------|----------|
285
+ | `auth_` | Authentication | auth_users, auth_roles |
286
+ | `nav_` | Navigation | nav_menus, nav_routes |
287
+ | `cfg_` | Configuration | cfg_settings, cfg_features |
288
+ | `ai_` | AI/Prompts | ai_prompts, ai_models |
289
+ | `ntf_` | Notifications | ntf_templates, ntf_logs |
290
+ | `wkf_` | Workflows | wkf_definitions, wkf_instances |
291
+ | `doc_` | Documents | doc_files, doc_versions |
292
+ | `tkt_` | Tickets/Support | tkt_tickets, tkt_comments |
293
+
294
+ **Custom Application Prefixes:**
295
+
296
+ Business applications define their own table prefix during `/business-analyse` cadrage phase.
297
+ Format: `{2-5 lowercase letters}_` — stored in `feature.json` → `metadata.tablePrefix`.
298
+
299
+ | Application | Prefix | Examples |
300
+ |-------------|--------|----------|
301
+ | Human Resources | `rh_` | rh_Employees, rh_Contracts |
302
+ | Finance | `fi_` | fi_Invoices, fi_Payments |
303
+ | CRM | `crm_` | crm_Contacts, crm_Opportunities |
304
+
305
+ Rules:
306
+ - Must not collide with platform prefixes
307
+ - Registered in MCP config via `conventions.customTablePrefixes`
308
+ - All entities in the same application share the same prefix
309
+
310
+ **Schemas:**
311
+ - `core` (SchemaConstants.Core): SmartStack platform tables
312
+ - `extensions` (SchemaConstants.Extensions): Client-specific tables
313
+ </tables>
314
+
315
+ <migrations>
316
+ ## Migration Naming
317
+
318
+ **Format**: `{context}_v{version}_{sequence}_{Description}.cs`
319
+
320
+ **Examples:**
321
+ - `core_v1.0.0_001_CreateAuthUsers.cs`
322
+ - `core_v1.2.0_001_AddUserProfiles.cs`
323
+ - `extensions_v1.0.0_001_CreateClientOrders.cs`
324
+
325
+ **Rules:**
326
+ - [ ] Context: `core` or `extensions`
327
+ - [ ] Version: semver format (1.0.0)
328
+ - [ ] Sequence: 3-digit padded (001, 002)
329
+ - [ ] Description: PascalCase, descriptive
330
+ </migrations>
331
+
332
+ <controllers>
333
+ ## Controller Conventions
334
+
335
+ **NavRoute attribute** (preferred):
336
+ ```csharp
337
+ [ApiController]
338
+ [NavRoute("administration.users")]
339
+ public class UsersController : ControllerBase // MUST be ControllerBase, NOT Controller
340
+ {
341
+ // Routes generated from NavRoute: /api/administration/users
342
+ }
343
+ ```
344
+
345
+ **CRITICAL: Controller Inheritance (S6934)**
346
+
347
+ ```csharp
348
+ // ❌ BAD - Inheriting from Controller
349
+ public class UsersController : Controller
350
+ {
351
+ // Brings in MVC View support (Razor, ViewData, ViewBag)
352
+ // Unnecessary for API controllers
353
+ }
354
+
355
+ // ✅ GOOD - Inheriting from ControllerBase
356
+ public class UsersController : ControllerBase
357
+ {
358
+ // Lightweight, API-only base class
359
+ // No MVC View support (cleaner, more appropriate)
360
+ }
361
+ ```
362
+
363
+ **Why ControllerBase?**
364
+ - API controllers don't need MVC View support
365
+ - `Controller` adds unnecessary dependencies (Razor, ViewData, etc.)
366
+ - `ControllerBase` is lighter and more focused on APIs
367
+ - Better performance and smaller footprint
368
+
369
+ **NavRoute format:**
370
+ - Lowercase only
371
+ - Dot-separated hierarchy
372
+ - Minimum 3 levels: `application.module.section`
373
+ - Full path: `application.module.section[.resource]`
374
+
375
+ **Examples:**
376
+ - `administration.users.management`
377
+ - `administration.roles.list`
378
+ - `crm.contacts.details`
379
+ - `crm.contacts.details.notes` (with resource)
380
+ </controllers>
381
+
382
+ <services>
383
+ ## Service Conventions
384
+
385
+ **Interface pattern:**
386
+ ```csharp
387
+ // Interface
388
+ public interface IUserService
389
+ {
390
+ Task<UserDto> GetByIdAsync(Guid id, CancellationToken ct);
391
+ Task<UserDto> CreateAsync(CreateUserDto dto, CancellationToken ct);
392
+ }
393
+
394
+ // Implementation
395
+ public class UserService : IUserService
396
+ {
397
+ // ...
398
+ }
399
+ ```
400
+
401
+ **Requirements:**
402
+ - [ ] Interface named `I{Name}Service`
403
+ - [ ] Implementation named `{Name}Service`
404
+ - [ ] All methods async with CancellationToken
405
+ - [ ] Return DTOs, not entities
406
+ </services>
407
+
408
+ <security>
409
+ ## SmartStack Security Patterns
410
+
411
+ <multi_tenant>
412
+ **Multi-tenant isolation:**
413
+ - ALL queries MUST filter by TenantId (automatic via EF Core global filters)
414
+ - NEVER expose TenantId in URLs
415
+ - NEVER allow cross-tenant data access
416
+ - Create methods MUST require tenantId parameter
417
+
418
+ **Check for violations:**
419
+ ```csharp
420
+ // BAD: No tenant filter
421
+ var users = await _context.Users.ToListAsync();
422
+
423
+ // GOOD: Tenant filter applied (via global filter or explicit)
424
+ var users = await _context.Users
425
+ .Where(u => u.TenantId == _tenantId)
426
+ .ToListAsync();
427
+ ```
428
+ </multi_tenant>
429
+
430
+ <authorization>
431
+ **RBAC with NavRoute:**
432
+ - Controllers use `[NavRoute]` for automatic permission mapping
433
+ - Permissions follow NavRoute pattern: `{navroute}.{action}`
434
+ - Actions: `read`, `create`, `update`, `delete`, `*` (wildcard)
435
+
436
+ **Example permissions:**
437
+ - `administration.users.read`
438
+ - `administration.users.create`
439
+ - `administration.users.*` (all actions)
440
+ </authorization>
441
+
442
+ <input_validation>
443
+ **FluentValidation:**
444
+ ```csharp
445
+ public class CreateUserDtoValidator : AbstractValidator<CreateUserDto>
446
+ {
447
+ public CreateUserDtoValidator()
448
+ {
449
+ RuleFor(x => x.Email)
450
+ .NotEmpty()
451
+ .EmailAddress()
452
+ .MaximumLength(256);
453
+ }
454
+ }
455
+ ```
456
+ </input_validation>
457
+ </security>
458
+
459
+ <tests>
460
+ ## Test Conventions
461
+
462
+ **Naming pattern:** `{Method}_When{Condition}_Should{Result}`
463
+ ```csharp
464
+ public class UserServiceTests
465
+ {
466
+ [Fact]
467
+ public async Task GetById_WhenUserExists_ShouldReturnUser() { }
468
+
469
+ [Fact]
470
+ public async Task GetById_WhenUserNotFound_ShouldThrowNotFoundException() { }
471
+
472
+ [Fact]
473
+ public async Task Create_WhenValidDto_ShouldCreateAndReturnUser() { }
474
+ }
475
+ ```
476
+
477
+ **Structure:**
478
+ ```
479
+ Tests/
480
+ ├── Unit/
481
+ │ ├── Services/
482
+ │ └── Validators/
483
+ └── Integration/
484
+ └── Controllers/
485
+ ```
486
+
487
+ **Patterns:**
488
+ - [ ] AAA pattern (Arrange, Act, Assert)
489
+ - [ ] FluentAssertions for assertions
490
+ - [ ] Moq for mocking
491
+ - [ ] Tenant isolation tests for all tenant-aware services
492
+ </tests>
493
+
494
+ <review_checklist>
495
+ ## SmartStack Code Review Checklist
496
+
497
+ **Run unified code review first:**
498
+ ```
499
+ mcp__smartstack__review_code
500
+ scope: "changed" # or "all" or "staged"
501
+ checks: ["all"] # 9 categories
502
+ severity: "all"
503
+ ```
504
+
505
+ **The tool automatically checks:**
506
+
507
+ <blocking_checks>
508
+ ### Blocking Issues (Must fix before merge)
509
+ **Security (SEC-xxx):**
510
+ - Hardcoded credentials or secrets
511
+ - SQL injection patterns
512
+ - XSS vulnerabilities
513
+ - Missing [Authorize] attributes
514
+
515
+ **AI Hallucinations (AI-xxx):**
516
+ - Non-existent imports/namespaces
517
+ - Phantom method calls
518
+ - Undefined types
519
+ </blocking_checks>
520
+
521
+ <critical_checks>
522
+ ### Critical Issues (Should fix ASAP)
523
+ **Architecture (ARCH-xxx):**
524
+ - Layer violations (Web → Infrastructure)
525
+ - Direct DbContext usage in controllers
526
+ - Service instantiation instead of DI
527
+
528
+ **Tests (TEST-xxx):**
529
+ - Missing tests for new entities/services
530
+ - Tests without real assertions
531
+ </critical_checks>
532
+
533
+ <warning_checks>
534
+ ### Warnings (Recommended fixes)
535
+ **Hardcoded Values (HARD-xxx):**
536
+ - Magic numbers
537
+ - Hardcoded URLs
538
+ - Feature flags in code
539
+
540
+ **Performance (PERF-xxx):**
541
+ - N+1 queries
542
+ - ToList() before Where()
543
+ - Multiple API calls per page
544
+
545
+ **Dead Code (DEAD-xxx):**
546
+ - Unused imports
547
+ - Commented code
548
+ - Old TODOs
549
+ </warning_checks>
550
+
551
+ <info_checks>
552
+ ### Info (Nice to have)
553
+ **i18n (I18N-xxx):**
554
+ - Hardcoded UI text
555
+ - Missing translations
556
+
557
+ **Accessibility (A11Y-xxx):**
558
+ - Missing alt attributes
559
+ - Missing ARIA labels
560
+ - Non-interactive click handlers
561
+ </info_checks>
562
+ </review_checklist>
563
+
564
+ <sources>
565
+ - SmartStack Architecture Documentation
566
+ - SmartStack.mcp validation tools
567
+ - Clean Architecture by Robert C. Martin
568
+ </sources>