@atlashub/smartstack-cli 4.17.0 → 4.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 (34) hide show
  1. package/package.json +1 -1
  2. package/templates/agents/ba-reader.md +9 -9
  3. package/templates/agents/ba-writer.md +13 -8
  4. package/templates/skills/business-analyse/SKILL.md +4 -4
  5. package/templates/skills/business-analyse/_rules.md +142 -0
  6. package/templates/skills/business-analyse/questionnaire/10-documentation.md +22 -7
  7. package/templates/skills/business-analyse/references/acceptance-criteria.md +4 -4
  8. package/templates/skills/business-analyse/references/agent-module-prompt.md +13 -9
  9. package/templates/skills/business-analyse/references/consolidation-structural-checks.md +2 -2
  10. package/templates/skills/business-analyse/references/deploy-data-build.md +3 -2
  11. package/templates/skills/business-analyse/references/prd-generation.md +15 -10
  12. package/templates/skills/business-analyse/references/robustness-checks.md +12 -9
  13. package/templates/skills/business-analyse/references/team-orchestration.md +77 -6
  14. package/templates/skills/business-analyse/references/validation-checklist.md +7 -4
  15. package/templates/skills/business-analyse/schemas/shared/common-defs.json +2 -1
  16. package/templates/skills/business-analyse/steps/step-00-init.md +31 -63
  17. package/templates/skills/business-analyse/steps/step-01-cadrage.md +97 -7
  18. package/templates/skills/business-analyse/steps/step-03a1-setup.md +3 -23
  19. package/templates/skills/business-analyse/steps/step-03a2-analysis.md +2 -3
  20. package/templates/skills/business-analyse/steps/step-03b-ui.md +2 -22
  21. package/templates/skills/business-analyse/steps/step-03c-compile.md +44 -139
  22. package/templates/skills/business-analyse/steps/step-03d-validate.md +198 -290
  23. package/templates/skills/business-analyse/steps/step-05a-handoff.md +117 -20
  24. package/templates/skills/ralph-loop/SKILL.md +8 -3
  25. package/templates/skills/ralph-loop/references/category-completeness.md +20 -4
  26. package/templates/skills/ralph-loop/references/compact-loop.md +80 -48
  27. package/templates/skills/ralph-loop/references/init-resume-recovery.md +3 -1
  28. package/templates/skills/ralph-loop/references/parallel-execution.md +27 -27
  29. package/templates/skills/ralph-loop/steps/step-00-init.md +19 -9
  30. package/templates/skills/ralph-loop/steps/step-01-task.md +10 -2
  31. package/templates/skills/ralph-loop/steps/step-02-execute.md +9 -4
  32. package/templates/skills/ralph-loop/steps/step-03-commit.md +1 -1
  33. package/templates/skills/ralph-loop/steps/step-04-check.md +5 -21
  34. package/templates/skills/ralph-loop/steps/step-05-report.md +6 -1
@@ -178,7 +178,7 @@ Example:
178
178
 
179
179
  ### 4. Map Specification to Files
180
180
 
181
- > **IMPORTANT:** Generate `handoff.filesToCreate` as structured object with 7 categories.
181
+ > **IMPORTANT:** Generate `handoff.filesToCreate` as structured object with 8 categories.
182
182
  > Each file entry is `{path, type, linkedFRs, linkedUCs, module}`. NO free text.
183
183
  > Read `.smartstack/config.json` to extract baseNamespace for project namespace.
184
184
 
@@ -198,9 +198,9 @@ For **EACH module** in topological order:
198
198
  > - API: `src/API/Controllers/HumanResources/ProjectsController.cs`
199
199
  > - Tests: `src/Tests/Unit/Domain/HumanResources/Projects/ProjectTests.cs`
200
200
 
201
- #### 4.1-4.7 File Category Templates
201
+ #### 4.1-4.8 File Category Templates
202
202
 
203
- See [references/handoff-file-templates.md](../references/handoff-file-templates.md) for the complete JSON templates for all 7 categories:
203
+ See [references/handoff-file-templates.md](../references/handoff-file-templates.md) for the complete JSON templates for all 8 categories:
204
204
 
205
205
  | Category | Source | Key rules |
206
206
  |----------|--------|-----------|
