@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.
- package/package.json +1 -1
- package/templates/agents/ba-reader.md +9 -9
- package/templates/agents/ba-writer.md +13 -8
- package/templates/skills/business-analyse/SKILL.md +4 -4
- package/templates/skills/business-analyse/_rules.md +142 -0
- package/templates/skills/business-analyse/questionnaire/10-documentation.md +22 -7
- package/templates/skills/business-analyse/references/acceptance-criteria.md +4 -4
- package/templates/skills/business-analyse/references/agent-module-prompt.md +13 -9
- package/templates/skills/business-analyse/references/consolidation-structural-checks.md +2 -2
- package/templates/skills/business-analyse/references/deploy-data-build.md +3 -2
- package/templates/skills/business-analyse/references/prd-generation.md +15 -10
- package/templates/skills/business-analyse/references/robustness-checks.md +12 -9
- package/templates/skills/business-analyse/references/team-orchestration.md +77 -6
- package/templates/skills/business-analyse/references/validation-checklist.md +7 -4
- package/templates/skills/business-analyse/schemas/shared/common-defs.json +2 -1
- package/templates/skills/business-analyse/steps/step-00-init.md +31 -63
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +97 -7
- package/templates/skills/business-analyse/steps/step-03a1-setup.md +3 -23
- package/templates/skills/business-analyse/steps/step-03a2-analysis.md +2 -3
- package/templates/skills/business-analyse/steps/step-03b-ui.md +2 -22
- package/templates/skills/business-analyse/steps/step-03c-compile.md +44 -139
- package/templates/skills/business-analyse/steps/step-03d-validate.md +198 -290
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +117 -20
- package/templates/skills/ralph-loop/SKILL.md +8 -3
- package/templates/skills/ralph-loop/references/category-completeness.md +20 -4
- package/templates/skills/ralph-loop/references/compact-loop.md +80 -48
- package/templates/skills/ralph-loop/references/init-resume-recovery.md +3 -1
- package/templates/skills/ralph-loop/references/parallel-execution.md +27 -27
- package/templates/skills/ralph-loop/steps/step-00-init.md +19 -9
- package/templates/skills/ralph-loop/steps/step-01-task.md +10 -2
- package/templates/skills/ralph-loop/steps/step-02-execute.md +9 -4
- package/templates/skills/ralph-loop/steps/step-03-commit.md +1 -1
- package/templates/skills/ralph-loop/steps/step-04-check.md +5 -21
- 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
|
|
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.
|
|
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
|
|
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
|
-
"
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
"
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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.
|
|
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,
|
|
47
|
-
3. Execute first task (step-02,
|
|
48
|
-
4. Commit + update progress (step-03,
|
|
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': () =>
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
task.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
//
|
|
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
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
103
|
-
TeamCreate
|
|
104
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
97
|
-
|
|
96
|
+
// ↓ MANDATORY: invoke apex now (not pseudo-code — execute this skill call)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**INVOKE `/apex -d {nextPhase.prdFile}`**
|
|
98
100
|
|
|
99
|
-
|
|
100
|
-
|
|
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;
|