@atlashub/smartstack-cli 3.16.0 → 3.18.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/dist/index.js +74 -42
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +752 -53
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/agents/gitflow/finish.md +21 -3
- package/templates/agents/gitflow/start.md +14 -4
- package/templates/skills/application/templates-backend.md +12 -1
- package/templates/skills/business-analyse/SKILL.md +4 -4
- package/templates/skills/business-analyse/html/ba-interactive.html +11 -5
- package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +11 -5
- package/templates/skills/business-analyse/references/deploy-data-build.md +25 -9
- package/templates/skills/business-analyse/references/validation-checklist.md +29 -2
- package/templates/skills/business-analyse/steps/step-00-init.md +23 -5
- package/templates/skills/business-analyse/steps/step-03a2-analysis.md +21 -3
- package/templates/skills/business-analyse/steps/step-03b-ui.md +31 -1
- package/templates/skills/business-analyse/steps/step-03d-validate.md +41 -4
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +9 -7
- package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +222 -40
- package/templates/skills/ralph-loop/SKILL.md +41 -1
- package/templates/skills/ralph-loop/references/category-rules.md +106 -1
- package/templates/skills/ralph-loop/references/compact-loop.md +85 -24
- package/templates/skills/ralph-loop/references/core-seed-data.md +48 -0
- package/templates/skills/ralph-loop/steps/step-00-init.md +30 -54
- package/templates/skills/ralph-loop/steps/step-01-task.md +102 -1
- package/templates/skills/ralph-loop/steps/step-04-check.md +87 -40
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
# Compact Loop — Inline Execution
|
|
2
2
|
|
|
3
3
|
> **Loaded by:** step-04 section 5 (after first full iteration)
|
|
4
|
-
> **Purpose:** Execute
|
|
5
|
-
> **
|
|
4
|
+
> **Purpose:** Execute ALL remaining tasks autonomously without stopping.
|
|
5
|
+
> **EXECUTION GUARANTEE:** This loop runs until ALL tasks are done, max iterations, or dead-end.
|
|
6
|
+
> **ABSOLUTE RULES:**
|
|
7
|
+
> - NEVER stop the loop
|
|
8
|
+
> - NEVER wait for user input
|
|
9
|
+
> - NEVER ask for confirmation
|
|
10
|
+
> - NEVER re-read step files
|
|
11
|
+
> - NEVER pause between iterations
|
|
12
|
+
> - Execute A → B → C → D → back to step-04 section 1 → repeat
|
|
6
13
|
|
|
7
14
|
---
|
|
8
15
|
|
|
@@ -208,10 +215,49 @@ Batch: {batch.length} [{firstCategory}] → {batch.map(t => `[${t.id}] ${t.descr
|
|
|
208
215
|
|
|
209
216
|
## C. Commit Batch
|
|
210
217
|
|
|
218
|
+
### C1. Update PRD Status (MANDATORY — BEFORE git commit)
|
|
219
|
+
|
|
220
|
+
> **CRITICAL:** PRD status MUST be updated BEFORE committing.
|
|
221
|
+
> In test-v4-005, the compact loop executed 44 tasks but NEVER updated prd.json status.
|
|
222
|
+
> This broke ALL downstream guardrails (module completeness check never triggered).
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
const now = new Date().toISOString();
|
|
226
|
+
const COMMIT_HASH_PENDING = 'pending-commit'; // Updated after git commit
|
|
227
|
+
|
|
228
|
+
for (const task of batch) {
|
|
229
|
+
if (task.status !== 'failed') task.status = 'completed';
|
|
230
|
+
task.completed_at = now;
|
|
231
|
+
task.iteration = prd.config.current_iteration;
|
|
232
|
+
task.commit_hash = COMMIT_HASH_PENDING;
|
|
233
|
+
}
|
|
234
|
+
prd.config.current_iteration++;
|
|
235
|
+
prd.updated_at = now;
|
|
236
|
+
writeJSON('.ralph/prd.json', prd);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### C2. Update Progress File (MANDATORY)
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
const completed = prd.tasks.filter(t => t.status === 'completed').length;
|
|
243
|
+
const total = prd.tasks.length;
|
|
244
|
+
const moduleName = prd.project?.module || 'unknown';
|
|
245
|
+
|
|
246
|
+
// Append to progress.txt (NOT overwrite)
|
|
247
|
+
appendFile('.ralph/progress.txt',
|
|
248
|
+
`Iteration ${prd.config.current_iteration - 1}: ${batch.map(t => t.id).join('/')} COMPLETED — ` +
|
|
249
|
+
`${batch[0].category} (${batch.length} tasks). ${completed}/${total} done.\n` +
|
|
250
|
+
` Files: ${batch.reduce((acc, t) => acc + (t.files_changed?.created?.length || 0), 0)} created\n`
|
|
251
|
+
);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### C3. Git Commit
|
|
255
|
+
|
|
211
256
|
```bash
|
|
212
257
|
# Stage all changed files from batch
|
|
213
258
|
git add {all_files_from_batch}
|
|
214
|
-
git add .ralph/prd.json
|
|
259
|
+
git add .ralph/prd.json .ralph/progress.txt
|
|
260
|
+
[ -f .ralph/modules-queue.json ] && git add .ralph/modules-queue.json
|
|
215
261
|
|
|
216
262
|
git commit -m "$(cat <<'EOF'
|
|
217
263
|
feat({scope}): [{category}] {batch.length} tasks — {short summary}
|
|
@@ -227,44 +273,59 @@ EOF
|
|
|
227
273
|
COMMIT_HASH=$(git rev-parse --short HEAD)
|
|
228
274
|
```
|
|
229
275
|
|
|
230
|
-
|
|
276
|
+
### C4. Finalize PRD with Commit Hash
|
|
277
|
+
|
|
231
278
|
```javascript
|
|
232
|
-
|
|
279
|
+
// Update commit hash now that we have the real one
|
|
233
280
|
for (const task of batch) {
|
|
234
|
-
if (task.
|
|
235
|
-
task.completed_at = now;
|
|
236
|
-
task.iteration = prd.config.current_iteration;
|
|
237
|
-
task.commit_hash = COMMIT_HASH;
|
|
281
|
+
if (task.commit_hash === 'pending-commit') task.commit_hash = COMMIT_HASH;
|
|
238
282
|
}
|
|
239
283
|
prd.history.push({
|
|
240
|
-
iteration: prd.config.current_iteration,
|
|
284
|
+
iteration: prd.config.current_iteration - 1,
|
|
241
285
|
task_ids: batch.map(t => t.id),
|
|
242
286
|
action: 'batch-completed',
|
|
243
287
|
timestamp: now,
|
|
244
288
|
commit_hash: COMMIT_HASH,
|
|
245
289
|
notes: "{summary}"
|
|
246
290
|
});
|
|
247
|
-
prd.config.current_iteration++;
|
|
248
|
-
prd.updated_at = now;
|
|
249
291
|
writeJSON('.ralph/prd.json', prd);
|
|
250
292
|
```
|
|
251
293
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
294
|
+
### C5. PRD Sync Verification (HARD CHECK)
|
|
295
|
+
|
|
296
|
+
> **MANDATORY:** Verify prd.json reflects reality before looping back.
|
|
297
|
+
|
|
298
|
+
```javascript
|
|
299
|
+
const prdCheck = readJSON('.ralph/prd.json');
|
|
300
|
+
const batchIds = batch.map(t => t.id);
|
|
301
|
+
const actuallyCompleted = prdCheck.tasks.filter(t => batchIds.includes(t.id) && t.status === 'completed');
|
|
302
|
+
|
|
303
|
+
if (actuallyCompleted.length !== batch.filter(t => t.status !== 'failed').length) {
|
|
304
|
+
console.error('PRD SYNC ERROR: Tasks executed but not marked completed in prd.json');
|
|
305
|
+
console.error(`Expected: ${batch.filter(t => t.status !== 'failed').length} completed, Got: ${actuallyCompleted.length}`);
|
|
306
|
+
// Force re-write
|
|
307
|
+
for (const task of batch) {
|
|
308
|
+
const prdTask = prdCheck.tasks.find(t => t.id === task.id);
|
|
309
|
+
if (prdTask && task.status === 'completed') prdTask.status = 'completed';
|
|
310
|
+
if (prdTask) prdTask.commit_hash = COMMIT_HASH;
|
|
311
|
+
}
|
|
312
|
+
writeJSON('.ralph/prd.json', prdCheck);
|
|
313
|
+
console.log('PRD SYNC REPAIRED');
|
|
314
|
+
}
|
|
257
315
|
```
|
|
258
316
|
|
|
259
317
|
---
|
|
260
318
|
|
|
261
|
-
## D. Loop Back
|
|
319
|
+
## D. Loop Back (MANDATORY — NO PAUSE)
|
|
262
320
|
|
|
263
321
|
**IMMEDIATELY return to step-04 section 1 (Read Current State).**
|
|
264
|
-
DO NOT stop. DO NOT wait for user. DO NOT re-read step files.
|
|
265
322
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
-
|
|
269
|
-
|
|
270
|
-
-
|
|
323
|
+
DO NOT stop. DO NOT wait for user. DO NOT ask "should I continue?". DO NOT re-read step files.
|
|
324
|
+
DO NOT display a summary and pause. DO NOT output partial results and wait.
|
|
325
|
+
**Go directly to step-04 section 1. NOW.**
|
|
326
|
+
|
|
327
|
+
The ONLY exit conditions (enforced by step-04 sections 2-4):
|
|
328
|
+
- ALL tasks complete (all modules if multi-module) → step-05
|
|
329
|
+
- Max iterations reached → step-05
|
|
330
|
+
- Dead-end (all remaining blocked/failed) → step-05
|
|
331
|
+
- User Ctrl+C (external interrupt)
|
|
@@ -385,6 +385,52 @@ public class NavigationResourceSeedEntry
|
|
|
385
385
|
|
|
386
386
|
## 3. PermissionsSeedData.cs — MCP-First
|
|
387
387
|
|
|
388
|
+
### CRITICAL: PermissionAction Safety Rules
|
|
389
|
+
|
|
390
|
+
> **NEVER use `Enum.Parse<PermissionAction>("...")` — this causes runtime crashes if the string is invalid.**
|
|
391
|
+
> The error only manifests at application startup, not at compile time.
|
|
392
|
+
|
|
393
|
+
**Valid PermissionAction values (from SmartStack.Domain.Authorization):**
|
|
394
|
+
|
|
395
|
+
| Enum Value | Int | Description |
|
|
396
|
+
|------------|-----|-------------|
|
|
397
|
+
| `PermissionAction.Access` | 0 | Wildcard permissions only (IsWildcard = true) |
|
|
398
|
+
| `PermissionAction.Read` | 1 | GET/HEAD — View data |
|
|
399
|
+
| `PermissionAction.Create` | 2 | POST — Create new records |
|
|
400
|
+
| `PermissionAction.Update` | 3 | PUT/PATCH — Modify existing records |
|
|
401
|
+
| `PermissionAction.Delete` | 4 | DELETE — Remove records |
|
|
402
|
+
| `PermissionAction.Export` | 5 | Export data (CSV, Excel, etc.) |
|
|
403
|
+
| `PermissionAction.Import` | 6 | Import data |
|
|
404
|
+
| `PermissionAction.Approve` | 7 | Approve workflow items |
|
|
405
|
+
| `PermissionAction.Reject` | 8 | Reject workflow items |
|
|
406
|
+
| `PermissionAction.Assign` | 9 | Assign items to users |
|
|
407
|
+
| `PermissionAction.Execute` | 10 | Execute actions (sync, run, etc.) |
|
|
408
|
+
|
|
409
|
+
**Anti-patterns (FORBIDDEN):**
|
|
410
|
+
|
|
411
|
+
```csharp
|
|
412
|
+
// FORBIDDEN — Runtime crash if string is not a valid enum value
|
|
413
|
+
Enum.Parse<PermissionAction>("Validate"); // ArgumentException at startup
|
|
414
|
+
(PermissionAction)Enum.Parse(typeof(PermissionAction), "Validate"); // Same crash
|
|
415
|
+
|
|
416
|
+
// FORBIDDEN — String-based action in anonymous objects
|
|
417
|
+
new { Action = "read" }; // Not type-safe, silent mismatch possible
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Correct patterns (MANDATORY):**
|
|
421
|
+
|
|
422
|
+
```csharp
|
|
423
|
+
// ALWAYS use the typed enum directly — compile-time safe
|
|
424
|
+
Action = PermissionAction.Read // Compile-time checked
|
|
425
|
+
Action = PermissionAction.Create // Compile-time checked
|
|
426
|
+
Action = PermissionAction.Approve // Compile-time checked
|
|
427
|
+
|
|
428
|
+
// For custom actions beyond standard CRUD, pick from the enum:
|
|
429
|
+
// Export, Import, Approve, Reject, Assign, Execute
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
**MCP validation:** `validate_conventions` with `checks: ["permissions"]` will detect and flag these anti-patterns.
|
|
433
|
+
|
|
388
434
|
### Step A: Call MCP (PRIMARY)
|
|
389
435
|
|
|
390
436
|
```
|
|
@@ -932,6 +978,8 @@ Before marking the task as completed, verify ALL:
|
|
|
932
978
|
- [ ] NavigationResources seeded (if `seedDataCore.navigationResources` present in feature.json)
|
|
933
979
|
- [ ] Section/Resource translations created (4 languages each, EntityType = Section/Resource)
|
|
934
980
|
- [ ] `dotnet build` passes after generation
|
|
981
|
+
- [ ] NO `Enum.Parse<PermissionAction>` usage anywhere in seeding code (use typed enum directly)
|
|
982
|
+
- [ ] ALL PermissionAction values are from the valid enum: Access, Read, Create, Update, Delete, Export, Import, Approve, Reject, Assign, Execute
|
|
935
983
|
|
|
936
984
|
**If ANY check fails, the task status = 'failed'.**
|
|
937
985
|
|
|
@@ -8,9 +8,12 @@ next_step: steps/step-01-task.md
|
|
|
8
8
|
|
|
9
9
|
## MANDATORY RULES:
|
|
10
10
|
|
|
11
|
+
- **FULLY AUTONOMOUS** — execute ALL tasks without stopping or asking the user
|
|
11
12
|
- NEVER skip MCP verification
|
|
12
13
|
- ALWAYS parse ALL flags before any action
|
|
13
14
|
- ONLY check resume if -r flag is set
|
|
15
|
+
- **NEVER ask the user** to choose a mode, confirm execution, or select options
|
|
16
|
+
- **NEVER set max_iterations = 1** — unless the user explicitly passed `-m 1`
|
|
14
17
|
- CONTEXT BUDGET: Keep output COMPACT
|
|
15
18
|
- **NEVER DELEGATE** the main Ralph loop to a sub-agent (single module)
|
|
16
19
|
- **Multi-module (2+ modules):** Use Agent Teams for parallel execution
|
|
@@ -48,69 +51,36 @@ mcp__context7__resolve-library-id: libraryName: "test" → {mcp_context7} = tru
|
|
|
48
51
|
|
|
49
52
|
If ANY fails: show error, suggest `smartstack check-mcp`, STOP.
|
|
50
53
|
|
|
51
|
-
## 3.
|
|
54
|
+
## 3. Execution Mode (FULLY AUTONOMOUS)
|
|
52
55
|
|
|
53
|
-
> **
|
|
56
|
+
> **CRITICAL:** Ralph-loop is FULLY AUTONOMOUS by default.
|
|
57
|
+
> It executes ALL tasks from start to finish WITHOUT stopping.
|
|
58
|
+
> The user launched `/ralph-loop` — this IS the instruction to execute everything.
|
|
54
59
|
|
|
55
|
-
|
|
60
|
+
**DO NOT** ask the user to confirm, choose a mode, or approve execution.
|
|
61
|
+
**DO NOT** set `max_iterations = 1` or any reduced value.
|
|
62
|
+
**DO NOT** stop after the first task to "check quality".
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
**Stratégie recommandée :**
|
|
63
|
-
|
|
64
|
-
1. **Première itération supervisée** - Lancer manuellement la première tâche pour vérifier :
|
|
65
|
-
- La qualité du code généré
|
|
66
|
-
- Le temps d'exécution des tests
|
|
67
|
-
- La clarté des messages d'erreur
|
|
68
|
-
|
|
69
|
-
2. **Mode autonome limité** - Si première itération OK, relancer avec `--max-iterations 5-10`
|
|
70
|
-
|
|
71
|
-
3. **Mode autonome complet** - Une fois confiant, augmenter à `--max-iterations 50`
|
|
72
|
-
|
|
73
|
-
### Checkpoint Prompt (Optional)
|
|
74
|
-
|
|
75
|
-
Si vous détectez que c'est la première utilisation de ralph-loop sur ce projet (pas de `.ralph/logs/`), proposer :
|
|
64
|
+
The loop runs autonomously until:
|
|
65
|
+
- ALL tasks are completed (success)
|
|
66
|
+
- `max_iterations` is reached (user explicitly set via `-m N`)
|
|
67
|
+
- Dead-end detected (all remaining tasks blocked/failed)
|
|
68
|
+
- User Ctrl+C (external interrupt)
|
|
76
69
|
|
|
77
70
|
```javascript
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
header: "Mode",
|
|
83
|
-
multiSelect: false,
|
|
84
|
-
options: [
|
|
85
|
-
{ label: "Supervisé (recommandé)", description: "Exécuter 1 tâche, puis demander confirmation" },
|
|
86
|
-
{ label: "Autonome limité", description: "Max 10 itérations automatiques" },
|
|
87
|
-
{ label: "Autonome complet", description: "Jusqu'à 50 itérations (mode AFK)" }
|
|
88
|
-
]
|
|
89
|
-
}]
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
if (answer === "Supervisé") {
|
|
93
|
-
max_iterations = 1;
|
|
94
|
-
console.log("Mode supervisé : 1 tâche sera exécutée. Relancez avec -r pour continuer.");
|
|
95
|
-
} else if (answer === "Autonome limité") {
|
|
96
|
-
max_iterations = Math.min(max_iterations, 10);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
71
|
+
// NO user prompt. NO mode selection. JUST EXECUTE.
|
|
72
|
+
// max_iterations is already set from flags (default: 50).
|
|
73
|
+
// If user wants fewer iterations, they use -m N explicitly.
|
|
74
|
+
console.log(`Mode: AUTONOMOUS | Max iterations: ${max_iterations}`);
|
|
99
75
|
```
|
|
100
76
|
|
|
101
|
-
###
|
|
102
|
-
|
|
103
|
-
⚠️ **IMPORTANT :** Si vos tests prennent >30 secondes, ralph-loop peut devenir inefficace.
|
|
77
|
+
### Speed Warning (informational only — does NOT block)
|
|
104
78
|
|
|
79
|
+
If tests are slow (>30s detected in previous logs), display a one-line warning but **continue execution**:
|
|
105
80
|
```
|
|
106
|
-
|
|
107
|
-
- Unitaires : <5s
|
|
108
|
-
- Intégration : <15s
|
|
109
|
-
- E2E : <30s (à exécuter en dehors du loop)
|
|
81
|
+
⚠ Slow tests detected. Consider disabling E2E during ralph-loop.
|
|
110
82
|
```
|
|
111
83
|
|
|
112
|
-
Si tests lents détectés (via logs), avertir l'utilisateur et suggérer de désactiver tests E2E pendant le loop.
|
|
113
|
-
|
|
114
84
|
## 4. Resume Mode
|
|
115
85
|
|
|
116
86
|
If `{resume_mode} = true`:
|
|
@@ -213,9 +183,15 @@ if (PRD_COUNT > 1) {
|
|
|
213
183
|
}
|
|
214
184
|
```
|
|
215
185
|
|
|
216
|
-
## 5. Completion Promise
|
|
186
|
+
## 5. Completion Promise (auto-set)
|
|
217
187
|
|
|
218
|
-
|
|
188
|
+
```javascript
|
|
189
|
+
// Auto-set if not provided — NEVER ask the user
|
|
190
|
+
if (!completion_promise) {
|
|
191
|
+
completion_promise = "COMPLETE";
|
|
192
|
+
}
|
|
193
|
+
// The promise is output ONLY when ALL tasks are done. No user interaction needed.
|
|
194
|
+
```
|
|
219
195
|
|
|
220
196
|
## 6. Collect Metadata
|
|
221
197
|
|
|
@@ -94,11 +94,62 @@ If `.ralph/prd.json` exists:
|
|
|
94
94
|
2. **v3 FAST PATH:** If `$version === "3.0.0"` AND `tasks[]` exists:
|
|
95
95
|
- Inject runtime fields if missing (status, config, feature, created, updated_at, history)
|
|
96
96
|
- Write back to `.ralph/prd.json`
|
|
97
|
+
- **Run CATEGORY COMPLETENESS CHECK (section 4b) before proceeding**
|
|
97
98
|
- Skip directly to section 5 (find next task)
|
|
98
99
|
3. **v2 legacy:** If `$version === "2.0.0"` → find next eligible task (section 5), skip to section 5
|
|
99
100
|
4. **FORMAT A (deprecated):** If `.project && .requirements && !.$version` → run `transformPrdJsonToRalphV2()` → section 5
|
|
100
101
|
|
|
101
|
-
If `.ralph/prd.json` does not exist: continue to section
|
|
102
|
+
If `.ralph/prd.json` does not exist: continue to section 1b.
|
|
103
|
+
|
|
104
|
+
### 1b. BA Handoff Quality Gate (BLOCKING)
|
|
105
|
+
|
|
106
|
+
> **CRITICAL:** Before generating ANY tasks, verify the BA handoff is complete.
|
|
107
|
+
> A missing handoff means no frontend files, no test plan, no BR-to-code mapping.
|
|
108
|
+
> Generating tasks without handoff produces an INCOMPLETE PRD (backend-only).
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
// Find the source feature.json for the current module
|
|
112
|
+
const sourceFeatureJson = prd?.metadata?.sourceFeatureJson
|
|
113
|
+
|| findFile('docs/business/**/business-analyse/**/feature.json');
|
|
114
|
+
|
|
115
|
+
if (sourceFeatureJson) {
|
|
116
|
+
const feature = readJSON(sourceFeatureJson);
|
|
117
|
+
const handoff = feature.handoff || {};
|
|
118
|
+
|
|
119
|
+
// CHECK 1: Handoff status must be "handed-off"
|
|
120
|
+
if (handoff.status !== 'handed-off') {
|
|
121
|
+
console.error(`
|
|
122
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
123
|
+
║ BLOCKING: BA HANDOFF INCOMPLETE ║
|
|
124
|
+
║ ║
|
|
125
|
+
║ feature.json handoff.status = "${handoff.status || 'missing'}"
|
|
126
|
+
║ Expected: "handed-off" ║
|
|
127
|
+
║ ║
|
|
128
|
+
║ The /business-analyse did not complete steps 04a→05c. ║
|
|
129
|
+
║ Without handoff, the PRD will be MISSING: ║
|
|
130
|
+
║ - Frontend tasks (pages, components, routes) ║
|
|
131
|
+
║ - Test tasks (unit, integration, security) ║
|
|
132
|
+
║ - BR-to-code mappings ║
|
|
133
|
+
║ - API endpoint summary ║
|
|
134
|
+
║ ║
|
|
135
|
+
║ ACTION: Run /business-analyse to complete the handoff, ║
|
|
136
|
+
║ then re-run /ralph-loop. ║
|
|
137
|
+
╚══════════════════════════════════════════════════════════════╝`);
|
|
138
|
+
STOP;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// CHECK 2: filesToCreate must have all 7 categories
|
|
142
|
+
const filesToCreate = handoff.filesToCreate || {};
|
|
143
|
+
const requiredCategories = ['domain', 'application', 'infrastructure', 'api', 'frontend', 'seedData', 'tests'];
|
|
144
|
+
const missingInHandoff = requiredCategories.filter(c => !filesToCreate[c] || filesToCreate[c].length === 0);
|
|
145
|
+
|
|
146
|
+
if (missingInHandoff.length > 0) {
|
|
147
|
+
console.warn(`⚠ Handoff has empty categories: ${missingInHandoff.join(', ')}`);
|
|
148
|
+
console.warn('PRD generation may produce incomplete tasks. Consider re-running /business-analyse step-05a.');
|
|
149
|
+
// WARNING only — allow proceeding if handoff.status is correct
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
102
153
|
|
|
103
154
|
## 2. Analyze Task Description
|
|
104
155
|
|
|
@@ -128,6 +179,56 @@ Initialize `.ralph/progress.txt`:
|
|
|
128
179
|
- Dependency references are valid (exist, no forward/circular)
|
|
129
180
|
- Task count 3-30
|
|
130
181
|
|
|
182
|
+
### 4b. Category Completeness Check (BLOCKING)
|
|
183
|
+
|
|
184
|
+
> **CRITICAL:** The PRD MUST contain tasks for ALL required categories.
|
|
185
|
+
> A PRD with only backend categories (domain, infrastructure, application, api) is INCOMPLETE.
|
|
186
|
+
> This check prevents the "no frontend generated" failure mode.
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
const REQUIRED_CATEGORIES = ['domain', 'infrastructure', 'application', 'api', 'frontend', 'test'];
|
|
190
|
+
const presentCategories = new Set(prd.tasks.map(t => t.category));
|
|
191
|
+
const missingCategories = REQUIRED_CATEGORIES.filter(c => !presentCategories.has(c));
|
|
192
|
+
|
|
193
|
+
if (missingCategories.length > 0) {
|
|
194
|
+
console.warn(`⚠ PRD MISSING CATEGORIES: ${missingCategories.join(', ')}`);
|
|
195
|
+
|
|
196
|
+
// AUTO-INJECT guardrail tasks for missing categories
|
|
197
|
+
let maxIdNum = Math.max(...prd.tasks.map(t => {
|
|
198
|
+
const num = parseInt(t.id.replace(/[^0-9]/g, ''), 10);
|
|
199
|
+
return isNaN(num) ? 0 : num;
|
|
200
|
+
}));
|
|
201
|
+
const prefix = prd.tasks[0]?.id?.replace(/[0-9]+$/, '') || 'GUARD-';
|
|
202
|
+
const lastBackendTask = prd.tasks.filter(t => t.category === 'api').pop()?.id || prd.tasks[prd.tasks.length - 1]?.id;
|
|
203
|
+
|
|
204
|
+
for (const cat of missingCategories) {
|
|
205
|
+
maxIdNum++;
|
|
206
|
+
const taskId = `${prefix}${String(maxIdNum).padStart(3, '0')}`;
|
|
207
|
+
const guardrailTask = {
|
|
208
|
+
id: taskId,
|
|
209
|
+
description: `[GUARDRAIL] Generate ${cat} layer for ${prd.project?.module || 'module'}`,
|
|
210
|
+
status: 'pending',
|
|
211
|
+
category: cat,
|
|
212
|
+
dependencies: cat === 'test' ? [lastBackendTask] : (cat === 'frontend' ? [lastBackendTask] : []),
|
|
213
|
+
acceptance_criteria: [
|
|
214
|
+
cat === 'frontend' ? 'React pages created via MCP scaffold_api_client + scaffold_routes, wired to App.tsx' :
|
|
215
|
+
cat === 'test' ? 'Unit + Integration test projects created, scaffold_tests MCP called, dotnet test passes' :
|
|
216
|
+
`${cat} layer fully implemented per category-rules.md`
|
|
217
|
+
],
|
|
218
|
+
started_at: null, completed_at: null, iteration: null,
|
|
219
|
+
commit_hash: null, files_changed: [], validation: null, error: null
|
|
220
|
+
};
|
|
221
|
+
prd.tasks.push(guardrailTask);
|
|
222
|
+
console.log(` → Injected guardrail: [${taskId}] ${cat}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
writeJSON('.ralph/prd.json', prd);
|
|
226
|
+
console.log(`PRD updated: ${prd.tasks.length} tasks (${missingCategories.length} guardrails added)`);
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Why this matters:** In test-v4-005, the PRD was generated with only backend categories (domain, infrastructure, application, api, seedData, validation). The frontend and test categories were entirely absent, resulting in 0 frontend pages and 0 tests generated across 3 modules.
|
|
231
|
+
|
|
131
232
|
## 5. Find Current Task
|
|
132
233
|
|
|
133
234
|
```javascript
|
|
@@ -71,6 +71,68 @@ if [ -f "web/package.json" ] || [ -f "package.json" ]; then
|
|
|
71
71
|
fi
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
### 1.7. Category Completeness Check (RUNS EVERY ITERATION)
|
|
75
|
+
|
|
76
|
+
> **CRITICAL:** This check runs REGARDLESS of allDone status.
|
|
77
|
+
> In test-v4-005, allDone was never true (PRD status not updated), so the
|
|
78
|
+
> module completeness check in section 3b never triggered, and missing
|
|
79
|
+
> frontend/test categories were never detected.
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
const presentCategories = new Set(prd.tasks.map(t => t.category));
|
|
83
|
+
const REQUIRED_CATEGORIES = ['domain', 'infrastructure', 'application', 'api', 'frontend', 'test'];
|
|
84
|
+
const missingFromPrd = REQUIRED_CATEGORIES.filter(c => !presentCategories.has(c));
|
|
85
|
+
|
|
86
|
+
if (missingFromPrd.length > 0) {
|
|
87
|
+
console.warn(`⚠ PRD MISSING CATEGORIES: ${missingFromPrd.join(', ')}`);
|
|
88
|
+
|
|
89
|
+
// Inject guardrail tasks for missing categories
|
|
90
|
+
let maxIdNum = Math.max(...prd.tasks.map(t => parseInt(t.id.replace(/[^0-9]/g, ''), 10) || 0));
|
|
91
|
+
const prefix = prd.tasks[0]?.id?.replace(/[0-9]+$/, '') || 'GUARD-';
|
|
92
|
+
const lastApiTask = prd.tasks.filter(t => t.category === 'api').pop()?.id;
|
|
93
|
+
|
|
94
|
+
for (const cat of missingFromPrd) {
|
|
95
|
+
maxIdNum++;
|
|
96
|
+
const taskId = `${prefix}${String(maxIdNum).padStart(3, '0')}`;
|
|
97
|
+
prd.tasks.push({
|
|
98
|
+
id: taskId,
|
|
99
|
+
description: `[GUARDRAIL] Generate missing ${cat} layer for ${prd.project?.module || 'module'}`,
|
|
100
|
+
status: 'pending', category: cat,
|
|
101
|
+
dependencies: lastApiTask ? [lastApiTask] : [],
|
|
102
|
+
acceptance_criteria: [
|
|
103
|
+
cat === 'frontend' ? 'React pages + routes wired to App.tsx (standard + tenant blocks)' :
|
|
104
|
+
cat === 'test' ? 'Unit + integration test projects with passing dotnet test' :
|
|
105
|
+
`${cat} layer complete`
|
|
106
|
+
],
|
|
107
|
+
started_at: null, completed_at: null, iteration: null,
|
|
108
|
+
commit_hash: null, files_changed: [], validation: null, error: null
|
|
109
|
+
});
|
|
110
|
+
console.log(` → Injected [${taskId}] ${cat} guardrail`);
|
|
111
|
+
}
|
|
112
|
+
writeJSON('.ralph/prd.json', prd);
|
|
113
|
+
|
|
114
|
+
// Re-count after injection
|
|
115
|
+
const newPending = prd.tasks.filter(t => t.status === 'pending').length;
|
|
116
|
+
console.log(`PRD updated: +${missingFromPrd.length} guardrails, ${newPending} pending tasks`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Also check: artifact existence for categories with completed tasks
|
|
120
|
+
const completedCats = new Set(prd.tasks.filter(t => t.status === 'completed').map(t => t.category));
|
|
121
|
+
const artifactChecks = {
|
|
122
|
+
'frontend': () => glob.sync('**/src/pages/**/*.tsx').length > 0,
|
|
123
|
+
'test': () => glob.sync('tests/**/*Tests.cs').length > 0,
|
|
124
|
+
'api': () => glob.sync('src/*/Controllers/**/*Controller.cs').length > 0
|
|
125
|
+
};
|
|
126
|
+
for (const [cat, check] of Object.entries(artifactChecks)) {
|
|
127
|
+
if (completedCats.has(cat) && !check()) {
|
|
128
|
+
console.warn(`ARTIFACT MISSING: ${cat} tasks marked completed but no files found — resetting`);
|
|
129
|
+
prd.tasks.filter(t => t.category === cat && t.status === 'completed')
|
|
130
|
+
.forEach(t => { t.status = 'pending'; t.error = 'Artifacts missing — re-execute'; t.completed_at = null; });
|
|
131
|
+
writeJSON('.ralph/prd.json', prd);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
74
136
|
## 2. Check Iteration Limit
|
|
75
137
|
|
|
76
138
|
If `current_iteration > max_iterations`:
|
|
@@ -82,6 +144,13 @@ Set `prd.status = 'partial'` → step-05.
|
|
|
82
144
|
|
|
83
145
|
## 3. Check All Tasks Complete
|
|
84
146
|
|
|
147
|
+
**Re-read state after section 1.7 (categories may have been injected):**
|
|
148
|
+
```javascript
|
|
149
|
+
const tasksCompletedNow = prd.tasks.filter(t => t.status === 'completed').length;
|
|
150
|
+
const tasksTotalNow = prd.tasks.length;
|
|
151
|
+
const allDone = (tasksCompletedNow + prd.tasks.filter(t => t.status === 'skipped').length) === tasksTotalNow;
|
|
152
|
+
```
|
|
153
|
+
|
|
85
154
|
If `allDone`:
|
|
86
155
|
|
|
87
156
|
### 3a. Team Mode (multi-module with Agent Teams)
|
|
@@ -107,42 +176,15 @@ if (fileExists(queuePath)) {
|
|
|
107
176
|
const queue = readJSON(queuePath);
|
|
108
177
|
const currentModule = queue.modules[queue.currentIndex];
|
|
109
178
|
|
|
110
|
-
// MODULE COMPLETENESS CHECK
|
|
179
|
+
// MODULE COMPLETENESS CHECK (lightweight — heavy check already done in section 1.7)
|
|
180
|
+
// Verify all expected layers have completed tasks
|
|
111
181
|
const completedCats = new Set(prd.tasks.filter(t => t.status === 'completed').map(t => t.category));
|
|
112
182
|
const expected = ['domain', 'infrastructure', 'application', 'api', 'frontend', 'test'];
|
|
113
183
|
const missing = expected.filter(c => !completedCats.has(c));
|
|
114
184
|
|
|
115
|
-
// ARTIFACT EXISTENCE CHECK: categories with completed tasks must have real files
|
|
116
|
-
const artifactChecks = {
|
|
117
|
-
'infrastructure': () => fs.existsSync('src/*/Persistence/Migrations') && fs.readdirSync('src/*/Persistence/Migrations').length > 0,
|
|
118
|
-
'test': () => fs.existsSync(`tests/${projectName}.Tests.Unit`) && glob.sync('tests/**/*Tests.cs').length > 0,
|
|
119
|
-
'api': () => glob.sync('src/*/Controllers/**/*Controller.cs').length > 0,
|
|
120
|
-
'frontend': () => glob.sync('**/src/pages/**/*.tsx').length > 0
|
|
121
|
-
};
|
|
122
|
-
for (const [cat, check] of Object.entries(artifactChecks)) {
|
|
123
|
-
if (completedCats.has(cat) && !check()) {
|
|
124
|
-
// Tasks marked completed but artifacts missing — reset to pending
|
|
125
|
-
prd.tasks.filter(t => t.category === cat && t.status === 'completed')
|
|
126
|
-
.forEach(t => { t.status = 'pending'; t.error = 'Artifacts missing — re-execute'; t.completed_at = null; });
|
|
127
|
-
missing.push(cat);
|
|
128
|
-
console.log(`ARTIFACT RESET: ${cat} tasks reset to pending (no files found)`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
185
|
if (missing.length > 0) {
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
for (const cat of missing) {
|
|
136
|
-
maxId++;
|
|
137
|
-
prd.tasks.push({
|
|
138
|
-
id: maxId, description: `[${cat}] GUARDRAIL: Generate missing ${cat} for ${currentModule.code}`,
|
|
139
|
-
status: 'pending', category: cat, dependencies: [],
|
|
140
|
-
acceptance_criteria: `${cat} layer fully implemented`, started_at: null, completed_at: null,
|
|
141
|
-
iteration: null, commit_hash: null, files_changed: { created: [], modified: [] },
|
|
142
|
-
validation: null, error: null, module: currentModule.code
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
writeJSON('.ralph/prd.json', prd);
|
|
186
|
+
// Section 1.7 should have injected guardrails already — fall through to compact loop
|
|
187
|
+
console.log(`Module ${currentModule.code}: missing categories ${missing.join(', ')} — continuing loop`);
|
|
146
188
|
// Fall through to section 5 (compact loop)
|
|
147
189
|
} else {
|
|
148
190
|
// Module complete — advance
|
|
@@ -198,28 +240,33 @@ DEAD-END: {completed} done, {failed} failed, {blocked} blocked
|
|
|
198
240
|
```
|
|
199
241
|
Set `prd.status = 'failed'` → step-05.
|
|
200
242
|
|
|
201
|
-
## 5. Compact Loop (
|
|
243
|
+
## 5. Compact Loop (MANDATORY — EXECUTE IMMEDIATELY)
|
|
202
244
|
|
|
203
|
-
> **
|
|
204
|
-
> DO NOT stop. DO NOT wait for user. DO NOT re-read step files.
|
|
205
|
-
> **Read `references/compact-loop.md` and execute inline.**
|
|
245
|
+
> **THIS IS NOT OPTIONAL.** If there are pending tasks, you MUST enter the compact loop NOW.
|
|
246
|
+
> DO NOT stop. DO NOT wait for user. DO NOT ask for confirmation. DO NOT re-read step files.
|
|
247
|
+
> **Read `references/compact-loop.md` and execute ALL sections inline.**
|
|
248
|
+
> **Stopping here is a VIOLATION of the execution guarantee.**
|
|
206
249
|
|
|
207
250
|
If `hasPending && iteration < max`:
|
|
208
251
|
|
|
209
252
|
1. Read `references/compact-loop.md`
|
|
210
|
-
2. Execute sections A → B → C → D inline
|
|
211
|
-
3.
|
|
253
|
+
2. Execute sections A → B → C → D inline (find tasks → execute batch → commit → loop back)
|
|
254
|
+
3. **IMMEDIATELY** return to section 1 of THIS step (do NOT pause)
|
|
255
|
+
4. Repeat until ALL tasks are complete, max iterations reached, or dead-end
|
|
212
256
|
|
|
213
257
|
---
|
|
214
258
|
|
|
215
|
-
## CRITICAL RULES:
|
|
259
|
+
## CRITICAL RULES — EXECUTION GUARANTEE:
|
|
216
260
|
|
|
217
261
|
- **NEVER** output completion promise unless ALL tasks done (all modules if multi)
|
|
218
262
|
- **NEVER** delegate the loop to sub-agents (except team mode for multi-module)
|
|
219
|
-
- **NEVER** stop between iterations
|
|
263
|
+
- **NEVER** stop between iterations — this is the CORE PROMISE of ralph-loop
|
|
264
|
+
- **NEVER** ask the user "should I continue?" or "is this OK?" — JUST CONTINUE
|
|
265
|
+
- **NEVER** pause to show intermediate results and wait for feedback
|
|
220
266
|
- **BATCH** same-category tasks (max 5)
|
|
221
267
|
- **MODULE COMPLETENESS:** All layers must have completed tasks before advancing
|
|
222
|
-
- Only valid stop
|
|
268
|
+
- **Only valid stop conditions:** all complete, max iterations, dead-end, or user Ctrl+C
|
|
269
|
+
- **If you stop for any other reason, you have VIOLATED the execution guarantee.**
|
|
223
270
|
|
|
224
271
|
---
|
|
225
272
|
|