@atlashub/smartstack-cli 3.33.0 → 3.35.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 (65) hide show
  1. package/.documentation/agents.html +5 -1
  2. package/.documentation/apex.html +644 -0
  3. package/.documentation/business-analyse.html +81 -1
  4. package/.documentation/cli-commands.html +5 -1
  5. package/.documentation/commands.html +5 -1
  6. package/.documentation/efcore.html +5 -1
  7. package/.documentation/gitflow.html +5 -1
  8. package/.documentation/hooks.html +5 -1
  9. package/.documentation/index.html +60 -2
  10. package/.documentation/init.html +414 -1
  11. package/.documentation/installation.html +5 -1
  12. package/.documentation/ralph-loop.html +365 -216
  13. package/.documentation/test-web.html +5 -1
  14. package/dist/index.js +32 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/mcp-entry.mjs +7 -24
  17. package/dist/mcp-entry.mjs.map +1 -1
  18. package/package.json +1 -2
  19. package/templates/agents/ba-writer.md +142 -15
  20. package/templates/mcp-scaffolding/controller.cs.hbs +5 -1
  21. package/templates/skills/apex/SKILL.md +9 -3
  22. package/templates/skills/apex/_shared.md +49 -4
  23. package/templates/skills/{ralph-loop → apex}/references/core-seed-data.md +20 -11
  24. package/templates/skills/{ralph-loop → apex}/references/error-classification.md +2 -1
  25. package/templates/skills/apex/references/post-checks.md +463 -3
  26. package/templates/skills/apex/references/smartstack-api.md +76 -8
  27. package/templates/skills/apex/references/smartstack-frontend.md +74 -1
  28. package/templates/skills/apex/references/smartstack-layers.md +21 -3
  29. package/templates/skills/apex/steps/step-00-init.md +121 -1
  30. package/templates/skills/apex/steps/step-01-analyze.md +58 -0
  31. package/templates/skills/apex/steps/step-02-plan.md +36 -0
  32. package/templates/skills/apex/steps/step-03-execute.md +114 -7
  33. package/templates/skills/apex/steps/step-04-examine.md +116 -2
  34. package/templates/skills/business-analyse/SKILL.md +31 -20
  35. package/templates/skills/business-analyse/_module-loop.md +68 -9
  36. package/templates/skills/business-analyse/_shared.md +80 -21
  37. package/templates/skills/business-analyse/questionnaire/00-application.md +4 -2
  38. package/templates/skills/business-analyse/questionnaire/00b-project.md +85 -0
  39. package/templates/skills/business-analyse/references/deploy-modes.md +69 -0
  40. package/templates/skills/business-analyse/references/team-orchestration.md +158 -7
  41. package/templates/skills/business-analyse/schemas/application-schema.json +15 -1
  42. package/templates/skills/business-analyse/schemas/project-schema.json +490 -0
  43. package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +2 -1
  44. package/templates/skills/business-analyse/steps/step-00-init.md +220 -38
  45. package/templates/skills/business-analyse/steps/step-01-cadrage.md +184 -5
  46. package/templates/skills/business-analyse/steps/step-01b-applications.md +423 -0
  47. package/templates/skills/business-analyse/steps/step-02-decomposition.md +23 -6
  48. package/templates/skills/business-analyse/steps/step-03c-compile.md +14 -2
  49. package/templates/skills/business-analyse/steps/step-03d-validate.md +32 -7
  50. package/templates/skills/business-analyse/steps/step-04a-collect.md +111 -0
  51. package/templates/skills/business-analyse/steps/step-05a-handoff.md +296 -103
  52. package/templates/skills/business-analyse/steps/step-05b-deploy.md +46 -14
  53. package/templates/skills/documentation/SKILL.md +92 -2
  54. package/templates/skills/ralph-loop/SKILL.md +14 -17
  55. package/templates/skills/ralph-loop/references/category-rules.md +63 -683
  56. package/templates/skills/ralph-loop/references/compact-loop.md +188 -428
  57. package/templates/skills/ralph-loop/references/section-splitting.md +439 -0
  58. package/templates/skills/ralph-loop/references/team-orchestration.md +13 -14
  59. package/templates/skills/ralph-loop/steps/step-01-task.md +27 -0
  60. package/templates/skills/ralph-loop/steps/step-02-execute.md +80 -691
  61. package/templates/skills/ralph-loop/steps/step-03-commit.md +38 -79
  62. package/templates/skills/ralph-loop/steps/step-04-check.md +39 -58
  63. package/templates/skills/ralph-loop/steps/step-05-report.md +31 -123
  64. package/scripts/health-check.sh +0 -168
  65. package/scripts/postinstall.js +0 -18
@@ -1,716 +1,96 @@
1
- # Category Execution Rules
1
+ # Category Ordering & Dependency Rules
2
2
 
3
- > **Loaded by:** step-02 (first iteration) and compact-loop.md (subsequent iterations)
4
- > **Purpose:** Defines execution rules, MCP tools, folder paths, and forbidden patterns per task category.
3
+ > **Loaded by:** step-02 (task ordering validation) and compact-loop.md (batch grouping)
4
+ > **Purpose:** Defines execution order, dependencies, and batch grouping rules.
5
+ > **Note:** All execution rules, MCP tool usage, conventions, and POST-CHECKs are in `/apex` references.
5
6
 
6
7
  ---
7
8
 
8
- ## Domain
9
+ ## Execution Order
9
10
 
10
- **Folder:** `Domain/Entities/{ContextPascal}/{App}/{Module}/`
11
- **Enums:** `Domain/Enums/{ContextPascal}/{App}/{Module}/`
12
- **Exceptions:** `Domain/Exceptions/{ContextPascal}/{App}/{Module}/`
13
- **MCP:** `validate_conventions`
11
+ Tasks MUST be executed in this order. Each category depends on the previous ones.
14
12
 
15
- Rules:
16
- - Entity base class inheritance (AuditableEntity, IHasData, ISoftDeletable)
17
- - Audit fields: CreatedAt, CreatedBy, ModifiedAt, ModifiedBy
18
- - Domain events for state changes
19
- - Value objects for composite values
13
+ | Order | Category | Depends On | Description |
14
+ |-------|----------|------------|-------------|
15
+ | 0 | `domain` | — | Entities, enums, value objects |
16
+ | 1 | `infrastructure` | domain | EF configs, migrations, seed data |
17
+ | 2 | `application` | infrastructure | Services, DTOs, validators, DI |
18
+ | 3 | `api` | application | Controllers, route attributes |
19
+ | 4 | `seedData` | api | Navigation, permissions, roles, dev data |
20
+ | 5 | `frontend` | seedData | Pages, routes, i18n, components |
21
+ | 6 | `test` | frontend | Unit tests, integration tests, coverage |
20
22
 