@@ -211,6 +211,11 @@ See [references/handoff-file-templates.md](../references/handoff-file-templates.
211
211
  | **4.5 Frontend** | `specification.uiWireframes[]` | Pages, Components, Hooks + wireframe traceability |
212
212
  | **4.6 SeedData** | `specification.seedDataCore` | 2 app-level + per module CORE (NavigationModule + NavigationSections + Permissions + Roles) + business + IClientSeedDataProvider |
213
213
  | **4.7 Tests** | All layers | Unit, Integration, Security tests |
214
+ | **4.8 Documentation** | `cadrage.documentation` | Technical docs, user guides, tooltips (doc-data.ts). Peut être `[]` si aucune doc requise. |
215
+
216
+ > **Catégorie `documentation` :** Inclure les fichiers identifiés en step-01-cadrage §4g.
217
+ > - Si `cadrage.documentation.userDocumentation.needed = false` ET `cadrage.documentation.technicalDocumentation.needed = false` → `"documentation": []`
218
+ > - Sinon, lister les fichiers attendus : `doc-data.ts` (tooltips in-app), guides Markdown, specs API supplémentaires.
214
219
 
215
220
  #### Route Convention (CRITICAL)
216
221
 
@@ -336,19 +341,54 @@ Additional per-section pages (dashboard, import, etc.) are separate entries.
336
341
  > **CRITICAL:** Cross-module integration tasks MUST exist in a dedicated PRD file.
337
342
  > Without this, ralph-loop will NOT execute E2E tests or FK validation.
338
343
 
344
+ > **FORMAT:** Must use ralph-loop v3 format with `tasks[]` array (NOT `implementation.filesToCreate`).
345
+ > Ralph's step-01 v3 fast path requires `$version === "3.0.0"` AND `tasks[]` to be present.
346
+ > A PRD with only `implementation.filesToCreate` will be silently ignored by ralph-loop.
347
+
339
348
  Generate `.ralph/prd-CrossModule.json` with:
340
349
  ```json
341
350
  {
342
351
  "$version": "3.0.0",
343
- "implementation": {
344
- "filesToCreate": {
345
- "tests": [
346
- { "path": "src/Tests/Integration/CrossModule/ForeignKeyValidationTests.cs", "type": "IntegrationTests", "module": "CrossModule" },
347
- { "path": "src/Tests/Integration/CrossModule/PermissionMatrixTests.cs", "type": "SecurityTests", "module": "CrossModule" },
348
- { "path": "src/Tests/Integration/CrossModule/E2EFlowTests.cs", "type": "E2ETests", "module": "CrossModule" }
349
- ]
352
+ "feature": "CrossModule Integration Tests",
353
+ "status": "pending",
354
+ "project": { "module": "CrossModule", "application": "{AppName}" },
355
+ "config": { "max_iterations": 10, "current_iteration": 1 },
356
+ "metadata": { "sourceFeatureJson": "{path to master feature.json}" },
357
+ "tasks": [
358
+ {
359
+ "id": "CM-001",
360
+ "description": "Generate FK validation integration tests across all modules",
361
+ "status": "pending",
362
+ "category": "test",
363
+ "dependencies": [],
364
+ "acceptance_criteria": "ForeignKeyValidationTests.cs created, all FK references validated across modules, dotnet test passes",
365
+ "started_at": null, "completed_at": null, "iteration": null,
366
+ "commit_hash": null, "files_changed": [], "validation": null, "error": null
367
+ },
368
+ {
369
+ "id": "CM-002",
370
+ "description": "Generate permission matrix integration tests (cross-module access control)",
371
+ "status": "pending",
372
+ "category": "test",
373
+ "dependencies": ["CM-001"],
374
+ "acceptance_criteria": "PermissionMatrixTests.cs created, all cross-module permission paths validated, dotnet test passes",
375
+ "started_at": null, "completed_at": null, "iteration": null,
376
+ "commit_hash": null, "files_changed": [], "validation": null, "error": null
377
+ },
378
+ {
379
+ "id": "CM-003",
380
+ "description": "Generate E2E flow integration tests covering multi-module user journeys",
381
+ "status": "pending",
382
+ "category": "test",
383
+ "dependencies": ["CM-001", "CM-002"],
384
+ "acceptance_criteria": "E2EFlowTests.cs created, end-to-end flows tested, dotnet test passes",
385
+ "started_at": null, "completed_at": null, "iteration": null,
386
+ "commit_hash": null, "files_changed": [], "validation": null, "error": null
350
387
  }
351
- }
388
+ ],
389
+ "history": [],
390
+ "created": "{ISO timestamp}",
391
+ "updated_at": "{ISO timestamp}"
352
392
  }
353
393
  ```
354
394
 
@@ -359,18 +399,52 @@ Add to progress.txt after all module tasks.
359
399
  > **Only generated when `workflow.mode === "project"`.**
360
400
  > Contains cross-application integration tests and shared entity validation.
361
401
 
402
+ > **FORMAT:** Same v3 `tasks[]` format as 6c above — NOT `implementation.filesToCreate`.
403
+
404
+ Generate `.ralph/prd-CrossApplication.json` with:
362
405
  ```json
363
406
  {
364
407
  "$version": "3.0.0",
365
- "implementation": {
366
- "filesToCreate": {
367
- "tests": [
368
- { "path": "src/Tests/Integration/CrossApplication/SharedEntityValidationTests.cs", "type": "IntegrationTests", "module": "CrossApplication" },
369
- { "path": "src/Tests/Integration/CrossApplication/CrossAppPermissionTests.cs", "type": "SecurityTests", "module": "CrossApplication" },
370
- { "path": "src/Tests/Integration/CrossApplication/CrossAppE2EFlowTests.cs", "type": "E2ETests", "module": "CrossApplication" }
371
- ]
408
+ "feature": "CrossApplication Integration Tests",
409
+ "status": "pending",
410
+ "project": { "module": "CrossApplication", "application": "{ProjectName}" },
411
+ "config": { "max_iterations": 10, "current_iteration": 1 },
412
+ "metadata": { "sourceFeatureJson": "{path to project feature.json}" },
413
+ "tasks": [
414
+ {
415
+ "id": "CA-001",
416
+ "description": "Generate shared entity validation tests across all applications",
417
+ "status": "pending",
418
+ "category": "test",
419
+ "dependencies": [],
420
+ "acceptance_criteria": "SharedEntityValidationTests.cs created, shared entities consistent across apps, dotnet test passes",
421
+ "started_at": null, "completed_at": null, "iteration": null,
422
+ "commit_hash": null, "files_changed": [], "validation": null, "error": null
423
+ },
424
+ {
425
+ "id": "CA-002",
426
+ "description": "Generate cross-application permission integration tests",
427
+ "status": "pending",
428
+ "category": "test",
429
+ "dependencies": ["CA-001"],
430
+ "acceptance_criteria": "CrossAppPermissionTests.cs created, inter-app permissions validated, dotnet test passes",
431
+ "started_at": null, "completed_at": null, "iteration": null,
432
+ "commit_hash": null, "files_changed": [], "validation": null, "error": null
433
+ },
434
+ {
435
+ "id": "CA-003",
436
+ "description": "Generate cross-application E2E flow tests",
437
+ "status": "pending",
438
+ "category": "test",
439
+ "dependencies": ["CA-001", "CA-002"],
440
+ "acceptance_criteria": "CrossAppE2EFlowTests.cs created, multi-app user journeys tested, dotnet test passes",
441
+ "started_at": null, "completed_at": null, "iteration": null,
442
+ "commit_hash": null, "files_changed": [], "validation": null, "error": null
372
443
  }
373
- }
444
+ ],
445
+ "history": [],
446
+ "created": "{ISO timestamp}",
447
+ "updated_at": "{ISO timestamp}"
374
448
  }
375
449
  ```
376
450
 
@@ -794,7 +868,30 @@ ELSE:
794
868
  })
