@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.
- package/.documentation/agents.html +1 -371
- package/.documentation/cli-commands.html +1 -1
- package/.documentation/commands.html +1 -1
- package/.documentation/efcore.html +1 -1
- package/.documentation/gitflow.html +1 -1
- package/.documentation/hooks.html +27 -66
- package/.documentation/index.html +166 -166
- package/.documentation/init.html +6 -7
- package/.documentation/installation.html +1 -1
- package/.documentation/ralph-loop.html +1 -9
- package/.documentation/test-web.html +15 -39
- package/dist/index.js +23 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/agents/gitflow/merge.md +56 -6
- package/templates/agents/gitflow/pr.md +70 -9
- package/templates/project/appsettings.json.template +8 -2
- package/templates/skills/business-analyse/html/ba-interactive.html +1 -27
- package/templates/skills/business-analyse/steps/step-03-specify.md +57 -0
- package/templates/skills/business-analyse/steps/step-05-handoff.md +211 -13
- package/templates/skills/gitflow/steps/step-pr.md +17 -5
- package/templates/skills/ralph-loop/SKILL.md +22 -15
- package/templates/skills/ralph-loop/steps/step-01-task.md +89 -4
- package/templates/skills/ralph-loop/steps/step-02-execute.md +408 -23
- package/templates/skills/ralph-loop/steps/step-03-commit.md +82 -0
- package/templates/skills/ralph-loop/steps/step-04-check.md +235 -6
- package/templates/skills/ralph-loop/steps/step-05-report.md +115 -0
|
@@ -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
|
|
116
|
-
5. Execute
|
|
117
|
-
6. Commit changes, update progress
|
|
118
|
-
7. Check completion
|
|
119
|
-
8.
|
|
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
|
|
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
|
|
283
|
-
- **VERIFY MCP FIRST** - Never skip MCP validation
|
|
284
|
-
- **
|
|
285
|
-
- **COMMIT AFTER EACH
|
|
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
|
|
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
|
|
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
|
|
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. **
|
|
20
|
-
2. **
|
|
21
|
-
3. **
|
|
22
|
-
4. **
|
|
23
|
-
5. **
|
|
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
|
|
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
|
-
|
|
467
|
+
**Execution sequence for frontend tasks (follow IN ORDER):**
|
|
114
468
|
|
|
115
|
-
1. **
|
|
116
|
-
-
|
|
117
|
-
- `
|
|
118
|
-
- `
|
|
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. **
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
-
|
|
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. **
|
|
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
|
-
|
|
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
|
|