21
- ---
22
-
23
- ## Infrastructure — EF Core Configurations
24
-
25
- **Folder:** `Infrastructure/Persistence/Configurations/{ContextPascal}/{App}/{Module}/`
26
- **MCP:** `validate_conventions`, `check_migrations`
27
-
28
- Rules:
29
- - One configuration per entity
30
- - Table name = plural entity name
31
- - All relationships explicitly configured (no convention-based)
32
- - Indexes on foreign keys and frequently queried fields
33
- - Register DbSet in ExtensionsDbContext
23
+ > **Key dependency:** `frontend` depends on `seedData` (not just `api`).
24
+ > Navigation seed data drives the menu — without it, frontend routes have no menu entries.
34
25
 
35
26
  ---
36
27
 
37
- ## Infrastructure — Migration (BLOCKING)
38
-
39
- > **CRITICAL:** After EF configs, a migration MUST be created and applied.
40
- > Without migrations, no database schema = no tests = no API = broken module.
41
-
42
- **Folder:** `Infrastructure/Persistence/Migrations/` (NEVER subdirectories)
43
- **MCP:** `suggest_migration` → get migration name
44
-
45
- Execution sequence:
46
- 0. Ensure `dotnet ef` is on PATH (platform-aware):
47
- ```bash
48
- if ! dotnet ef --version &>/dev/null; then
49
- for TOOLS_DIR in "$USERPROFILE/.dotnet/tools" "$HOME/.dotnet/tools" "$LOCALAPPDATA/Microsoft/dotnet/tools"; do
50
- [ -n "$TOOLS_DIR" ] && [ -d "$TOOLS_DIR" ] && export PATH="$TOOLS_DIR:$PATH"
51
- done
52
- dotnet ef --version &>/dev/null || { echo "ERROR: dotnet-ef not found. Install: dotnet tool install --global dotnet-ef"; exit 1; }
53
- fi
54
- ```
55
- > **Why:** On Windows (Git Bash), `$USERPROFILE/.dotnet/tools` is the correct path.
56
- > NEVER use `$HOME/.dotnet/tools` alone — on WSL, `$HOME` resolves to `/home/{user}` where .NET SDK is not installed.
57
- 1. Call `mcp__smartstack__suggest_migration` → get standardized name
58
- 2. `dotnet ef migrations add {Name} --project src/{Infra}.csproj --startup-project src/{Api}.csproj -o Persistence/Migrations`
59
- 3. Cleanup corrupted EF Core artifacts: `for d in src/*/bin?Debug; do [ -d "$d" ] && rm -rf "$d"; done`
60
- 4. `dotnet ef database update --project src/{Infra}.csproj --startup-project src/{Api}.csproj`
61
- 5. `dotnet build --no-restore` → verify build passes
62
-
63
- **BLOCKING:** If migration fails, DO NOT proceed. Fix the EF configs first.
64
-
65
- **Rules:**
66
- - NEVER create subdirectories under Migrations/
67
- - Always use `-o Persistence/Migrations` flag
68
- - One migration per module (all entities together)
69
- - Migration name from MCP (NEVER invented)
70
-
71
- ---
72
-
73
- ## Infrastructure — Seed Data (MANDATORY reference loading)
74
-
75
- **Folder:** `Infrastructure/Persistence/Seeding/Data/{Module}/`
76
- **MCP:** `generate_permissions` (PRIMARY), fallback to templates
77
-
78
- > **IF** task description contains "seed data", "SeedData", "NavigationModule",
79
- > "PermissionsSeedData", "RolesSeedData", or "IClientSeedDataProvider":
80
- > **THEN read `references/core-seed-data.md`** — this is MANDATORY, DO NOT improvise.
81
-
82
- **Seed Data Chain (9 files minimum):**
28
+ ## Dependency Rules
83
29
 
84
- Application-level (created ONCE, shared across modules):
85
- - **NavigationApplicationSeedData.cs**: Application navigation entry (MUST be first). Provides ApplicationId.
86
- - **ApplicationRolesSeedData.cs**: 4 application-scoped roles (admin, manager, contributor, viewer). Provides role entries for SeedRolesAsync().
87
-
88
- Per-module:
89
- - **NavigationModuleSeedData.cs**: deterministic GUIDs (SHA256), 4 languages (fr, en, it, de)
90
- - **Permissions.cs**: Static permission constants (`Permissions.{Module}.Read`). Referenced by `[RequirePermission]`.
91
- - **PermissionsSeedData.cs**: MCP `generate_permissions` first, fallback to template
92
- - **RolesSeedData.cs**: code-based role-permission mapping (Admin=wildcard, Manager=CRU, Contributor=CR, Viewer=R)
93
-
94
- Per-module (MANDATORY when `seedDataCore.navigationSections` exists in feature.json):
95
- - **NavigationSectionSeedData**: section entries, section translations (4 languages), section-level permissions, and section-level role mappings — all within `NavigationModuleSeedData.cs`, `PermissionsSeedData.cs`, and `RolesSeedData.cs`
96
- - Section-level permissions: wildcard + CRUD per section (same pattern as module-level)
97
- - Section-level role mappings: Admin=wildcard, Manager=CRU, Contributor=CR, Viewer=R per section
98
-
99
- Infrastructure:
100
- - **SeedConstants.cs**: shared deterministic GUIDs (ApplicationId, ModuleIds)
101
- - **{App}SeedDataProvider.cs**: implements IClientSeedDataProvider with 4 methods
102
- - DI: `services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>()`
103
-
104
- **Rules:**
105
- - IClientSeedDataProvider: SeedNavigationAsync + SeedRolesAsync + SeedPermissionsAsync + SeedRolePermissionsAsync
106
- - Admin=wildcard(*), Manager=CRU (read+create+update), Contributor=CR (read+create), Viewer=R (read only)
107
-
108
- **Business seed data (DevDataSeeder):**
109
- - ALL seeded business entities MUST include `TenantId = {tenantGuid}`
110
- - Reference entities (types, categories, statuses) MUST set TenantId
111
- - Use deterministic TenantId from SeedConstants (NEVER hardcoded inline)
112
- - DevDataSeeder MUST implement `IDevDataSeeder` with idempotent `SeedAsync()` method
113
-
114
- **Section route conventions (BLOCKING):**
115
- - `list` section route = module route (e.g., `/business/human-resources/employees`) — NO `/list` suffix
116
- - `detail` section route = module route + `/:id` (e.g., `/business/human-resources/employees/:id`) — NOT `/detail/:id`
117
- - Other sections (dashboard, approve, import) = module route + `/{section-kebab}` (normal)
118
- - FORBIDDEN: `/{module}/list`, `/{module}/detail/:id` — these are CRUD view modes, not sub-areas
119
-
120
- **FORBIDDEN:**
121
- - `Guid.NewGuid()` → use deterministic GUIDs
122
- - Empty seed data classes with only GUIDs and no seeding methods
123
- - Missing translations (must have all 4 languages)
124
- - Seeding business entities WITHOUT `TenantId`
125
- - Navigation section routes ending in `/list` or `/detail/:id`
126
-
127
- ### POST-CHECK: Navigation translations diacritical marks
128
-
129
- After generating `NavigationTranslationSeedEntry` strings, verify ALL non-English translations contain proper diacritical marks:
130
-
131
- - **FR** must use: é, è, ê, ë, à, â, ç, ù, û, ô, î (e.g., "Employés" NOT "Employes", "Activités" NOT "Activites")
132
- - **DE** must use: ä, ö, ü, ß (e.g., "Aktivitäten" NOT "Aktivitaten")
133
- - **IT** must use: à, è, é, ì, ò, ù (e.g., "Attività" NOT "Attivita")
134
-
135
- Cross-reference navigation seed data labels with the corresponding frontend `i18n/index.ts` translations for consistency.
136
-
137
- **BLOCKING** if any `NavigationTranslationSeedEntry` for fr/de/it contains only ASCII characters where diacritical marks are expected in the target language.
30
+ 1. **A task can only start when ALL its dependencies are `completed`**
31
+ 2. **If a dependency is `failed` or `blocked`, the task becomes `blocked`**
32
+ 3. **Cross-category dependencies are implicit** domain before infrastructure, etc.
33
+ 4. **Same-category tasks MAY run in parallel** (batched together)
34
+ 5. **PRD `dependencies[]` array overrides implicit ordering** — explicit deps always checked first
138
35
 