795
869
  ```
796
870
 
797
- #### 7d. Final Verification (BLOCKING)
871
+ #### 7d. PRD Metadata Contract (MANDATORY)
872
+
873
+ > **Ralph-loop quality gate** (`step-01 section 1b`) reads `prd.metadata.sourceFeatureJson` to locate
874
+ > the feature.json and verify `handoff.status === "handed-off"`. This field MUST be written by
875
+ > `ss derive-prd` into each generated PRD's `metadata` section.
876
+
877
+ Verify that the `ss derive-prd` MCP tool writes the following into each generated PRD:
878
+ ```json
879
+ {
880
+ "metadata": {
881
+ "sourceFeatureJson": "{absolute or relative path to this module's feature.json}",
882
+ "moduleCode": "{module code}",
883
+ "application": "{application name}"
884
+ }
885
+ }
886
+ ```
887
+
888
+ **If `sourceFeatureJson` is absent from the generated PRD**, ralph-loop falls back to a file search
889
+ (`findFile('docs/business/**/business-analyse/**/feature.json')`). This works for single-feature projects
890
+ but may return the wrong feature.json in multi-feature projects. Report this to the MCP team if observed.
891
+
892
+ ---
893
+
894
+ #### 7e. Final Verification (BLOCKING)
798
895
 
799
896
  ```
800
897
  count = 0
@@ -43,9 +43,9 @@ Execute the Ralph Weegund technique — iterative module orchestration. Ralph re
43
43
 
44
44
  **Single module:**
45
45
  1. Init: parse flags, verify MCP, setup .ralph/ (step-00)
46
- 2. Load tasks from prd.json — unified v3 has pre-computed tasks (step-01, READ ONCE)
47
- 3. Execute first task (step-02, READ ONCE)
48
- 4. Commit + update progress (step-03, READ ONCE)
46
+ 2. Load tasks from prd.json — unified v3 has pre-computed tasks (step-01, step FILE read once)
47
+ 3. Execute first task (step-02, step FILE read once)
48
+ 4. Commit + update progress (step-03, step FILE read once)
49
49
  5. Check completion → enter COMPACT LOOP (step-04)
50
50
  6. **COMPACT LOOP** (step-04 → references/compact-loop.md):
51
51
  - Find eligible → batch by category (max 5) → execute → test → commit → loop
@@ -105,6 +105,7 @@ LOAD → DELEGATE TO /apex -d → VERIFY PRD STATE → COMMIT PRD → NEXT MODUL
105
105
  | `{section_phases}` | SectionPhase[] | Phase execution plan (Phase 0: foundation, Phase 1..N: per-section) |
