@atlashub/smartstack-cli 3.0.0 → 3.1.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.
@@ -109,22 +109,27 @@ The loop only stops when this exact tag is output or max iterations reached.
109
109
 
110
110
  <workflow>
111
111
  **Standard flow (single module):**
112
- 1. Parse flags and task description
113
- 2. Verify MCP servers are available (MANDATORY)
114
- 3. Initialize .ralph/ structure and state files
115
- 4. Load current task from prd.json (or create initial task breakdown with categories, dependencies, and acceptance criteria)
116
- 5. Execute ONE task per iteration
117
- 6. Commit changes, update progress
118
- 7. Check completion criteria
119
- 8. If complete: generate final report
112
+ 1. Parse flags and task description (step-00)
113
+ 2. Verify MCP servers are available (step-00, MANDATORY)
114
+ 3. Initialize .ralph/ structure and state files (step-00)
115
+ 4. Load current task from prd.json create task breakdown if new (step-01, READ ONCE)
116
+ 5. Execute first task (step-02, READ ONCE)
117
+ 6. Commit changes, update progress (step-03, READ ONCE)
118
+ 7. Check completion → enter COMPACT LOOP (step-04)
119
+ 8. **COMPACT LOOP** (step-04 section 5, NO re-reading step files):
120
+ - Find eligible tasks → batch by category (max 5)
121
+ - Execute batch inline
122
+ - Commit batch
123
+ - Re-check completion → loop or finish
124
+ 9. When complete: generate final report (step-05)
120
125
 
121
126
  **Multi-module flow (from BA handoff):**
122
127
  1-3. Same as standard flow
123
128
  4. Detect `prd-*.json` files → create `modules-queue.json`
124
129
  5. Copy current module's prd to `prd.json`
125
- 6. Execute all tasks for current module (standard loop: steps 01→02→03→04)
126
- 7. When module complete: advance to next module in queue
127
- 8. Repeat steps 5-7 for each module
130
+ 6. Execute all tasks for current module via COMPACT LOOP
131
+ 7. When module complete: advance to next module in queue (step-04 section 3)
132
+ 8. Repeat steps 5-7 for each module (step-01 is re-read ONLY for module transitions)
128
133
  9. When all modules done: generate cross-module report
129
134
  </workflow>
130
135
 
@@ -279,12 +284,14 @@ Before ANY work, verify MCP servers:
279
284
 
280
285
  <execution_rules>
281
286
 
282
- - **Load one step at a time** - Only load the current step file
283
- - **VERIFY MCP FIRST** - Never skip MCP validation
284
- - **ONE TASK PER ITERATION** - Don't batch tasks
285
- - **COMMIT AFTER EACH TASK** - Atomic progress
287
+ - **Load step files ONCE** - Step files are read only on FIRST iteration. After that, use the COMPACT LOOP in step-04 section 5.
288
+ - **VERIFY MCP FIRST** - Never skip MCP validation at init
289
+ - **BATCH same-category tasks** - Execute up to 5 tasks of the same category per iteration (reduces loop iterations from 30+ to ~8)
290
+ - **COMMIT AFTER EACH BATCH** - One commit per batch, not per individual task
286
291
  - **UPDATE progress.txt** - Persist learnings
287
292
  - **NEVER fake completion** - Only output promise when truly done
293
+ - **NEVER STOP THE LOOP** - The loop is AUTONOMOUS. DO NOT pause between iterations. DO NOT wait for user input. Only stop on: completion, max iterations, dead-end, or explicit user interruption. Stopping for any other reason wastes context and causes re-reads.
294
+ - **COMPACT OUTPUT** - During loop iterations, use 1-2 lines per task. Save verbose output for the final report.
288
295
  </execution_rules>
289
296
 
290
297
  <success_criteria>
@@ -6,6 +6,12 @@ next_step: steps/step-02-execute.md
6
6
 
7
7
  # Step 1: Load Task
8
8
 