139
36
  ---
140
37
 
141
- ## Infrastructure SQL Objects
142
-
143
- **Folder:** `Infrastructure/Persistence/SqlObjects/Functions/`
144
-
145
- Rules:
146
- - `.sql` files with `CREATE OR ALTER` for idempotency
147
- - `SqlObjectHelper.cs` for embedded resource loading
148
-
149
- ---
38
+ ## Batch Grouping Rules
150
39
 
151
- ## Post-Infrastructure Build Check (BLOCKING)
40
+ When selecting tasks for a batch:
152
41
 
153
- > **After ALL infrastructure tasks (configs + migration + seed data) are done:**
42
+ 1. **Group by category** take the first eligible category with pending tasks
43
+ 2. **Max batch size: 5 tasks** — prevents overloading a single apex invocation
44
+ 3. **Same category only** — never mix domain + infrastructure in one batch
45
+ 4. **Order within batch: by task ID** (ascending) — preserves PRD creation order
154
46
 
155
- ```bash
156
- dotnet build --no-restore
47
+ ```javascript
48
+ const eligible = prd.tasks.filter(/* pending + deps met */);
49
+ const firstCategory = eligible[0].category;
50
+ const batch = eligible.filter(t => t.category === firstCategory).slice(0, 5);
157
51
  ```
158
52
 
159
- **BLOCKING:** Build MUST pass before proceeding to application/api/test/frontend.
160
- If build fails, fix infrastructure code first.
161
-
162
- > **NOTE:** If build passes but you suspect assembly issues (e.g., new packages added to Infrastructure
163
- > that may not be in the meta-package), also run a quick startup check:
164
- > `dotnet run --project {ApiProject} --urls "http://localhost:5098" &` and verify the process survives 5 seconds.
165
- > See `references/error-classification.md` for error classification if it crashes.
166
-
167
53
  ---
168
54
 
169
- ## Application
170
-
171
- **Services:** `Application/Services/{ContextPascal}/{App}/{Module}/`
172
- **DTOs:** `Application/DTOs/{ContextPascal}/{App}/{Module}/`
173
- **Validators:** `Application/Validators/{ContextPascal}/{App}/{Module}/`
174
- **MCP:** `validate_conventions`, `scaffold_extension`
175
-
176
- Rules:
177
- - CQRS pattern with MediatR
178
- - FluentValidation for all commands
179
- - DTOs separate from domain entities
180
- - Service interfaces in Application, implementations in Infrastructure
181
-
182
- **Tenant isolation (BLOCKING — SECURITY CRITICAL):**
183
-
184
- > **ROOT CAUSE (test-v4-005):** Services were generated WITHOUT TenantId filtering,
185
- > creating cross-tenant data leakage on ALL 70+ CRUD endpoints.
186
- > This is an OWASP A01 (Broken Access Control) vulnerability.
187
-
188
- - ALL queries on tenant entities MUST include `.Where(x => x.TenantId == _currentUser.TenantId)`
189
- - ALL entity creation MUST pass `_currentUser.TenantId` as first parameter to `Entity.Create(tenantId, ...)`
190
- - NEVER use `new Entity { }` without `TenantId =` — always prefer factory method `Entity.Create()`
191
- - NEVER use `Guid.Empty` as a placeholder for userId, tenantId, or any business identifier
192
- - Service constructor MUST inject `ICurrentUserService _currentUser` to access TenantId
193
-
194
- **MANDATORY Service Template (use as skeleton for ALL services):**
195
-
196
- ```csharp
197
- public class {Entity}Service : I{Entity}Service
198
- {
199
- private readonly IExtensionsDbContext _db;
200
- private readonly ICurrentUserService _currentUser;
201
-
202
- public {Entity}Service(IExtensionsDbContext db, ICurrentUserService currentUser)
203
- {
204
- _db = db;
205
- _currentUser = currentUser;
206
- }
207
-
208
- public async Task<PaginatedResult<{Entity}Response>> GetAllAsync(/* filters */, CancellationToken ct)
209
- {
210
- var tenantId = _currentUser.TenantId;
211
- var query = _db.{Entities}
212
- .Where(x => x.TenantId == tenantId) // ← MANDATORY tenant filter
213
- .AsQueryable();
214
- // ... apply filters, pagination, projection to Response DTO
215
- }
216
-
217
- public async Task<{Entity}Response?> GetByIdAsync(Guid id, CancellationToken ct)
218
- {
219
- var tenantId = _currentUser.TenantId;
220
- var entity = await _db.{Entities}
221
- .Where(x => x.TenantId == tenantId) // ← MANDATORY tenant filter
222
- .FirstOrDefaultAsync(x => x.Id == id, ct);
223
- // ...
224
- }
225
-
226
- public async Task<{Entity}Response> CreateAsync(Create{Entity}Dto dto, CancellationToken ct)
227
- {
228
- var entity = {Entity}.Create(_currentUser.TenantId, /* dto fields */);
229
- // ← TenantId as FIRST parameter
230
- _db.{Entities}.Add(entity);
231
- await _db.SaveChangesAsync(ct);
232
- return MapToResponse(entity);
233
- }
234
-
235
- public async Task<{Entity}Response> UpdateAsync(Guid id, Update{Entity}Dto dto, CancellationToken ct)
236
- {
237
- var entity = await _db.{Entities}
238
- .Where(x => x.TenantId == _currentUser.TenantId) // ← MANDATORY
239
- .FirstOrDefaultAsync(x => x.Id == id, ct)
240
- ?? throw new NotFoundException(nameof({Entity}), id);
241
- // ... update fields
242
- }
55
+ ## PRD Category Completeness
243
56
 
244
- public async Task DeleteAsync(Guid id, CancellationToken ct)
245
- {
246
- var entity = await _db.{Entities}
247
- .Where(x => x.TenantId == _currentUser.TenantId) // ← MANDATORY
248
- .FirstOrDefaultAsync(x => x.Id == id, ct)
249
- ?? throw new NotFoundException(nameof({Entity}), id);
250
- // ...
251
- }
57
+ A valid PRD MUST contain tasks in these categories:
252
58
 
253
- private static {Entity}Response MapToResponse({Entity} entity) => new()
254
- {
255
- // Map entity fields to response DTO
256
- };
257
- }
258
- ```
259
-
260
- **POST-CHECK after writing ANY service:** Grep the file for `TenantId`. If 0 occurrences → FAIL, rewrite with tenant filtering.
261
-
262
- **Lifecycle-aware services:**
263
- - Services operating on entities with a `lifeCycle` (status field) MUST validate entity state before mutations
264
- - Example: `if (entity.Status == EmployeeStatus.Terminated) throw new BusinessException("Cannot update terminated employee")`
265
- - Guard checks MUST match the `allowedTransitions` from the entity's lifecycle definition
266
- - Delete/archive operations MUST respect terminal states (`isTerminal: true`)
267
-
268
- **Validator completeness (BLOCKING):**
269
- - For EVERY `Create{Entity}Validator`, a matching `Update{Entity}Validator` MUST exist
270
- - ALL validators MUST be registered in `Application/DependencyInjection.cs`:
271
- `services.AddValidatorsFromAssemblyContaining<Create{Entity}Validator>()`
272
- - DependencyInjection.cs MUST NOT be empty or contain only TODO comments
273
- - After writing validators, VERIFY DI registration exists — if missing, add it immediately
59
+ - `domain` at least 1 entity task
60
+ - `infrastructure` — at least 1 EF config + migration task
61
+ - `application` at least 1 service task
62
+ - `api` — at least 1 controller task
63
+ - `seedData` — at least 1 navigation/permission task
64
+ - `frontend` — at least 1 page task
65
+ - `test` — at least 1 test task
274
66
 
275
- **POST-CHECK after writing validators:**
276
- ```bash
277
- # Count Create validators vs Update validators
278
- CREATE_COUNT=$(find . -path "*/Validators/*" -name "Create*Validator.cs" | wc -l)
279
- UPDATE_COUNT=$(find . -path "*/Validators/*" -name "Update*Validator.cs" | wc -l)
280
- if [ "$CREATE_COUNT" -ne "$UPDATE_COUNT" ]; then
281
- echo "VALIDATOR MISMATCH: $CREATE_COUNT Create vs $UPDATE_COUNT Update → MUST be equal"
282
- # List missing UpdateValidators and CREATE them
283
- fi
284
- ```
285
-
286
- **Mapper pattern (DRY):**
287
- - Each service MUST include a `private static {Entity}Response MapToResponse({Entity} entity)` method
288
- - For complex mappings with related entities, use an extension method in `Application/Mappings/{Module}Mappings.cs`
289
- - NEVER duplicate mapping logic between GetAll, GetById, Create, Update — always call MapToResponse
290
-
291
- **FORBIDDEN:**
292
- - Empty DependencyInjection.cs with `// TODO` placeholder
293
- - CreateValidator without matching UpdateValidator
294
- - Validators not registered in DI container
295
- - Service query without TenantId filter (cross-tenant data leak)
296
- - `new Entity { }` without TenantId assignment
297
- - `Guid.Empty` as a business value in services or controllers
298
- - Entity.Create() without tenantId as first parameter
299
- - Duplicated mapping logic (entity→response) in multiple methods
67
+ If any category is missing, ralph step-01 injects a guardrail task.
300
68
 