106
106
  | `{parallel_mode}` | boolean | Teams mode enabled via `-p` flag. Ralph generates Phase 0 (entities) sequentially, then spawns parallel teammates. |
107
107
  | `{team_name}` | string\|null | Team name for parallel mode (e.g., "smartstack-hr") |
108
+ | `{team_active}` | boolean | True after `TeamCreate`, false after `TeamDelete`. Used in step-04 section 3a to send `MODULE_COMPLETE` to team-lead instead of advancing the module queue locally. |
108
109
  </state_variables>
109
110
 
110
111
  <mcp_requirements>
@@ -162,6 +163,10 @@ When the user invokes `/ralph-loop`, they are giving you the instruction to:
162
163
  | 04 | `steps/step-04-check.md` | Check completion, compact loop entry | ~185 |
163
164
  | 05 | `steps/step-05-report.md` | Generate final report | ~150 |
164
165
 
166
+ > **"Read Once" rule:** Each **step file** (step-01 to step-04) is loaded only once at the start of the first iteration. From iteration 2 onward, `compact-loop.md` drives the loop inline — step files are NOT re-read. This reduces context consumption. **Exception:** prd.json is re-read every iteration (it changes); step files are not.
167
+
168
+ > **Distinction:** `/apex -d prd.json` is invoked every iteration (B2 of compact-loop) — that is normal and expected. "Read Once" applies only to the step instruction files, not to data files or apex delegations.
169
+
165
170
  **Reference files (loaded conditionally):**
166
171
  | File | Loaded when |
167
172
  |------|-------------|