9
+ > **CONTEXT OPTIMIZATION:** This file is only read ONCE (first iteration).
10
+ > After the first full iteration (step-01 → step-02 → step-03 → step-04),
11
+ > ALL subsequent iterations MUST use the COMPACT LOOP in step-04-check.md section 5.
12
+ > **DO NOT re-read this file for iterations > 1.**
13
+ > If you are re-reading this file on iteration > 1, STOP and go to step-04 section 5 instead.
14
+
9
15
  ## YOUR TASK:
10
16
 
11
17
  Load the current task from prd.json or create initial task breakdown with categories, dependencies, and acceptance criteria.
@@ -76,23 +82,37 @@ function transformPrdJsonToRalphV2(prdJson, moduleCode) {
76
82
  const filesToCreate = prdJson.implementation?.filesToCreate || {};
77
83
 
78
84
  // 1. Generate tasks from implementation.filesToCreate (primary source)
85
+ // CRITICAL: Frontend and i18n tasks are CONSOLIDATED into ONE module-level task each.
86
+ // These use MCP scaffolding tools, NOT manual file-by-file generation.
79
87
  const lastIdByCategory = {};
88
+ const frontendFiles = []; // Collect frontend files for consolidated task
89
+ const i18nFiles = []; // Collect i18n files for consolidated task
80
90
 
81
91
  for (const layer of layerOrder) {
82
92
  const files = filesToCreate[layer.key] || [];
93
+
94
+ // FRONTEND & I18N: Collect files, do NOT create individual tasks
95
+ if (layer.category === "frontend") {
96
+ frontendFiles.push(...files);
97
+ continue; // Skip individual task creation
98
+ }
99
+
83
100
  for (const fileSpec of files) {
101
+ // Check if this is an i18n file (category "infrastructure" but path contains i18n)
102
+ if (fileSpec.path?.includes('i18n/') || fileSpec.path?.includes('/locales/')) {
103
+ i18nFiles.push(fileSpec);
104
+ continue; // Skip individual task creation
105
+ }
106
+
84
107
  const depIds = [];
85
108
  // Depend on last task in same category (sequential within layer)
86
109
  if (lastIdByCategory[layer.category]) {
87
110
  depIds.push(lastIdByCategory[layer.category]);
88
111
  }
89
- // Cross-layer: api depends on last application task, frontend depends on last api task
112
+ // Cross-layer: api depends on last application task
90
113
  if (layer.category === "api" && lastIdByCategory["application"]) {
91
114
  depIds.push(lastIdByCategory["application"]);
92
115
  }
93
- if (layer.category === "frontend" && lastIdByCategory["api"]) {
94
- depIds.push(lastIdByCategory["api"]);
95
- }
96
116
  if (layer.category === "test" && lastIdByCategory["api"]) {
97
117
  depIds.push(lastIdByCategory["api"]);
98
118
  }
@@ -131,6 +151,71 @@ function transformPrdJsonToRalphV2(prdJson, moduleCode) {
131
151
  }
132
152
  }
133
153
 
154
+ // 1b. Create CONSOLIDATED frontend task (ONE task per module, uses MCP tools)
155
+ if (frontendFiles.length > 0) {
156
+ const apiDepId = lastIdByCategory["api"] || lastIdByCategory["application"];
157
+ const allLinkedUCs = [...new Set(frontendFiles.flatMap(f => f.linkedUCs || []))];
158
+ const allLinkedWireframes = [...new Set(frontendFiles.flatMap(f => f.linkedWireframes || []))];
159
+ const allPaths = frontendFiles.map(f => f.path);
160
+
161
+ tasks.push({
162
+ id: taskId,
163
+ description: `[frontend] Generate COMPLETE frontend for module ${moduleCode} via MCP scaffold tools (${frontendFiles.length} files: pages, components, hooks, API client)`,
164
+ status: "pending",
165
+ category: "frontend",
166
+ dependencies: apiDepId ? [apiDepId] : [],
167
+ acceptance_criteria: [
168
+ "MCP scaffold_api_client called → API client + types generated",
169
+ "MCP scaffold_routes called → routes.tsx updated with nested routes inside Layout wrapper",
170
+ "Pages match wireframes: " + allLinkedWireframes.join(", "),
171
+ "SmartTable for lists (NOT HTML tables), EntityCard for grids (NOT custom divs)",
172
+ "CSS variables ONLY (NO hardcoded Tailwind colors like bg-blue-600)",
173
+ "useNavigate + useParams for routing, NOT window.location",
174
+ "Loading/error/empty states on all pages",
175
+ "4-language i18n keys (fr, en, it, de)",
176
+ "npm run typecheck passes"
177
+ ].join("; "),
178
+ started_at: null,
179
+ completed_at: null,
180
+ iteration: null,
181
+ commit_hash: null,
182
+ files_changed: { created: allPaths, modified: [] },
183
+ validation: null,
184
+ error: null,
185
+ module: moduleCode,
186
+ _frontendMeta: {
187
+ wireframes: allLinkedWireframes,
188
+ linkedUCs: allLinkedUCs,
189
+ filesToCreate: frontendFiles
190
+ }
191
+ });
192
+
193
+ lastIdByCategory["frontend"] = taskId;
194
+ taskId++;
195
+ }
196
+
197
+ // 1c. Create CONSOLIDATED i18n task (ONE task per module)
198
+ if (i18nFiles.length > 0) {
199
+ tasks.push({
200
+ id: taskId,
201
+ description: `[i18n] Generate i18n translations for module ${moduleCode} (4 languages: fr, en, it, de)`,
202
+ status: "pending",
203
+ category: "i18n",
204
+ dependencies: lastIdByCategory["frontend"] ? [lastIdByCategory["frontend"]] : [],
205
+ acceptance_criteria: "4 JSON files (fr, en, it, de) with identical keys; all UI labels translated",
206
+ started_at: null,
207
+ completed_at: null,
208
+ iteration: null,
209
+ commit_hash: null,
210
+ files_changed: { created: i18nFiles.map(f => f.path), modified: [] },
211
+ validation: null,
212
+ error: null,
213
+ module: moduleCode
214
+ });
215
+ lastIdByCategory["i18n"] = taskId;
216
+ taskId++;
217
+ }
218
+
134
219
  // 2. Add IClientSeedDataProvider task for client projects (ExtensionsDbContext)
135
220
  // This is MANDATORY - without it, core seed data (navigation, permissions, roles) is dead code
136
221
  const hasClientSeedData = filesToCreate["seedData"]?.some(f =>
@@ -1,14 +1,19 @@
1
1
  ---
2
2
  name: step-02-execute
3
- description: Execute ONE task with dependency checks and rich tracking
3
+ description: Execute task(s) with dependency checks and rich tracking
4
4
  next_step: steps/step-03-commit.md
5
5
  ---
6
6
 
7
7
  # Step 2: Execute Task
8
8
 
9
+ > **CONTEXT OPTIMIZATION:** This file is only read ONCE (first iteration).
10
+ > After the first full iteration, ALL subsequent iterations use the COMPACT LOOP
11
+ > in step-04-check.md section 5 which includes inline execution.
12
+ > **DO NOT re-read this file for iterations > 1.**
13
+
9
14
  ## YOUR TASK:
10
15
 
11
- Execute exactly ONE task from prd.json. Do NOT batch multiple tasks.
16
+ Execute the current task(s) from prd.json. For the first iteration, execute ONE task. After the first iteration, the compact loop in step-04 handles batch execution.
12
17
 
13
18
  **ULTRA THINK about the implementation.**
14
19
 
@@ -16,12 +21,11 @@ Execute exactly ONE task from prd.json. Do NOT batch multiple tasks.
16
21
 
17
22
  ## EXECUTION RULES:
18
23
 
19
- 1. **ONE TASK ONLY** - Execute only `{current_task_description}`
20
- 2. **ATOMIC CHANGES** - Changes should be complete and working
21
- 3. **USE MCP** - Validate with SmartStack MCP as needed
22
- 4. **DOCUMENT** - Track what you're doing
23
- 5. **CHECK DEPENDENCIES** - Verify all dependencies are met before starting
24
- 6. **TRACK FILES** - Record every file created or modified
24
+ 1. **ATOMIC CHANGES** - Changes should be complete and working
25
+ 2. **USE MCP** - Validate with SmartStack MCP (once per batch, not per task)
26
+ 3. **TRACK FILES** - Record every file created or modified
27
+ 4. **CHECK DEPENDENCIES** - Verify all dependencies are met before starting
28
+ 5. **BATCH ALLOWED** - Multiple tasks of the same category can be batched (max 5)
25
29
 
26
30
  ---
27
31
 
@@ -81,6 +85,352 @@ writeJSON('.ralph/prd.json', prd);
81
85
  | `test` | `scaffold_tests`, `analyze_test_coverage` | xUnit, test naming conventions |
82
86
  | `validation` | `validate_conventions` | Build, test, lint checks |
83
87
 
88
+ **Test task guidance — TDD CYCLE (MANDATORY):**
89
+
90
+ > **CRITICAL:** Tests are NOT optional. Every feature MUST have tests that pass.
91
+ > The cycle is: Generate Code → Generate Tests → Run Tests → Fix Code → Re-run Tests → Pass.
92
+
93
+ When executing `test` category tasks, follow this EXACT sequence:
94
+
95
+ **1. ENSURE TEST PROJECT EXISTS (BLOCKING)**
96
+
97
+ ```bash
98
+ # Detect project name from solution
99
+ PROJECT_NAME=$(basename $(pwd))
100
+ TEST_PROJECT="tests/${PROJECT_NAME}.Tests.Unit"
101
+
102
+ if [ ! -d "$TEST_PROJECT" ]; then
103
+ echo "⚠️ Test project missing. Scaffolding now..."
104
+
105
+ # Create test project
106
+ dotnet new xunit -n "${PROJECT_NAME}.Tests.Unit" -o "$TEST_PROJECT"
107
+
108
+ # Add required packages
109
+ cd "$TEST_PROJECT"
110
+ dotnet add package Moq --version 4.20.72
111
+ dotnet add package FluentAssertions --version 8.3.0
112
+ dotnet add package Microsoft.Extensions.DependencyInjection --version 10.0.2
113
+ cd ../..
114
+
115
+ # Add project references to all src/ projects
116
+ for proj in src/*/*.csproj; do
117
+ dotnet add "$TEST_PROJECT" reference "$proj"
118
+ done
119
+
120
+ # Add test project to solution
121
+ dotnet sln add "$TEST_PROJECT/${PROJECT_NAME}.Tests.Unit.csproj"
122
+
123
+ echo "✅ Test project scaffolded at $TEST_PROJECT"
124
+ fi
125
+ ```
126
+
127
+ **2. GENERATE TESTS USING MCP (DO NOT WRITE MANUALLY)**
128
+
129
+ ```javascript
130
+ // Identify what to test based on task description
131
+ const task = prd.tasks.find(t => t.id === {current_task_id});
132
+ const testType = detectTestType(task.description); // "unit" | "integration" | "security"
133
+
134
+ // For domain tests
135
+ if (task.description.includes("domain entity") || task.description.includes("value object")) {
136
+ // Call MCP to scaffold domain tests
137
+ mcp__smartstack__scaffold_tests({
138
+ module: extractModuleName(task.description),
139
+ test_type: "unit",
140
+ target_layer: "domain",
141
+ target_file: identifyEntityFile(task.description)
142
+ });
143
+ }
144
+
145
+ // For service tests
146
+ if (task.description.includes("service")) {
147
+ mcp__smartstack__scaffold_tests({
148
+ module: extractModuleName(task.description),
149
+ test_type: "unit",
150
+ target_layer: "application",
151
+ target_file: identifyServiceFile(task.description)
152
+ });
153
+ }
154
+
155
+ // For controller tests
156
+ if (task.description.includes("controller") || task.description.includes("API")) {
157
+ mcp__smartstack__scaffold_tests({
158
+ module: extractModuleName(task.description),
159
+ test_type: "integration",
160
+ target_layer: "api",
161
+ target_file: identifyControllerFile(task.description)
162
+ });
163
+ }
164
+
165
+ // For security/permissions tests
166
+ if (task.description.includes("permission") || task.description.includes("security")) {
167
+ mcp__smartstack__scaffold_tests({
168
+ module: extractModuleName(task.description),
169
+ test_type: "security",
170
+ target_layer: "api",
171
+ target_file: identifyControllerFile(task.description)
172
+ });
173
+ }
174
+ ```
175
+
176
+ **3. RUN TESTS IMMEDIATELY (BLOCKING)**
177
+
178
+ ```bash
179
+ # Build test project first
180
+ dotnet build "$TEST_PROJECT" --no-restore
181
+
182
+ # Run tests with detailed output
183
+ dotnet test "$TEST_PROJECT" --no-build --verbosity normal
184
+
185
+ # Capture exit code
186
+ TEST_EXIT_CODE=$?
187
+ ```
188
+
189
+ **4. ANALYZE RESULTS AND IMPROVE (LOOP UNTIL GREEN)**
190
+
191
+ ```javascript
192
+ if (TEST_EXIT_CODE !== 0) {
193
+ echo "❌ Tests failed. Analyzing failures...";
194
+
195
+ // Parse test output to identify failures
196
+ const failures = parseTestFailures(testOutput);
197
+
198
+ // CRITICAL: DO NOT PROCEED. Fix the code.
199
+ for (const failure of failures) {
200
+ echo ` - ${failure.testName}: ${failure.reason}`;
201
+
202
+ // Identify what needs fixing
203
+ if (failure.reason.includes("NullReferenceException")) {
204
+ // Fix null handling in implementation
205
+ } else if (failure.reason.includes("Expected") && failure.reason.includes("Actual")) {
206
+ // Fix logic to match expected behavior
207
+ } else if (failure.reason.includes("NotImplementedException")) {
208
+ // Implement missing method
209
+ }
210
+ }
211
+
212
+ // After fixes, RE-RUN TESTS (step 3)
213
+ // LOOP until TEST_EXIT_CODE === 0
214
+
215
+ // If cannot fix after 3 attempts:
216
+ task.status = 'failed';
217
+ task.error = `Tests failed after 3 fix attempts: ${failures.map(f => f.testName).join(', ')}`;
218
+ // STOP - do not proceed to commit
219
+ }
220
+
221
+ if (TEST_EXIT_CODE === 0) {
222
+ echo "✅ All tests passed!";
223
+ {validation_result} = "passed";
224
+ }
225
+ ```
226
+
227
+ **5. VERIFY TEST COVERAGE (MANDATORY ≥80%)**
228
+
229
+ ```javascript
230
+ // Use MCP to analyze coverage
231
+ const coverage = mcp__smartstack__analyze_test_coverage({
232
+ project_path: process.cwd(),
233
+ module: extractModuleName(task.description)
234
+ });
235
+
236
+ if (coverage.percentage < 80) {
237
+ echo `⚠️ Test coverage ${coverage.percentage}% < 80% minimum`;
238
+ echo `Missing coverage for: ${coverage.uncovered_files.join(', ')}`;
239
+
240
+ // Generate additional tests for uncovered code
241
+ for (const file of coverage.uncovered_files) {
242
+ mcp__smartstack__scaffold_tests({
243
+ module: extractModuleName(task.description),
244
+ test_type: "unit",
245
+ target_file: file
246
+ });
247
+ }
248
+
249
+ // Re-run tests (go back to step 3)
250
+ }
251
+
252
+ echo `✅ Test coverage: ${coverage.percentage}% (${coverage.lines_covered}/${coverage.lines_total} lines)`;
253
+ ```
254
+
255
+ **6. TEST TASK COMPLETION CRITERIA**
256
+
257
+ A test task is ONLY complete when:
258
+ - ✅ Test project exists
259
+ - ✅ Tests generated via MCP (NOT manually written)
260
+ - ✅ `dotnet test` exits with code 0 (all tests pass)
261
+ - ✅ Test coverage ≥ 80%
262
+ - ✅ No `[Fact(Skip = "...")]` present (no skipped tests)
263
+
264
+ **If ANY criterion fails, task.status = 'failed' and DO NOT commit.**
265
+
266
+ **Validation task guidance — FINAL VERIFICATION (BLOCKING):**
267
+
268
+ > **CRITICAL:** The validation task is the LAST task in each module. It verifies the module is production-ready.
269
+
270
+ When executing `validation` category tasks, follow this EXACT sequence:
271
+
272
+ **1. CLEAN BUILD (MANDATORY)**
273
+
274
+ ```bash
275
+ echo "🔨 Running clean build..."
276
+
277
+ # Clean previous builds
278
+ dotnet clean --verbosity quiet
279
+
280
+ # Restore dependencies
281
+ dotnet restore --verbosity quiet
282
+
283
+ # Build entire solution
284
+ dotnet build --no-restore --verbosity normal
285
+
286
+ BUILD_EXIT_CODE=$?
287
+
288
+ if [ $BUILD_EXIT_CODE -ne 0 ]; then
289
+ echo "╔════════════════════════════════════════════════════════════╗"
290
+ echo "║ ❌ BUILD FAILED ║"
291
+ echo "╠════════════════════════════════════════════════════════════╣"
292
+ echo "║ The solution does not compile. ║"
293
+ echo "║ This indicates a critical error in the code. ║"
294
+ echo "╠════════════════════════════════════════════════════════════╣"
295
+ echo "║ CORRECTIVE ACTION: ║"
296
+ echo "║ 1. Review build errors above ║"
297
+ echo "║ 2. Fix compilation errors ║"
298
+ echo "║ 3. Re-run: dotnet build ║"
299
+ echo "║ 4. Once build succeeds, Ralph will continue ║"
300
+ echo "╚════════════════════════════════════════════════════════════╝"
301
+
302
+ task.status = 'failed';
303
+ task.error = 'Build failed. See build output for details.';
304
+ exit 1;
305
+ fi
306
+
307
+ echo "✅ Build succeeded";
308
+ ```
309
+
310
+ **2. RUN FULL TEST SUITE (MANDATORY)**
311
+
312
+ ```bash
313
+ echo "🧪 Running full test suite..."
314
+
315
+ PROJECT_NAME=$(basename $(pwd))
316
+ TEST_PROJECT="tests/${PROJECT_NAME}.Tests.Unit"
317
+
318
+ if [ ! -d "$TEST_PROJECT" ]; then
319
+ echo "⚠️ WARNING: No test project found at $TEST_PROJECT";
320
+ echo "This module has NO tests. This is NOT recommended.";
321
+ task.validation = "warning: no tests";
322
+ else
323
+ # Run ALL tests with detailed output
324
+ dotnet test "$TEST_PROJECT" --no-build --verbosity normal --logger "console;verbosity=detailed"
325
+
326
+ TEST_EXIT_CODE=$?
327
+
328
+ if [ $TEST_EXIT_CODE -ne 0 ]; then
329
+ echo "╔════════════════════════════════════════════════════════════╗"
330
+ echo "║ ❌ TESTS FAILED ║"
331
+ echo "╠════════════════════════════════════════════════════════════╣"
332
+ echo "║ One or more tests are failing. ║"
333
+ echo "║ The module is NOT production-ready. ║"
334
+ echo "╠════════════════════════════════════════════════════════════╣"
335
+ echo "║ CORRECTIVE ACTION: ║"
336
+ echo "║ 1. Review test failures above ║"
337
+ echo "║ 2. Fix the failing code or tests ║"
338
+ echo "║ 3. Re-run: dotnet test ║"
339
+ echo "║ 4. Once all tests pass, Ralph will continue ║"
340
+ echo "╚════════════════════════════════════════════════════════════╝"
341
+
342
+ task.status = 'failed';
343
+ task.error = 'Test suite failed. Module not production-ready.';
344
+ exit 1;
345
+ fi
346
+
347
+ echo "✅ All tests passed";
348
+ fi
349
+ ```
350
+
351
+ **3. MCP VALIDATION (MANDATORY)**
352
+
353
+ ```javascript
354
+ echo "🔍 Running MCP validation...";
355
+
356
+ // Validate SmartStack conventions
357
+ const validation = mcp__smartstack__validate_conventions({
358
+ checks: ["all"]
359
+ });
360
+
361
+ if (validation.status === "error" || validation.errors.length > 0) {
362
+ echo "❌ MCP validation failed:";
363
+ for (const error of validation.errors) {
364
+ echo ` - ${error.file}: ${error.message}`;
365
+ }
366
+
367
+ task.status = 'failed';
368
+ task.error = `MCP validation failed: ${validation.errors.length} error(s)`;
369
+ exit 1;
370
+ }
371
+
372
+ if (validation.warnings.length > 0) {
373
+ echo "⚠️ MCP validation warnings:";
374
+ for (const warning of validation.warnings) {
375
+ echo ` - ${warning.file}: ${warning.message}`;
376
+ }
377
+ task.validation = `passed with ${validation.warnings.length} warning(s)`;
378
+ } else {
379
+ task.validation = "passed";
380
+ }
381
+
382
+ echo "✅ MCP validation passed";
383
+ ```
384
+
385
+ **4. GENERATE VALIDATION REPORT**
386
+
387
+ ```javascript
388
+ const report = {
389
+ module: {current_module},
390
+ build: { status: "success", duration: buildDuration },
391
+ tests: {
392
+ status: TEST_EXIT_CODE === 0 ? "success" : "failed",
393
+ total: testStats.total,
394
+ passed: testStats.passed,
395
+ failed: testStats.failed,
396
+ skipped: testStats.skipped,
397
+ duration: testStats.duration,
398
+ coverage: coverage.percentage
399
+ },
400
+ mcp: {
401
+ status: validation.status,
402
+ errors: validation.errors.length,
403
+ warnings: validation.warnings.length
404
+ },
405
+ production_ready: (BUILD_EXIT_CODE === 0 && TEST_EXIT_CODE === 0 && validation.errors.length === 0)
406
+ };
407
+
408
+ // Append to progress.txt
409
+ const progressEntry = `
410
+ ╔═══════════════════════════════════════════════════════════════╗
411
+ ║ MODULE VALIDATION REPORT - ${current_module}
412
+ ╠═══════════════════════════════════════════════════════════════╣
413
+ ║ Build: ${report.build.status.toUpperCase()}
414
+ ║ Tests: ${report.tests.passed}/${report.tests.total} passed (${report.tests.coverage}% coverage)
415
+ ║ MCP: ${report.mcp.status.toUpperCase()} (${report.mcp.errors} errors, ${report.mcp.warnings} warnings)
416
+ ║ Production Ready: ${report.production_ready ? "YES ✅" : "NO ❌"}
417
+ ╚═══════════════════════════════════════════════════════════════╝
418
+ `;
419
+ appendToFile('.ralph/progress.txt', progressEntry);
420
+
421
+ echo progressEntry;
422
+ ```
423
+
424
+ **5. VALIDATION TASK COMPLETION CRITERIA**
425
+
426
+ A validation task is ONLY complete when:
427
+ - ✅ `dotnet build` exits with code 0
428
+ - ✅ `dotnet test` exits with code 0 (or no test project + warning logged)
429
+ - ✅ MCP `validate_conventions` has 0 errors
430
+ - ✅ Production ready = true
431
+
432
+ **If ANY criterion fails, task.status = 'failed', module is NOT production-ready, and Ralph STOPS.**
433
+
84
434
  **Infrastructure task guidance (MANDATORY directory conventions):**
85
435
 
86
436
  When executing `infrastructure` category tasks, follow these rules strictly:
@@ -108,26 +458,61 @@ When executing `infrastructure` category tasks, follow these rules strictly:
108
458
  - Use `SqlObjectHelper.cs` for embedded resource loading
109
459
  - Use `CREATE OR ALTER` in SQL files for idempotency
110
460
 
111
- **Frontend task guidance (MANDATORY Layout wrapper):**
461
+ **Frontend task guidance — MCP-FIRST PROTOCOL (MANDATORY):**
462
+
463
+ > **CRITICAL:** Frontend code is generated via MCP tools, NOT written from scratch.
464
+ > Writing plain HTML tables, custom div cards, or hardcoded Tailwind is FORBIDDEN.
465
+ > The MCP tools produce SmartStack-compliant code automatically.
112
466
 
113
- When executing `frontend` category tasks, follow these rules strictly:
467
+ **Execution sequence for frontend tasks (follow IN ORDER):**
114
468
 
115
- 1. **Routes MUST be inside Layout wrapper** (CRITICAL - violating this breaks the entire UI shell)
116
- - All routes MUST be nested inside the appropriate context Layout component
117
- - `platform.*` `<Route path="/platform" element={<AdminLayout />}>` children
118
- - `business.*` → `<Route path="/business" element={<BusinessLayout />}>` children
119
- - `personal.*` → `<Route path="/personal/myspace" element={<UserLayout />}>` children
120
- - **If routes are placed OUTSIDE the layout wrapper: header, sidebar, and AvatarMenu will NOT render**
121
- - NEVER create flat routes at the top level of `<Routes>`
469
+ 1. **Call `mcp__smartstack__scaffold_api_client`** FIRST
470
+ - Generates: `{entity}Api.ts` with type-safe CRUD methods + React Query hook
471
+ - Uses `apiClient` (NOT axios), NavRoute integration, permission checking
472
+ - Output: `src/services/api/{module}Api.ts`
122
473
 
123
- 2. **Nested routes, NOT flat routes**
124
- - Use `<Route path="application"><Route path="module" element={...} /></Route>` pattern
125
- - NEVER use `<Route path="/context/application/module" element={...} />` (flat)
126
- - Use `<Route index element={<Navigate to="default" replace />} />` for default redirects
474
+ 2. **Call `mcp__smartstack__scaffold_routes`** (source: "controllers", scope: "all")
475
+ - Updates: `navRoutes.generated.ts` + route config in App.tsx
476
+ - Generates: Nested routes INSIDE correct Layout wrapper
477
+ - Context mapping: `business.*` `BusinessLayout`, `platform.*` `AdminLayout`
127
478
 
128
- 3. **Tenant-prefixed routes** - If `/t/:slug/` block exists, add routes there too
479
+ 3. **Create pages using SmartStack components** (reference: `templates-frontend.md`)
480
+ - **List pages**: Use `SmartTable` (NOT HTML `<table>`), `SmartFilter`, `EntityCard` for grid view
481
+ - **Detail pages**: Use `EntityDetailCard`, `StatusBadge`, tab layout
482
+ - **Create/Edit pages**: Use `SmartForm` with `FluentValidation`-backed fields, tab layout
483
+ - **Dashboard pages**: Use `StatCard`, Recharts components
484
+ - Match wireframes from feature.json `specification.uiWireframes[]`
485
+
486
+ 4. **Create preferences hook**: `use{Module}Preferences.ts`
487
+ - pageSize, sortColumn, sortDirection, visibleColumns, viewMode, filters
488
+
489
+ 5. **Generate i18n** (4 languages: fr, en, it, de)
490
+ - All UI labels, validation messages, button text, empty states
491
+
492
+ 6. **Post-generation validation (BLOCKING)**:
493
+ - `npm run typecheck` MUST pass
494
+ - NO hardcoded colors (scan for `bg-blue-`, `text-gray-`, etc.)
495
+ - CSS variables ONLY: `bg-[var(--bg-card)]`, `text-[var(--text-primary)]`
496
+ - Routes are NESTED and INSIDE Layout wrapper
497
+ - All pages have loading/error/empty states
498
+ - API client uses `@/services/api/apiClient` (NOT `import axios`)
499
+ - `EntityCard` for grid views (NOT custom `<div>` cards)
500
+
501
+ **FORBIDDEN patterns (any of these = FAIL the task):**
502
+ ```
503
+ import axios from 'axios' → use @/services/api/apiClient
504
+ <table>...</table> → use SmartTable component
505
+ <div className="bg-blue-600"> → use bg-[var(--color-accent-600)]
506
+ <Route path="/business/app/mod" /> → MUST be nested inside BusinessLayout
507
+ Only fr/en translations → MUST have 4 languages (fr, en, it, de)
508
+ ```
129
509
 
130
- 4. **See `/application` skill templates-frontend.md** for full template patterns
510
+ **Layout wrapper mapping:**
511
+ | Context | Layout | Route path |
512
+ |---------|--------|------------|
513
+ | `platform.*` | `AdminLayout` | `/platform` |
514
+ | `business.*` | `BusinessLayout` | `/business` |
515
+ | `personal.*` | `UserLayout` | `/personal/myspace` |
131
516
 
132
517
  **API/Controller task guidance (MANDATORY folder hierarchy):**
133
518