@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.
Files changed (26) hide show
  1. package/dist/index.js +74 -42
  2. package/dist/index.js.map +1 -1
  3. package/dist/mcp-entry.mjs +752 -53
  4. package/dist/mcp-entry.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/templates/agents/gitflow/finish.md +21 -3
  7. package/templates/agents/gitflow/start.md +14 -4
  8. package/templates/skills/application/templates-backend.md +12 -1
  9. package/templates/skills/business-analyse/SKILL.md +4 -4
  10. package/templates/skills/business-analyse/html/ba-interactive.html +11 -5
  11. package/templates/skills/business-analyse/html/src/scripts/05-render-specs.js +11 -5
  12. package/templates/skills/business-analyse/references/deploy-data-build.md +25 -9
  13. package/templates/skills/business-analyse/references/validation-checklist.md +29 -2
  14. package/templates/skills/business-analyse/steps/step-00-init.md +23 -5
  15. package/templates/skills/business-analyse/steps/step-03a2-analysis.md +21 -3
  16. package/templates/skills/business-analyse/steps/step-03b-ui.md +31 -1
  17. package/templates/skills/business-analyse/steps/step-03d-validate.md +41 -4
  18. package/templates/skills/business-analyse/steps/step-05b-deploy.md +9 -7
  19. package/templates/skills/business-analyse/steps/step-05c-ralph-readiness.md +222 -40
  20. package/templates/skills/ralph-loop/SKILL.md +41 -1
  21. package/templates/skills/ralph-loop/references/category-rules.md +106 -1
  22. package/templates/skills/ralph-loop/references/compact-loop.md +85 -24
  23. package/templates/skills/ralph-loop/references/core-seed-data.md +48 -0
  24. package/templates/skills/ralph-loop/steps/step-00-init.md +30 -54
  25. package/templates/skills/ralph-loop/steps/step-01-task.md +102 -1
  26. 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 all subsequent iterations inline without re-reading step files.
5
- > **Rule:** NEVER stop the loop. NEVER wait for user input. NEVER re-read step files.
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
- **Finalize tasks in prd.json:**
276
+ ### C4. Finalize PRD with Commit Hash
277
+
231
278
  ```javascript
232
- const now = new Date().toISOString();
279
+ // Update commit hash now that we have the real one
233
280
  for (const task of batch) {
234
- if (task.status !== 'failed') task.status = 'completed';
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
- **Commit progress:**
253
- ```bash
254
- git add .ralph/prd.json .ralph/progress.txt
255
- [ -f .ralph/modules-queue.json ] && git add .ralph/modules-queue.json
256
- git commit -m "chore(ralph): progress — iteration {iteration}"
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
- The only exit conditions:
267
- - ALL tasks complete (all modules if multi-module)
268
- - Max iterations reached
269
- - Dead-end (all remaining blocked/failed)
270
- - User Ctrl+C
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. Human-in-the-Loop Checkpoint (RECOMMENDED)
54
+ ## 3. Execution Mode (FULLY AUTONOMOUS)
52
55
 
53
- > **Best Practice:** Commencer en mode supervisé avant le mode autonome complet.
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
- ### Why HITL First?
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
- Ralph-loop est puissant mais peut diverger si :
58
- - Les requirements sont ambigus
59
- - Les tests sont lents (>30s)
60
- - Le code généré ne compile pas immédiatement
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
- if (!dirExists('.ralph/logs') && !resume_mode) {
79
- AskUserQuestion({
80
- questions: [{
81
- question: "Premier usage de ralph-loop détecté. Démarrer en mode supervisé ou autonome ?",
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
- ### Feedback Speed Warning
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
- Temps test recommandés :
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
- If `{completion_promise}` is null: ask user to choose (COMPLETE, ALL TESTS PASS, DONE, or custom).
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 2.
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: verify all expected layers AND their artifacts
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
- // Inject guardrail tasks for missing layers
134
- let maxId = Math.max(...prd.tasks.map(t => t.id));
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 (CRITICAL)
243
+ ## 5. Compact Loop (MANDATORY — EXECUTE IMMEDIATELY)
202
244
 
203
- > **MANDATORY:** After first full iteration, ALL subsequent iterations use this inline loop.
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. Return to section 1 of THIS step
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: all complete, max iterations, dead-end, or user Ctrl+C
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