@@ -151,14 +151,30 @@ if (guardrailsNeeded.length > 0) {
151
151
 
152
152
  ## Artifact Verification
153
153
 
154
- After category check, verify artifacts actually exist for completed tasks:
154
+ After category check, verify artifacts actually exist for completed tasks.
155
+
156
+ > **Patterns read from PRD metadata first** (`prd.metadata.artifactPatterns`), falling back to SmartStack defaults.
157
+ > This allows non-standard project structures to work without hardcoded paths.
155
158
 
156
159
  ```javascript
157
160
  const completedCats = new Set(prd.tasks.filter(t => t.status === 'completed').map(t => t.category));
161
+
162
+ // Read custom patterns from PRD metadata (set by ss derive-prd based on project structure)
163
+ const customPatterns = prd.metadata?.artifactPatterns || {};
164
+
158
165
  const artifactChecks = {
159
- 'frontend': () => glob.sync('**/src/pages/**/*.tsx').length > 0,
160
- 'test': () => glob.sync('tests/**/*Tests.cs').length > 0,
161
- 'api': () => glob.sync('src/*/Controllers/**/*Controller.cs').length > 0
166
+ 'frontend': () => {
167
+ const patterns = customPatterns.frontend || ['**/src/pages/**/*.tsx', '**/src/app/**/*.tsx'];
168
+ return patterns.some(p => glob.sync(p).length > 0);
169
+ },
170
+ 'test': () => {
171
+ const patterns = customPatterns.test || ['tests/**/*Tests.cs', '**/*.spec.ts', '**/*.test.ts'];
172
+ return patterns.some(p => glob.sync(p).length > 0);
173
+ },
174
+ 'api': () => {
175
+ const patterns = customPatterns.api || ['src/*/Controllers/**/*Controller.cs', 'src/**/controllers/**/*.ts'];
176
+ return patterns.some(p => glob.sync(p).length > 0);
177
+ }
162
178
  };
163
179
 
164
180
  for (const [cat, check] of Object.entries(artifactChecks)) {
@@ -36,7 +36,7 @@ if (prd._sectionSplit?.enabled) {
36
36
 
37
37
  if (!nextPhase) {
38
38
  // All phases done — check if master PRD tasks are all complete
39
- goto CHECK_COMPLETION;
39
+ // → STOP this section. Return to step-04 section 1 (CHECK_COMPLETION path).
40
40
  }
41
41
 
42
42
  const depsOk = nextPhase.dependsOn.every(depIdx =>
@@ -46,7 +46,7 @@ if (prd._sectionSplit?.enabled) {
46
46
  if (!depsOk) {
47
47
  // Phase blocked — should not happen with topological sort
48
48
  console.warn(`Phase ${nextPhase.phase} blocked — dependencies incomplete`);
49
- goto CHECK_COMPLETION;
49
+ // → STOP this section. Return to step-04 section 1 (CHECK_COMPLETION path).
50
50
  }
51
51
 
52
52
  const phaseLabel = nextPhase.type === 'foundation'
@@ -54,11 +54,14 @@ if (prd._sectionSplit?.enabled) {
54
54
  : `Phase ${nextPhase.phase}: Section "${nextPhase.sectionCode}" (${nextPhase.entities.length} entities)`;
55
55
  console.log(`[{iteration}/{max}] SECTION SPLIT: ${phaseLabel}`);
56
56
 
57
- // INVOKE /apex -d {nextPhase.prdFile}
58
- // Apex sees a normal (smaller) PRD and executes normally
57
+ // MANDATORY: execute this skill call now (not pseudo-code)
58
+ ```
59
+
60
+ **INVOKE `/apex -d {nextPhase.prdFile}`**
61
+
62
+ Apex sees a focused PRD for this phase only. After apex returns, continue:
59
63
 
60
- // After apex returns: merge results back into master PRD
61
- // Read references/section-splitting.md sections 7-9
64
+ ```javascript
62
65
  const phasePrd = readJSON(nextPhase.prdFile);
63
66
  for (const phaseTask of phasePrd.tasks) {
64
67
  const masterTask = prd.tasks.find(t => t.id === phaseTask.id);
@@ -96,7 +99,7 @@ if (prd._sectionSplit?.enabled) {
96
99
  writeJSON('.ralph/prd.json', prd);
97
100
 
98
101
  // → Skip to section C (commit PRD state), then D (loop back)
99
- goto COMMIT_PRD;
102
+ // → SKIP sections A and B. Jump directly to section C (COMMIT_PRD) below.
100
103
  }
101
104
  ```
102
105
 
@@ -112,43 +115,65 @@ if (prd._sectionSplit?.enabled) {
112
115
  ```javascript
113
116
  const MAX_RETRIES = 3;
114
117
 
118
+ // DEAD-END PRE-CHECK: verify if we are truly stuck BEFORE resetting retries.
119
+ // Resetting retries FIRST would mask a dead-end — the check must run on the raw state.
120
+ const failedBeforeRetry = prd.tasks.filter(t => t.status === 'failed');
121
+ const retriableBeforeReset = failedBeforeRetry.filter(t => (t._retryCount || 0) < MAX_RETRIES);
122
+ const hasPendingBeforeRetry = prd.tasks.some(t => t.status === 'pending');
123
+ const allDoneBeforeRetry = prd.tasks.every(t => t.status === 'completed' || t.status === 'skipped');
124
+
125
+ const isDeadEnd = !hasPendingBeforeRetry && !allDoneBeforeRetry && retriableBeforeReset.length === 0;
126
+
127
+ if (isDeadEnd) {
128
+ // TRUE dead-end: no pending tasks, not all done, and no retries left.
129
+ // Mark prd.status = 'failed' NOW so step-04 section 4 routes to step-05.
130
+ // Then fall through to `eligible.length === 0` below which returns to step-04.
131
+ console.log(`DEAD-END: ${prd.tasks.filter(t=>t.status==='completed').length} done, ${failedBeforeRetry.length} failed (retries exhausted), ${prd.tasks.filter(t=>t.status==='blocked').length} blocked`);
132
+ prd.status = 'failed';
133
+ writeJSON('.ralph/prd.json', prd);
134
+ // DO NOT reset retries below — skip to eligible search (will find 0 tasks → CHECK_COMPLETION)
135
+ }
136
+
115
137
  // RETRY: Reset failed tasks to pending if retries remain (max 3 attempts per task)
116
- for (const task of prd.tasks) {
117
- if (task.status !== 'failed') continue;
118
- const retryCount = task._retryCount || 0;
119
- if (retryCount < MAX_RETRIES) {
120
- task.status = 'pending';
121
- task._retryCount = retryCount + 1;
122
- task.error = null;
123
- console.log(`RETRY [${task.id}] attempt ${retryCount + 1}/${MAX_RETRIES}: ${task.description.substring(0, 60)}`);
138
+ // Skipped when isDeadEnd = true (no retriable tasks remain anyway).
139
+ if (!isDeadEnd) {
140
+ for (const task of prd.tasks) {
141
+ if (task.status !== 'failed') continue;
142
+ const retryCount = task._retryCount || 0;
143
+ if (retryCount < MAX_RETRIES) {
144
+ task.status = 'pending';
145
+ task._retryCount = retryCount + 1;
146
+ task.error = null;
147
+ console.log(`RETRY [${task.id}] attempt ${retryCount + 1}/${MAX_RETRIES}: ${task.description.substring(0, 60)}`);
148
+ }
124
149
  }
125
- }
126
150
 
127
- // Unblock tasks whose dependencies are no longer failed (due to retries above)
128
- for (const task of prd.tasks) {
129
- if (task.status !== 'blocked') continue;
130
- const stillBlocked = task.dependencies.some(depId => {
131
- const dep = prd.tasks.find(t => t.id === depId);
132
- return dep && (dep.status === 'failed' || dep.status === 'blocked');
133
- });
134
- if (!stillBlocked) {
135
- task.status = 'pending';
136
- task.error = null;
137
- console.log(`UNBLOCKED [${task.id}]: dependencies resolved`);
151
+ // Unblock tasks whose dependencies are no longer failed (due to retries above)
152
+ for (const task of prd.tasks) {
153
+ if (task.status !== 'blocked') continue;
154
+ const stillBlocked = task.dependencies.some(depId => {
155
+ const dep = prd.tasks.find(t => t.id === depId);
156
+ return dep && (dep.status === 'failed' || dep.status === 'blocked');
157
+ });
158
+ if (!stillBlocked) {
159
+ task.status = 'pending';
160
+ task.error = null;
161
+ console.log(`UNBLOCKED [${task.id}]: dependencies resolved`);
162
+ }
138
163
  }
139
- }
140
164
 
141
- // Block pending tasks whose dependencies are still failed/blocked
142
- for (const task of prd.tasks) {
143
- if (task.status !== 'pending') continue;
144
- const depsBlocked = task.dependencies.some(depId => {
145
- const dep = prd.tasks.find(t => t.id === depId);
146
- return dep && (dep.status === 'failed' || dep.status === 'blocked');
147
- });
148
- if (depsBlocked) { task.status = 'blocked'; task.error = 'Blocked by failed dependency'; }
149
- }
165
+ // Block pending tasks whose dependencies are still failed/blocked
166
+ for (const task of prd.tasks) {
167
+ if (task.status !== 'pending') continue;
168
+ const depsBlocked = task.dependencies.some(depId => {
169
+ const dep = prd.tasks.find(t => t.id === depId);
170
+ return dep && (dep.status === 'failed' || dep.status === 'blocked');
171
+ });
172
+ if (depsBlocked) { task.status = 'blocked'; task.error = 'Blocked by failed dependency'; }
173
+ }
150
174
 
151
- writeJSON('.ralph/prd.json', prd);
175
+ writeJSON('.ralph/prd.json', prd);
176
+ } // end if (!isDeadEnd)
152
177
 
153
178
  // Find ALL eligible tasks (dependencies met)
154
179
  const eligible = prd.tasks.filter(task => {
@@ -160,8 +185,8 @@ const eligible = prd.tasks.filter(task => {
160
185
  });
161
186
 
162
187
  if (eligible.length === 0) {
163
- // Dead-end or all done — return to step-04 sections 2-4
164
- goto CHECK_COMPLETION;
188
+ // All done or dead-end — return to step-04 sections 2-4
189
+ // [goto CHECK_COMPLETION]
165
190
  }
166
191
 
167
192
  // BATCH: group by category, take first group (max 5)
@@ -265,21 +290,28 @@ chore(ralph): update PRD state — iteration {iteration}
265
290
  Tasks: {completed}/{batch_size} completed via /apex
266
291
  Overall: {totalCompleted}/{total}
267
292
 
268
- Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
293
+ Co-Authored-By: Claude AI <noreply@anthropic.com>
269
294
  EOF
270
295
  )"
271
296
  ```
272
297
 
273
298
  ### C4. PRD Sync Verification (HARD CHECK)
274
299
 
275
- ```javascript
276
- const prdVerify = readJSON('.ralph/prd.json');
277
- const actuallyCompleted = prdVerify.tasks.filter(t => batchIds.includes(t.id) && t.status === 'completed');
300
+ > **Note:** `prdCheck` (read in C1) is the authoritative post-apex snapshot.
301
+ > `completed` (from B3) was computed on a DIFFERENT read — do NOT mix the two.
302
+ > Re-derive the count from `prdCheck` to avoid stale-reference mismatches.
278
303
 
279
- if (actuallyCompleted.length !== completed) {
280
- console.error('PRD SYNC ERROR: Mismatch between apex results and prd.json');
281
- // Force re-read apex may have written after our read
282
- // DO NOT overwrite apex is the source of truth for task statuses
304
+ ```javascript
305
+ // Re-derive completed count from the SAME object used for the commit (prdCheck)
306
+ const actuallyCompleted = prdCheck.tasks.filter(t => batchIds.includes(t.id) && t.status === 'completed').length;
307
+ const actuallyFailed = prdCheck.tasks.filter(t => batchIds.includes(t.id) && t.status === 'failed').length;
308
+
309
+ if (actuallyCompleted !== completed) {
310
+ console.warn(`PRD SYNC WARNING: B3 reported ${completed} completed, prdCheck shows ${actuallyCompleted}`);
311
+ console.warn('Using prdCheck as source of truth (apex may have written between reads)');
312
+ // DO NOT overwrite prdCheck — apex is the source of truth for task statuses
313
+ // Update local `completed` variable for progress reporting consistency
314
+ completed = actuallyCompleted;
283
315
  }
284
316
  ```
285
317
 
@@ -88,7 +88,9 @@ if (handoffStatus === 'handed-off') {
88
88
  console.log('BA artifacts detected without PRD — auto-recovering...');
89
89
 
90
90
  // Call ss derive-prd to generate PRD files
91
- const result = exec(`ss derive-prd --application ${masterFeature}`);
91
+ // Flag: --feature (path to feature.json) — matches ss derive-prd CLI convention
92
+ // See also: step-05c-ralph-readiness.md section 2 "Fix: ss derive-prd --feature {path} --output ..."
93
+ const result = exec(`ss derive-prd --feature ${masterFeature}`);
92
94
 
93
95
  if (result.exitCode !== 0) {
94
96
  console.error('ss derive-prd failed — cannot auto-recover');
@@ -63,7 +63,7 @@ git commit -m "chore(foundation): entities for all modules
63
63
 
64
64
  $(allModules.map(m => m.code).join(' + ')) — ${allEntities.length} entities
65
65
 
66
- Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
66
+ Co-Authored-By: Claude AI <noreply@anthropic.com>"
67
67
  ```
68
68
 
69
69
  Result: ModelSnapshot contains all entities. Database ready for application layers.
@@ -159,7 +159,8 @@ DO NOT:
159
159
  - Modify DbContext (already done in Phase 0)
160
160
 
161
161
  EXECUTE:
162
- For each PRD file: invoke /apex -d {prdFile}
162
+ For each PRD file:
163
+ **INVOKE `/apex -d {prdFile}`**
163
164
  Apex will detect existing entities and skip domain/infrastructure layers.
164
165
 
165
166
  When done, send a message to team lead with results:
@@ -177,33 +178,32 @@ When done, send a message to team lead with results:
177
178
 
178
179
  ### 4. Wait for Teammates
179
180
 
180
- Ralph waits for all teammates to send completion messages:
181
+ Ralph waits for all teammates to send completion messages via `TaskOutput`.
181
182
 
182
- ```javascript
183
- const MAX_WAIT = 3600; // 1 hour timeout
184
- const POLL_INTERVAL = 5; // 5 seconds
185
-
186
- const teamMateResults = [];
187
- const deadline = Date.now() + MAX_WAIT * 1000;
188
-
189
- while (teamMateResults.length < teammates.length && Date.now() < deadline) {
190
- // Check for new messages from teammates
191
- const messages = getTeamMessages();
192
- for (const msg of messages) {
193
- if (msg.type === 'completion') {
194
- teamMateResults.push(msg);
195
- console.log(` ✅ ${msg.teammate}: ${msg.status}`);
196
- }
197
- }
198
- if (teamMateResults.length < teammates.length) {
199
- sleep(POLL_INTERVAL * 1000);
200
- }
201
- }
183
+ > **How messaging works in Claude Code Agent Teams:**
184
+ > - Teammates communicate by calling `SendMessage({ recipient: "team-lead", content: ... })`
185
+ > - Ralph (team lead) receives messages automatically — there is NO polling loop
186
+ > - Each message delivery triggers a new turn for Ralph
187
+ > - Ralph stays idle between messages — this is normal behavior (not a hang)
188
+ > - An idle notification does NOT mean the teammate has finished or failed
202
189
 
203
- if (teamMateResults.length < teammates.length) {
204
- console.error(`TIMEOUT: ${teammates.length - teamMateResults.length} teammates did not complete`);
205
- // Set status to 'partial'
206
- }
190
+ ```javascript
191
+ // Track expected completions (set before spawning)
192
+ const expectedCount = teammates.length;
193
+ const results = []; // populated as SendMessage notifications arrive
194
+
195
+ // Ralph's response to each incoming message:
196
+ // - Parse content: "complete" | "failed"
197
+ // - Push to results[]
198
+ // - If results.length === expectedCount → proceed to section 5
199
+ // - If "failed": log error, continue waiting for other teammates
200
+
201
+ // If a teammate goes silent > 30 min:
202
+ // → Send it a wake-up message (idle is normal, silence is not)
203
+ // → If no response after 2 retries: mark as "timed-out" and continue
204
+
205
+ console.log(`Waiting for ${expectedCount} teammates...`);
206
+ // (Ralph is idle here — woken up by each incoming SendMessage)
207
207
  ```
208
208
 
209
209
  ### 5. Collect & Validate Results
@@ -91,21 +91,31 @@ Quick: `glob('.ralph/prd-*.json').length > 1` → create modules-queue.json
91
91
 
92
92
  ---
93
93
 
94
- ## 4c. Team Orchestration (multi-module, 2+ modules)
94
+ ## 4c. Team Orchestration (multi-module LEGACY — dependency layer mode only)
95
+
96
+ > **MANDATORY:** Load `references/team-orchestration.md` BEFORE creating any team.
97
+ > The full spawn protocol (teammate prompts, wait logic, layer coordination, cleanup)
98
+ > is defined there. Creating a team without loading it results in an empty team with no teammates.
99
+
100
+ > **PARALLEL MODE (`-p`):** Team creation does NOT happen here. It happens in step-02 section 3a,
101
+ > after step-01 loads the tasks. Sections 5-7 below (completion promise, metadata, summary) must
102
+ > still be executed. Do NOT return early when `{parallel_mode}` is true.
95
103
 
96
104
  ```javascript
97
105
  if (PRD_COUNT > 1) {
98
- // Read references/team-orchestration.md for full protocol
106
+ // STEP 1: Load references/team-orchestration.md NOW (before any TeamCreate call)
107
+ // This file contains: mode selection, Phase 0 protocol (parallel), layer protocol (legacy), cleanup.
108
+
99
109
  const layers = master.metadata?.workflow?.dependencyGraph?.layers;
100
110
 
101
- if (layers) {
102
- // PARALLEL MODE: Use Agent Teams
103
- TeamCreate({ team_name: `ralph-${appName}` });
104
- // Spawn teammates per layer (see reference)
105
- // STOP here — team orchestration takes over
111
+ if (layers && !{parallel_mode}) {
112
+ // DEPENDENCY LAYER MODE (legacy, no -p flag): team orchestration takes over here.
113
+ // TeamCreate + spawn logic are defined in references/team-orchestration.md section "Dependency Layer Mode".
114
+ // STOP here legacy team orchestration handles everything from this point.
106
115
  return;
107
116
  }
108
- // If no layers: fall through to sequential mode
117
+ // PARALLEL MODE (-p): fall through to sections 5-7, team created later in step-02 section 3a.
118
+ // NO -p, NO layers: fall through to sequential mode (compact loop per module).
109
119
  }
110
120
  ```
111
121
 
@@ -123,7 +133,7 @@ if (!completion_promise) {
123
133
  ## 6. Collect Metadata
124
134
 
125
135
  ```bash
126
- CLI_VERSION=$(node -e "console.log(require('package.json').version)" 2>/dev/null || echo "unknown")
136
+ CLI_VERSION=$(node -e "try{console.log(require('./package.json').version)}catch(e){console.log('unknown')}" 2>/dev/null || echo "unknown")
127
137
  CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
128
138
  PROJECT_PATH=$(pwd)
129
139
  ```
@@ -58,16 +58,24 @@ If `.ralph/prd.json` exists:
58
58
 
59
59
  If `.ralph/prd.json` does not exist: continue to section 1b.
60
60
 
61
- ### 1b. BA Handoff Quality Gate (BLOCKING)
61
+ ### 1b. BA Handoff Quality Gate (BLOCKING when BA artifacts present)
62
62
 
63
63
  > **CRITICAL:** Before generating ANY tasks, verify the BA handoff is complete.
64
64
  > A missing handoff means no frontend files, no test plan, no BR-to-code mapping.
65
65
 
66
+ > **INTENTIONAL SKIP:** If `sourceFeatureJson` is null (manual start via `{task_description}`, no BA run),
67
+ > this quality gate is **intentionally skipped** — the loop proceeds in "manual mode" and generates tasks
68
+ > from the task description. This is by design, not a bug. Inform the user in the summary (section 6 of step-00).
69
+
66
70
  ```javascript
67
71
  const sourceFeatureJson = prd?.metadata?.sourceFeatureJson
68
72
  || findFile('docs/business/**/business-analyse/**/feature.json');
69
73
 
70
- if (sourceFeatureJson) {
74
+ if (!sourceFeatureJson) {
75
+ // Manual mode: no BA artifacts found — skip quality gate
76
+ console.log('Manual mode: no BA artifacts detected — tasks will be generated from task description');
77
+ // Continue to section 2 (Analyze Task Description)
78
+ } else {
71
79
  const feature = readJSON(sourceFeatureJson);
72
80
  const handoff = feature.handoff || {};
73
81
 
@@ -93,11 +93,16 @@ if (prd._sectionSplit?.enabled) {
93
93
  : `Phase ${nextPhase.phase}: Section "${nextPhase.sectionCode}" (services + controllers + pages + tests)`;
94
94
  console.log(`SECTION SPLIT: ${phaseLabel}`);
95
95
 
96
- // INVOKE /apex -d {nextPhase.prdFile}
97
- // Apex sees a normal (smaller) PRD and executes normally
96
+ // MANDATORY: invoke apex now (not pseudo-code — execute this skill call)
97
+ ```
98
+
99
+ **INVOKE `/apex -d {nextPhase.prdFile}`**
98
100
 
99
- // After apex returns: merge results into master PRD
100
- // Read references/section-splitting.md sections 7-9 (execution, merging, failure handling)
101
+ Apex sees a focused (smaller) PRD covering only this phase's scope and executes normally.
102
+ After apex returns: merge results into master PRD.
103
+ Read `references/section-splitting.md` sections 7-9 for execution, merging, and failure handling.
104
+
105
+ ```javascript
101
106
 
102
107
  nextPhase.status = 'completed';
103
108
  prd._sectionSplit.currentPhase = nextPhase.phase;
@@ -52,7 +52,7 @@ chore(ralph): PRD state — iteration {iteration}, {completed}/{total} tasks
52
52
 
53
53
  Module: {current_module}
54
54
 
55
- Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
55
+ Co-Authored-By: Claude AI <noreply@anthropic.com>
56
56
  EOF
57
57
  )"
58
58