301
69
  ---
302
70
 
303
- ## API
304
-
305
- **Controllers:** `Api/Controllers/{ContextShort}/{App}/{Entity}Controller.cs`
306
- **MCP:** `scaffold_routes`, `validate_security`
307
-
308
- **Context-to-folder mapping (`{ContextShort}`):**
309
-
310
- | NavRoute Prefix | Controller Folder |
311
- |-----------------|-------------------|
312
- | `platform.administration` | `Admin` |
313
- | `platform.support` | `Support` |
314
- | `business.*` | `Business` |
315
- | `personal.*` | `User` |
316
-
317
- **Rules:**
318
- - `[RequirePermission(Permissions.{Module}.{Action})]` on EVERY endpoint (NOT `[Authorize]`)
319
- - Swagger XML documentation
320
- - Consistent route patterns: `api/{context}/{app}/{module}`
321
- - Return DTOs, never domain entities
322
- - **ALL GetAll endpoints MUST accept `?search=` query parameter** (enables EntityLookup on frontend)
323
- - **GetAll returns paginated results:** `PaginatedResult<T>` with items, totalCount, page, pageSize
324
-
325
- **Controller coverage (BLOCKING):**
326
- - EVERY entity in the module MUST have a controller with CRUD endpoints (GET list, GET by id, POST, PUT, DELETE)
327
- - Reference/lookup entities (types, categories, statuses) MUST also have controllers — they are needed for dropdowns and configuration
328
- - Count: `controllers created >= entities in module`. If fewer → FAIL
329
-
330
- **Section-level controllers (CONDITIONAL: when `navSections[]` defined in feature.json):**
331
- - File path: `Api/Controllers/{ContextShort}/{App}/{Section}Controller.cs`
332
- - NavRoute attribute: `[NavRoute("{context}.{app}.{module}.{section}")]`
333
- - Permission attribute: `[RequirePermission(Permissions.{Module}.{Section}.{Action})]`
334
- - Route prefix: `api/{context}/{app}/{module}/{section}`
335
- - Each section gets its own controller with CRUD endpoints scoped to the section
336
-
337
- **FORBIDDEN:**
338
- - `[Authorize]` without specific permission → use `[RequirePermission]`
339
- - Returning domain entities directly
340
- - Skipping controllers for reference/lookup entities
341
-
342
- ---
343
-
344
- ## Frontend (MCP-FIRST PROTOCOL)
345
-
346
- > **CRITICAL:** Frontend code is generated via MCP tools, NOT written from scratch.
347
-
348
- **Page hierarchy:** `src/pages/{ContextPascal}/{AppPascal}/{Module}/{Page}.tsx`
349
- **FORBIDDEN:** `src/pages/{Module}/` (flat structure without Context/App)
350
-
351
- **Execution sequence (IN ORDER):**
352
- 0. **Read back feature.json** (via `prd.source.featurePath`) — The PRD only carries the file list (structural skeleton). The behavioral specs (columnDefs, componentMapping, layout, rowActions, emptyState, tab structure, i18n key-value pairs, dashboard KPIs, state machine transitions) are ONLY in the original feature.json. Without this step, generated code will be generic and miss entity-specific UI details.
353
- 1. `mcp__smartstack__scaffold_api_client` → API client + types + React Query hook
354
- 2. `mcp__smartstack__scaffold_routes` (with `outputFormat: "clientRoutes"`) → route registry + route fragments
355
- 3. **Wire routes to App.tsx (detect pattern: `contextRoutes` array OR JSX `<Route>`):**
356
- - **Pattern A** (`contextRoutes: ContextRouteExtensions` in App.tsx): add to `contextRoutes.{context}[]` with RELATIVE paths → auto-injected into both standard + tenant trees
357
- - **Pattern B** (JSX `<Route>` in App.tsx): insert `<Route>` entries inside correct Layout wrapper (BOTH standard AND tenant-prefixed `/t/:slug/...` blocks)
358
- 4. Create pages using **`/ui-components` skill** — **LOAD the skill step files before creating any page.** This is MANDATORY for entity lists, grids, tables, dashboards, charts. The skill ensures CSS variables, EntityCard, SmartTable, loading/error/empty states, and responsive grid patterns. **Pages MUST use `t()` from `useTranslation()` for ALL visible text from the very first line of code.** Do NOT hardcode English strings with the intent of adding i18n later — the i18n keys are created in step 6 based on what keys the pages reference. Pattern: `t('{moduleLower}:actions.create', 'Create')`.
359
- 5. Create preferences hook: `use{Module}Preferences.ts`
360
- 6. Generate i18n (4 languages: fr, en, it, de) — see I18n section below for detailed rules
361
- 7. `npm run typecheck` MUST pass (BLOCKING)
362
-
363
- **Components:**
364
- - Lists: `SmartTable` + `SmartFilter` (NOT HTML `<table>`)
365
- - Grids: `EntityCard` (NOT custom `<div>` cards)
366
- - Detail: `EntityDetailCard`, `StatusBadge`, tab layout
367
- - Forms: `SmartForm` with FluentValidation-backed fields — **MUST be full pages, NEVER modals**
368
- - FK fields: `EntityLookup` for searchable entity selection (NEVER plain text for Guid FK)
369
- - Dashboard: `StatCard`, Recharts components
370
-
371
- **Per-page /ui-components validation (MANDATORY before commit):**
372
- - After creating EACH page, verify it follows /ui-components patterns:
373
- 1. List pages: uses `DataTable` or `SmartTable` (NOT raw `<table>`)
374
- 2. List pages: uses `EntityCard` for card/grid views (NOT custom `<div>` cards)
375
- 3. All pages: ALL colors use CSS variables (`bg-[var(--xxx)]`) — ZERO hardcoded Tailwind colors
376
- 4. All pages: loading skeleton, error state with retry, empty state are present
377
- 5. All pages: ALL visible text uses `t()` from `useTranslation()`
378
- 6. Delete actions: use `ConfirmDialog` component (NOT `window.confirm()`)
379
- 7. Status displays: use `StatusBadge` with CSS variable colors (NOT inline styled spans)
380
- - **If ANY check fails: fix the page BEFORE moving to the next page**
381
- - The POST-CHECKs in step-02 enforce these rules with BLOCKING severity
382
-
383
- **Form pages (CRITICAL — ZERO modals/popups/drawers):**
384
- - Create form: `EntityCreatePage.tsx` with route `/{module}/create`
385
- - Edit form: `EntityEditPage.tsx` with route `/{module}/:id/edit`
386
- - ALL forms are full pages with their own URL — NEVER Modal/Dialog/Drawer/Popup
387
- - Back button with `navigate(-1)` on every form page
388
- - Use React.lazy() + `<Suspense fallback={<PageLoader />}>` for ALL page imports (not just forms — see "Lazy loading" section above)
389
- - **Form tests (MANDATORY):** Co-located `EntityCreatePage.test.tsx` and `EntityEditPage.test.tsx`
390
- → Cover: rendering, validation, submit, pre-fill (edit), navigation, error handling
391
-
392
- **Layout wrapper mapping:**
393
-
394
- | Context | Layout | Route path |
395
- |---------|--------|------------|
396
- | `platform.*` | `AdminLayout` | `/platform` |
397
- | `business.*` | `BusinessLayout` | `/business` |
398
- | `personal.*` | `UserLayout` | `/personal/myspace` |
399
-
400
- **Route naming convention (CRITICAL — must match backend):**
401
- - Frontend route paths MUST use **kebab-case** matching the backend API route convention
402
- - Convention: `HumanResources` (C# namespace) → `human-resources` (URL segment)
403
- - ALL multi-word route segments MUST contain hyphens: `time-management`, `human-resources`, `leave-balance`
404
- - FORBIDDEN: `humanresources`, `timemanagement`, `leavebalance` (concatenated without hyphens)
405
- - Frontend path `/business/human-resources/clients` matches API `api/business/human-resources/clients`
406
- - Navigation seed data Route values MUST also use kebab-case
407
- - The POST-CHECK in step-02 will BLOCK if frontend routes don't match backend kebab-case convention
408
-
409
- **Section-level pages (CONDITIONAL: when `navSections[]` defined in feature.json):**
410
- - Page file: `src/pages/{ContextPascal}/{AppPascal}/{Module}/{Section}Page.tsx`
411
- - Route: nested as child of module route in App.tsx (e.g., `/business/human-resources/projects/timesheets`)
412
- - Add to `contextRoutes.{context}[]` (Pattern A) or as nested `<Route>` (Pattern B)
413
- - Each section page has its own route and permission check
414
-
415
- **React Router mapping for sections:**
416
- - `list` section → already the module's `index: true` route (NO separate `path: 'list'`)
417
- - `detail` section → already the module's `path: ':id'` route (NO separate `path: 'detail'`)
418
- - Other sections → `path: '{section-kebab}'` as child of module route
419
- - FORBIDDEN frontend paths: `path: 'list'`, `path: 'detail'` — these are handled by the module's index and :id routes
420
-
421
- **CSS:** Variables ONLY → `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`
422
-
423
- **Form error handling (MANDATORY):**
424
- - ALL SmartForm components MUST include `onError` callback for API errors
425
- - Validation errors MUST be displayed inline next to the relevant field
426
- - API errors (4xx, 5xx) MUST show a user-friendly error notification (toast/banner)
427
- - Forms MUST preserve user input on error (no data loss on failed submit)
428
-
429
- **Hook error handling pattern (MANDATORY):**
430
- - ALL hooks MUST preserve server validation errors from axios responses
431
- - Pattern: `catch (err) { if (axios.isAxiosError(err)) { return err.response?.data; } throw err; }`
432
- - NEVER discard `err.response.data` — it contains field-level validation errors from FluentValidation
433
- - Error messages MUST use i18n: `setError(t('{mod}:errors.saveFailed'))` — NEVER hardcoded English strings
434
- - Toast/notification messages MUST also use i18n
435
-
436
- **Service call pattern (MANDATORY — NO custom entity hooks):**
437
-
438
- > **ROOT CAUSE (test-v4-014):** ralph-loop generated custom hooks (`useEmployees()`, `useProjects()`,
439
- > `useTimeManagement()`) that wrapped services with `useState`/`useEffect`/`try-catch`.
440
- > These hooks swallowed 401 errors with generic `catch → setError(string)`, creating a race condition
441
- > with SmartStack's auth interceptor (`window.location.href = "/login"`).
442
- > Result: token cleared + redirect + React re-render → cascade of unauthenticated requests.
443
-
444
- - Pages MUST call API services **directly** in `useCallback` + `useEffect` — NOT through custom wrapper hooks
445
- - FORBIDDEN: `useEmployees()`, `useProjects()`, `use{Entity}()` — custom hooks that wrap service calls with useState/useEffect/try-catch
446
- - Only allowed custom hook: `use{Module}Preferences.ts` (preferences only, step 5)
447
- - Pattern for pages:
448
- ```tsx
449
- // CORRECT — direct service call in page component
450
- const [data, setData] = useState<EmployeeResponseDto[]>([]);
451
- const fetchData = useCallback(async () => {
452
- setLoading(true);
453
- try {
454
- const response = await employeeApi.getAll({ pageSize: 200 });
455
- setData(response.items); // ← extract .items from PaginatedResult
456
- } catch { setError(t('employees:errors.loadFailed')); }
457
- finally { setLoading(false); }
458
- }, []);
459
- useEffect(() => { fetchData(); }, [fetchData]);
460
- ```
461
- - **PaginatedResult<T>**: ALL service `getAll` methods MUST type responses as `PaginatedResult<T>` — extract `.items` in the page
462
- - FORBIDDEN: `api.get<Employee[]>(url)` — use `api.get<PaginatedResult<EmployeeResponseDto>>(url)` then `.items`
463
-
464
- **Lazy loading (MANDATORY — ALL pages, not just forms):**
465
- - ALL page imports in App.tsx MUST use `React.lazy()` + `<Suspense fallback={<PageLoader />}>`
466
- - FORBIDDEN: `lazy(() => import(...))` without a `<Suspense>` boundary in the rendering tree
467
- - Pattern for App.tsx:
468
- ```tsx
469
- import { lazy, Suspense } from 'react';
470
- const EmployeesListPage = lazy(() => import('./pages/.../EmployeesListPage'));
471
- // In routes:
472
- { path: 'employees', element: <Suspense fallback={<PageLoader />}><EmployeesListPage /></Suspense> }
473
- ```
474
-
475
- **Dependency verification (BLOCKING):**
476
- - BEFORE writing any import, verify the package exists in `package.json`
477
- - If package not present: run `npm install {package}` BEFORE writing the import
478
- - Common missing packages: @tanstack/react-query, recharts, date-fns, react-router-dom
479
- - After ALL frontend tasks: run `npm ls --depth=0` to detect any MISSING dependencies
480
-
481
- **FORBIDDEN patterns (any = FAIL):**
482
- ```
483
- import axios from 'axios' → use @/services/api/apiClient
484
- <table>...</table> → use SmartTable
485
- <div className="bg-blue-600"> → use bg-[var(--color-accent-600)]
486
- <Route path="/business/app/mod" /> → MUST be nested inside Layout
487
- Only fr/en translations → MUST have 4 languages
488
- src/pages/{Module}/ → MUST be src/pages/{Context}/{App}/{Module}/
489
- Routes generated but NOT added to App.tsx → MUST wire routes to App.tsx after scaffold_routes
490
- Routes in standard block only → MUST also add to /t/:slug/ tenant block (Pattern B only; Pattern A auto-handles)
491
- Adding routes to clientRoutes[] instead of contextRoutes.{context}[] → MUST use contextRoutes for business/platform/personal routes
492
- '00000000-0000-0000-0000-000000000000' → use dynamic ID from auth context or route params
493
- const api = axios.create(...) → use @/services/api/apiClient (single instance)
494
- useXxx with raw axios inside hooks → hooks MUST use the shared apiClient from @/services/api
495
- <input type="text" value={...employeeId} → FK Guid fields MUST use EntityLookup (searchable select)
496
- placeholder="Enter ID" / "Enter GUID" → EntityLookup provides search-based entity selection
497
- <Modal>/<Dialog>/<Drawer>/<Popup> for forms → forms are FULL PAGES with own URL routes (/create, /:id/edit)
498
- useState(showCreateModal/editDialog) → navigate to form page, NEVER toggle modal visibility
499
- <table>/<thead>/<tbody> → MUST use SmartTable or DataTable component
500
- bg-blue-600 / bg-red-500 / etc. → MUST use CSS variables: bg-[var(--color-accent-600)]
501
- text-green-700 / text-gray-500 / etc. → MUST use CSS variables: text-[var(--success-text)]
502
- window.confirm() / confirm() → MUST use ConfirmDialog component with i18n
503
- Hardcoded English text in JSX (>Create<) → MUST use t('{mod}:actions.create', 'Create')
504
- Hardcoded error strings in hooks → MUST use t('{mod}:errors.loadFailed') in all hooks
505
- /business/humanresources/ → MUST use kebab-case: /business/human-resources/
506
- useEmployees() / use{Entity}() hooks → pages call services DIRECTLY in useCallback (no hook wrapper)
507
- api.get<Employee[]>(url) → api.get<PaginatedResult<Employee>>(url) then .items
508
- lazy(() => import(...)) without Suspense → ALL lazy pages MUST have <Suspense fallback={<PageLoader />}>
509
- ```
510
-
511
- ---
512
-
513
- ## I18n (MANDATORY for frontend)
514
-
515
- > **CRITICAL:** Every frontend module MUST have 4 translation files. Missing i18n = broken UI (untranslated labels, empty buttons).
516
-
517
- **File path:** `src/i18n/locales/{lang}/{moduleLower}.json` (4 files: fr, en, it, de)
518
-
519
- **JSON key structure (identical across all 4 languages):**
520
- ```json
521
- {
522
- "actions": { "create": "...", "edit": "...", "delete": "...", "save": "...", "cancel": "...", "search": "...", "filter": "...", "export": "...", "refresh": "..." },
523
- "labels": { "title": "...", "description": "...", "status": "...", "createdAt": "...", "updatedAt": "..." },
524
- "columns": { "name": "...", "status": "...", "date": "...", "actions": "..." },
525
- "form": { "name": "...", "description": "...", "submit": "...", "required": "..." },
526
- "errors": { "loadFailed": "...", "saveFailed": "...", "deleteFailed": "...", "notFound": "..." },
527
- "validation": { "required": "...", "minLength": "...", "maxLength": "...", "invalid": "..." },
528
- "messages": { "created": "...", "updated": "...", "deleted": "...", "confirmDelete": "..." },
529
- "empty": { "title": "...", "description": "...", "action": "..." }
530
- }
531
- ```
532
-
533
- **Usage pattern in TSX:** `t('{moduleLower}:actions.create', 'Create')` — ALWAYS namespace prefix + fallback value.
534
-
535
- **Rules:**
536
- - 4 JSON files per module: fr, en, it, de — ALL mandatory (not just fr/en)
537
- - File structure: `src/i18n/locales/{lang}/{moduleLower}.json` — NEVER inline translations in index.ts
538
- - Identical key structures across all languages — same keys, translated values
539
- - All UI labels, validation messages, button text, empty states, error messages
540
- - Depends on frontend page completion (pages define which keys are needed)
541
- - Add entity-specific keys under `labels`, `columns`, `form` for each entity field
542
- - **Hooks MUST also use i18n** — error messages like "Failed to load" MUST use `t('{mod}:errors.loadFailed')`
543
- - **FORBIDDEN:** Inline translation objects in `i18n/index.ts` — use separate JSON files per language per module
544
- - Reference `smartstack-frontend.md` for the complete template
545
-
546
- ---
547
-
548
- ## Test (BLOCKING)
549
-
550
- **MCP:** `scaffold_tests`, `analyze_test_coverage`
551
-
552
- > **CRITICAL:** Test generation is a MANDATORY category. If the PRD has no test tasks,
553
- > the category completeness check (step-01 section 4b) will inject a guardrail task.
554
- > Test projects MUST be created as the FIRST action in this category — before generating any test files.
555
-
556
- **Execution sequence:**
557
-
558
- 1. **Ensure test projects exist (FIRST — before any test generation):**
559
- ```bash
560
- # Unit test project
561
- UNIT_TEST_PROJECT="tests/${PROJECT_NAME}.Tests.Unit"
562
- if [ ! -d "$UNIT_TEST_PROJECT" ]; then
563
- dotnet new xunit -n "${PROJECT_NAME}.Tests.Unit" -o "$UNIT_TEST_PROJECT"
564
- dotnet add "$UNIT_TEST_PROJECT" package Moq
565
- dotnet add "$UNIT_TEST_PROJECT" package FluentAssertions
566
- for proj in src/*/*.csproj; do dotnet add "$UNIT_TEST_PROJECT" reference "$proj"; done
567
- dotnet sln add "$UNIT_TEST_PROJECT/${PROJECT_NAME}.Tests.Unit.csproj"
568
- fi
569
-
570
- # Integration test project (SQL Server LocalDB)
571
- INT_TEST_PROJECT="tests/${PROJECT_NAME}.Tests.Integration"
572
- if [ ! -d "$INT_TEST_PROJECT" ]; then
573
- dotnet new xunit -n "${PROJECT_NAME}.Tests.Integration" -o "$INT_TEST_PROJECT"
574
- dotnet add "$INT_TEST_PROJECT" package FluentAssertions
575
- dotnet add "$INT_TEST_PROJECT" package Microsoft.AspNetCore.Mvc.Testing
576
- dotnet add "$INT_TEST_PROJECT" package Microsoft.EntityFrameworkCore.SqlServer
577
- dotnet add "$INT_TEST_PROJECT" package Microsoft.Data.SqlClient
578
- dotnet add "$INT_TEST_PROJECT" package Respawn
579
- for proj in src/*/*.csproj; do dotnet add "$INT_TEST_PROJECT" reference "$proj"; done
580
- dotnet sln add "$INT_TEST_PROJECT/${PROJECT_NAME}.Tests.Integration.csproj"
581
- fi
582
- ```
583
-
584
- 2. **Generate test infrastructure** (FIRST, before entity tests):
585
- - `scaffold_tests` with target="infrastructure" → generates DatabaseFixture, DatabaseCollection, SmartStackTestFactory, TestAuthHandler, TestTenantService, IntegrationTestBase, TestDataSeeder
586
- - These use **SQL Server LocalDB** (not SQLite) → real migrations, real LINQ→SQL
587
-
588
- 3. **Generate entity tests via MCP** (NOT manually):
589
- - Domain: `scaffold_tests` with target="entity"
590
- - Service: `scaffold_tests` with target="service"
591
- - Controller: `scaffold_tests` with target="controller", testTypes=["integration"]
592
- - Repository: `scaffold_tests` with target="repository", testTypes=["integration"]
593
- - Security: `scaffold_tests` with target="controller", testTypes=["security"]
594
-
595
- 4. **Run tests (BLOCKING):**
596
- ```bash
597
- dotnet build --no-restore
598
- dotnet test --no-build --verbosity normal
599
- ```
600
- Integration tests run against **real SQL Server LocalDB** via DatabaseFixture:
601
- - Migrations are applied automatically (validates migration chain)
602
- - Queries execute real T-SQL (validates LINQ→SQL translation)
603
- - Multi-tenant isolation is enforced via global query filters on SQL Server
604
- - Respawn resets data between tests (~50ms)
605
-
606
- 5. **Fix loop:** If tests fail → analyze → fix code (NOT tests) → rebuild → retest → loop until 100% pass
607
-
608
- 6. **Coverage check:** `analyze_test_coverage` → must be >= 80%
609
-
610
- **Completion criteria (ALL required):**
611
- - Unit test project exists
612
- - Integration test project exists with SQL Server LocalDB infrastructure
613
- - Tests generated via MCP (scaffold_tests)
614
- - `dotnet test` exits 0 (all pass — including integration tests on SQL Server)
615
- - Coverage >= 80%
616
- - No `[Fact(Skip = "...")]`
617
-
618
- **Frontend test completeness (BLOCKING):**
619
-
620
- After all frontend pages are generated, verify test coverage:
621
-
622
- ```bash
623
- # Count page files vs test files
624
- PAGE_COUNT=$(find src/pages/ -name "*.tsx" ! -name "*.test.tsx" 2>/dev/null | wc -l)
625
- TEST_COUNT=$(find src/pages/ -name "*.test.tsx" 2>/dev/null | wc -l)
626
-
627
- if [ "$PAGE_COUNT" -gt 0 ] && [ "$TEST_COUNT" -eq 0 ]; then
628
- echo "BLOCKING: Category D — No .test.tsx files found in src/pages/"
629
- echo "Every module with pages MUST have at least one test file"
630
- echo "Pages found: $PAGE_COUNT, Tests found: 0"
631
- exit 1
632
- fi
633
-
634
- # Minimum ratio: at least 1 test per 3 pages
635
- MIN_TESTS=$(( (PAGE_COUNT + 2) / 3 ))
636
- if [ "$TEST_COUNT" -lt "$MIN_TESTS" ]; then
637
- echo "WARNING: Low test coverage — $TEST_COUNT tests for $PAGE_COUNT pages (minimum: $MIN_TESTS)"
638
- fi
639
- ```
640
-
641
- ---
642
-
643
- ## Validation (FINAL — BLOCKING)
644
-
645
- **Execution sequence:**
646
-
647
- 1. `dotnet clean && dotnet restore && dotnet build` → MUST pass
648
-
649
- 1a. **DB migration validation (BLOCKING — if infrastructure tasks exist):**
650
- ```bash
651
- INFRA_PROJECT=$(ls src/*Infrastructure*/*.csproj 2>/dev/null | head -1)
652
- API_PROJECT=$(ls src/*Api*/*.csproj 2>/dev/null | head -1)
653
- if [ -n "$INFRA_PROJECT" ] && [ -n "$API_PROJECT" ]; then
654
- # Check no pending model changes
655
- dotnet ef migrations has-pending-model-changes \
656
- --project "$INFRA_PROJECT" --startup-project "$API_PROJECT"
657
- # Apply migrations on fresh SQL Server LocalDB
658
- DB_NAME="SmartStack_Validation_$(date +%s)"
659
- CONN_STRING="Server=(localdb)\\MSSQLLocalDB;Database=$DB_NAME;Integrated Security=true;TrustServerCertificate=true;Connect Timeout=120;"
660
- dotnet ef database update --connection "$CONN_STRING" \
661
- --project "$INFRA_PROJECT" --startup-project "$API_PROJECT"
662
- # Cleanup
663
- sqlcmd -S "(localdb)\MSSQLLocalDB" -Q "IF DB_ID('$DB_NAME') IS NOT NULL BEGIN ALTER DATABASE [$DB_NAME] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [$DB_NAME]; END" 2>/dev/null
664
- fi
665
- ```
666
- If FAIL → migration is broken → fix before continuing
71
+ ## Section-Level Splitting (>4 Entities)
667
72
 
668
- 1b. **Runtime assembly validation (BLOCKING):**
669
- ```bash
670
- API_PROJECT=$(ls src/*Api*/*.csproj 2>/dev/null | head -1)
671
- if [ -n "$API_PROJECT" ]; then
672
- dotnet run --project "$API_PROJECT" --urls "http://localhost:5098" > /tmp/ralph-validation-startup.log 2>&1 &
673
- VAL_PID=$!
73
+ When a module has many entities, a single `/apex -d` call saturates the context window.
74
+ Section-level splitting breaks the module into smaller, manageable phases.
674
75
 
675
- # Wait up to 10 seconds
676
- STARTED=false
677
- for i in $(seq 1 10); do
678
- if ! kill -0 $VAL_PID 2>/dev/null; then
679
- # Process crashed
680
- CRASH_LOG=$(cat /tmp/ralph-validation-startup.log 2>/dev/null)
681
- echo "VALIDATION FAILED: API startup crash"
682
- echo "$CRASH_LOG"
683
- break
684
- fi
685
- HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5098/health 2>/dev/null)
686
- if [ "$HTTP_CODE" != "000" ]; then
687
- STARTED=true
688
- break
689
- fi
690
- sleep 1
691
- done
76
+ **Activation threshold:** `> 4 domain tasks` AND `> 1 architecture section`
692
77
 
693
- kill $VAL_PID 2>/dev/null
694
- wait $VAL_PID 2>/dev/null
695
- rm -f /tmp/ralph-validation-startup.log
78
+ | Phase | Content | Depends On |
79
+ |-------|---------|------------|
80
+ | Phase 0 | ALL domain + infrastructure + migration + module seed data | — |
81
+ | Phase 1 | Section A: services, controllers, section seed, pages, i18n, tests | Phase 0 |
82
+ | Phase 2 | Section B: idem | Phase 0 (+ Phase 1 if FK cross-section) |
83
+ | Phase N | Section N: idem | Phase 0 (+ earlier phases if FK) |
84
+ | Final | Cross-validation (dotnet build, typecheck, MCP validate) | All phases |
696
85
 
697
- if [ "$STARTED" != "true" ]; then
698
- # Classify error per references/error-classification.md
699
- # Provide actionable fix command
700
- # DO NOT proceed to tests fix the startup issue first
701
- echo "VALIDATION FAILED: Runtime assembly error detected"
702
- fi
703
- fi
704
- ```
86
+ **Key rules:**
87
+ - Phase 0 creates ALL entity classes + ALL EF configs + ONE migration (avoids ModelSnapshot conflicts)
88
+ - Section phases NEVER create entities or migrations — only services, controllers, pages on top
89
+ - FK cross-section dependencies determine section execution order (topological sort)
90
+ - Orphan entities (not in any section) are included in Phase 0 with their services/controllers
91
+ - Each phase produces a temporary PRD file: `.ralph/prd-{module}-phase0.json`, `.ralph/prd-{module}-section-{code}.json`
92
+ - Temporary PRD files are cleaned up in step-05 (report)
705
93
 
706
- 2. `dotnet test` (full suite) MUST pass
707
- 3. `mcp__smartstack__validate_conventions` → 0 errors
708
- 4. Generate validation report in progress.txt
94
+ **Backward compatible:** Modules with `<= 4 domain tasks` or `1 section`standard execution, zero impact.
709
95
 
710
- **Completion criteria:**
711
- - Build exit code 0
712
- - DB migrations apply on SQL Server LocalDB (no pending model changes)
713
- - Runtime startup check passes (no assembly errors)
714
- - Test exit code 0 (unit tests + integration tests on real SQL Server)
715
- - MCP validation 0 errors
716
- - Production ready = true
96
+ See `references/section-splitting.md` for full detection, mapping, and execution logic.