@atlashub/smartstack-cli 3.33.0 → 3.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.documentation/agents.html +5 -1
- package/.documentation/apex.html +644 -0
- package/.documentation/business-analyse.html +81 -1
- package/.documentation/cli-commands.html +5 -1
- package/.documentation/commands.html +5 -1
- package/.documentation/efcore.html +5 -1
- package/.documentation/gitflow.html +5 -1
- package/.documentation/hooks.html +5 -1
- package/.documentation/index.html +60 -2
- package/.documentation/init.html +414 -1
- package/.documentation/installation.html +5 -1
- package/.documentation/ralph-loop.html +365 -216
- package/.documentation/test-web.html +5 -1
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +7 -24
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -2
- package/templates/agents/ba-writer.md +142 -15
- package/templates/mcp-scaffolding/controller.cs.hbs +5 -1
- package/templates/skills/apex/SKILL.md +9 -3
- package/templates/skills/apex/_shared.md +49 -4
- package/templates/skills/{ralph-loop → apex}/references/core-seed-data.md +20 -11
- package/templates/skills/{ralph-loop → apex}/references/error-classification.md +2 -1
- package/templates/skills/apex/references/post-checks.md +463 -3
- package/templates/skills/apex/references/smartstack-api.md +76 -8
- package/templates/skills/apex/references/smartstack-frontend.md +74 -1
- package/templates/skills/apex/references/smartstack-layers.md +21 -3
- package/templates/skills/apex/steps/step-00-init.md +121 -1
- package/templates/skills/apex/steps/step-01-analyze.md +58 -0
- package/templates/skills/apex/steps/step-02-plan.md +36 -0
- package/templates/skills/apex/steps/step-03-execute.md +114 -7
- package/templates/skills/apex/steps/step-04-examine.md +116 -2
- package/templates/skills/business-analyse/SKILL.md +31 -20
- package/templates/skills/business-analyse/_module-loop.md +68 -9
- package/templates/skills/business-analyse/_shared.md +80 -21
- package/templates/skills/business-analyse/questionnaire/00-application.md +4 -2
- package/templates/skills/business-analyse/questionnaire/00b-project.md +85 -0
- package/templates/skills/business-analyse/references/deploy-modes.md +69 -0
- package/templates/skills/business-analyse/references/team-orchestration.md +158 -7
- package/templates/skills/business-analyse/schemas/application-schema.json +15 -1
- package/templates/skills/business-analyse/schemas/project-schema.json +490 -0
- package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +2 -1
- package/templates/skills/business-analyse/steps/step-00-init.md +220 -38
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +184 -5
- package/templates/skills/business-analyse/steps/step-01b-applications.md +423 -0
- package/templates/skills/business-analyse/steps/step-02-decomposition.md +23 -6
- package/templates/skills/business-analyse/steps/step-03c-compile.md +14 -2
- package/templates/skills/business-analyse/steps/step-03d-validate.md +32 -7
- package/templates/skills/business-analyse/steps/step-04a-collect.md +111 -0
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +296 -103
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +46 -14
- package/templates/skills/documentation/SKILL.md +92 -2
- package/templates/skills/ralph-loop/SKILL.md +14 -17
- package/templates/skills/ralph-loop/references/category-rules.md +63 -683
- package/templates/skills/ralph-loop/references/compact-loop.md +188 -428
- package/templates/skills/ralph-loop/references/section-splitting.md +439 -0
- package/templates/skills/ralph-loop/references/team-orchestration.md +13 -14
- package/templates/skills/ralph-loop/steps/step-01-task.md +27 -0
- package/templates/skills/ralph-loop/steps/step-02-execute.md +80 -691
- package/templates/skills/ralph-loop/steps/step-03-commit.md +38 -79
- package/templates/skills/ralph-loop/steps/step-04-check.md +39 -58
- package/templates/skills/ralph-loop/steps/step-05-report.md +31 -123
- package/scripts/health-check.sh +0 -168
- package/scripts/postinstall.js +0 -18
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
# Section-Level Splitting — Large Module Handling
|
|
2
|
+
|
|
3
|
+
> **Loaded by:** step-01 section 4c (detection) and compact-loop.md (execution)
|
|
4
|
+
> **Purpose:** When a module has >4 domain entities AND >1 architecture section,
|
|
5
|
+
> split `/apex -d` calls per SECTION instead of sending the whole module at once.
|
|
6
|
+
> **Threshold:** `domainTaskCount > 4 AND sectionCount > 1`
|
|
7
|
+
> **Backward compatible:** Modules with <=4 entities are UNAFFECTED.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Why Section-Level Splitting?
|
|
12
|
+
|
|
13
|
+
When a module has 7+ entities (e.g., HR with Employee, Department, Position, LeaveRequest, LeaveType, TimeTracking, WorkLog), a single `/apex -d` saturates the context window. Result: migrations incomplete, pages missing, conventions lost. Success rate drops from ~95% to ~30%.
|
|
14
|
+
|
|
15
|
+
**Solution:** Phase 0 builds ALL entities + ONE migration (foundation). Phase 1..N build services/controllers/pages PER SECTION. Each section has 1-3 entities — comfortably within apex's safe zone.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Architecture: Phases
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
Phase 0: FOUNDATION (1 apex call)
|
|
23
|
+
- ALL entity classes (Domain layer)
|
|
24
|
+
- ALL EF configurations (Infrastructure layer)
|
|
25
|
+
- ONE migration covering ALL entities
|
|
26
|
+
- Application + Module navigation seed data
|
|
27
|
+
- DbContext registration + DI skeleton
|
|
28
|
+
-> Eliminates migration conflicts between sections
|
|
29
|
+
|
|
30
|
+
Phase 1..N: PER SECTION (1 apex call each)
|
|
31
|
+
- Services + DTOs + Validators for section entities
|
|
32
|
+
- Controllers for section entities
|
|
33
|
+
- Section-level seed data (NavigationSection, permissions, roles)
|
|
34
|
+
- Frontend pages (List, Detail, Create, Edit) for section entities
|
|
35
|
+
- i18n for section entities
|
|
36
|
+
- Tests for section entities
|
|
37
|
+
|
|
38
|
+
Phase Final: CROSS-VALIDATION (inline in step-04)
|
|
39
|
+
- dotnet build (full project)
|
|
40
|
+
- npm run typecheck
|
|
41
|
+
- MCP validate_conventions
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Why ALL Entities in Phase 0?
|
|
45
|
+
|
|
46
|
+
EF Core creates a single ModelSnapshot per DbContext. If Phase 1 creates a migration for Employee+Department and Phase 2 for LeaveRequest+LeaveType, the second migration fails when LeaveRequest has a FK to Employee (ModelSnapshot is inconsistent). **Phase 0 = all entities + 1 migration.** Section phases add services/controllers/pages on top of existing entities.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 1. Detection
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
const prd = readJSON('.ralph/prd.json');
|
|
54
|
+
const domainTasks = prd.tasks.filter(t => t.category === 'domain');
|
|
55
|
+
const sections = prd.architecture?.sections ?? [];
|
|
56
|
+
|
|
57
|
+
const shouldSplit = domainTasks.length > 4 && sections.length > 1;
|
|
58
|
+
|
|
59
|
+
if (!shouldSplit) {
|
|
60
|
+
// Standard execution — no splitting needed
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log(`SECTION SPLIT: ${domainTasks.length} domain tasks, ${sections.length} sections → activating`);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 2. Entity-to-Section Mapping
|
|
70
|
+
|
|
71
|
+
Build from `prd.architecture.sections[].resources[].entity`:
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
const entityToSection = {};
|
|
75
|
+
for (const section of prd.architecture.sections) {
|
|
76
|
+
for (const resource of section.resources ?? []) {
|
|
77
|
+
if (resource.entity) {
|
|
78
|
+
entityToSection[resource.entity] = section.code;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Orphan entities (not in any section) go into Phase 0
|
|
84
|
+
const allEntities = prd.architecture.entities?.map(e => e.name) ?? [];
|
|
85
|
+
const orphans = allEntities.filter(e => !entityToSection[e]);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 3. FK Dependency Resolution
|
|
91
|
+
|
|
92
|
+
Build section dependency graph from `prd.architecture.entities[].relationships[]`:
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
const sectionDeps = {}; // sectionCode -> Set<sectionCode>
|
|
96
|
+
for (const entity of prd.architecture.entities ?? []) {
|
|
97
|
+
const entitySection = entityToSection[entity.name];
|
|
98
|
+
if (!entitySection) continue;
|
|
99
|
+
|
|
100
|
+
for (const rel of entity.relationships ?? []) {
|
|
101
|
+
const targetSection = entityToSection[rel.target];
|
|
102
|
+
// If target is in a DIFFERENT section, this section depends on that one
|
|
103
|
+
if (targetSection && targetSection !== entitySection) {
|
|
104
|
+
sectionDeps[entitySection] = sectionDeps[entitySection] || new Set();
|
|
105
|
+
sectionDeps[entitySection].add(targetSection);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 4. Topological Sort of Sections
|
|
114
|
+
|
|
115
|
+
Order sections by FK dependencies (sections with no deps first):
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
function topoSort(sections, sectionDeps) {
|
|
119
|
+
const sorted = [];
|
|
120
|
+
const visited = new Set();
|
|
121
|
+
const visiting = new Set();
|
|
122
|
+
|
|
123
|
+
function visit(code) {
|
|
124
|
+
if (visited.has(code)) return;
|
|
125
|
+
if (visiting.has(code)) {
|
|
126
|
+
// Circular dependency — break cycle, include as-is
|
|
127
|
+
console.warn(`Circular section dependency detected involving: ${code}`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
visiting.add(code);
|
|
131
|
+
for (const dep of sectionDeps[code] || []) {
|
|
132
|
+
visit(dep);
|
|
133
|
+
}
|
|
134
|
+
visiting.delete(code);
|
|
135
|
+
visited.add(code);
|
|
136
|
+
sorted.push(code);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const section of sections) {
|
|
140
|
+
visit(section.code);
|
|
141
|
+
}
|
|
142
|
+
return sorted;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const orderedSections = topoSort(prd.architecture.sections, sectionDeps);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 5. Phase Construction
|
|
151
|
+
|
|
152
|
+
### 5a. Phase 0 — Foundation PRD
|
|
153
|
+
|
|
154
|
+
Copy the master PRD and keep ONLY foundation tasks:
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
const phase0Prd = deepClone(prd);
|
|
158
|
+
phase0Prd.tasks = prd.tasks.filter(t =>
|
|
159
|
+
t.category === 'domain' ||
|
|
160
|
+
t.category === 'infrastructure' ||
|
|
161
|
+
// Module-level seed data (NavigationApplicationSeedData, NavigationModuleSeedData)
|
|
162
|
+
(t.category === 'seedData' && !t.section)
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// Remove section-specific content from architecture (keep all entities for EF)
|
|
166
|
+
// Keep ALL entities, ALL relationships — Phase 0 needs the full domain model
|
|
167
|
+
|
|
168
|
+
phase0Prd._phaseInfo = { phase: 0, type: 'foundation' };
|
|
169
|
+
writeJSON(`.ralph/prd-${moduleCode}-phase0.json`, phase0Prd);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Phase 0 includes:**
|
|
173
|
+
- ALL `domain` tasks (entity classes, enums, value objects)
|
|
174
|
+
- ALL `infrastructure` tasks (EF configs, DbContext, migration)
|
|
175
|
+
- Module-level `seedData` tasks (NavigationApplication, NavigationModule seed data)
|
|
176
|
+
|
|
177
|
+
**Phase 0 excludes:**
|
|
178
|
+
- `application` tasks (services, DTOs, validators)
|
|
179
|
+
- `api` tasks (controllers)
|
|
180
|
+
- `frontend` tasks (pages, routes, i18n)
|
|
181
|
+
- `test` tasks
|
|
182
|
+
- Section-level `seedData` tasks (NavigationSection, permissions per section)
|
|
183
|
+
|
|
184
|
+
### 5b. Section Phase PRDs (Phase 1..N)
|
|
185
|
+
|
|
186
|
+
For each section in topological order:
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
for (let i = 0; i < orderedSections.length; i++) {
|
|
190
|
+
const sectionCode = orderedSections[i];
|
|
191
|
+
const phaseNum = i + 1;
|
|
192
|
+
|
|
193
|
+
const sectionPrd = deepClone(prd);
|
|
194
|
+
|
|
195
|
+
// Filter tasks by section
|
|
196
|
+
sectionPrd.tasks = prd.tasks.filter(t =>
|
|
197
|
+
t.section === sectionCode && (
|
|
198
|
+
t.category === 'application' ||
|
|
199
|
+
t.category === 'api' ||
|
|
200
|
+
t.category === 'frontend' ||
|
|
201
|
+
t.category === 'i18n' ||
|
|
202
|
+
t.category === 'test' ||
|
|
203
|
+
t.category === 'validation' ||
|
|
204
|
+
// Section-level seed data
|
|
205
|
+
(t.category === 'seedData' && t.section === sectionCode)
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// Filter architecture to section-relevant entities only
|
|
210
|
+
const sectionEntities = new Set();
|
|
211
|
+
const section = prd.architecture.sections.find(s => s.code === sectionCode);
|
|
212
|
+
for (const resource of section?.resources ?? []) {
|
|
213
|
+
if (resource.entity) sectionEntities.add(resource.entity);
|
|
214
|
+
}
|
|
215
|
+
sectionPrd.architecture.entities = prd.architecture.entities?.filter(
|
|
216
|
+
e => sectionEntities.has(e.name)
|
|
217
|
+
) ?? [];
|
|
218
|
+
sectionPrd.architecture.sections = [section];
|
|
219
|
+
|
|
220
|
+
// Filter seedData to section-relevant parts
|
|
221
|
+
if (sectionPrd.seedData?.coreSeedData) {
|
|
222
|
+
const csd = sectionPrd.seedData.coreSeedData;
|
|
223
|
+
if (csd.navigationSections) {
|
|
224
|
+
csd.navigationSections = csd.navigationSections.filter(
|
|
225
|
+
s => s.code === sectionCode
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
if (csd.navigationResources) {
|
|
229
|
+
csd.navigationResources = csd.navigationResources.filter(
|
|
230
|
+
r => sectionEntities.has(r.entity)
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Resolve phase dependencies
|
|
236
|
+
const deps = [0]; // All sections depend on Phase 0
|
|
237
|
+
for (const depSection of sectionDeps[sectionCode] || []) {
|
|
238
|
+
const depPhaseNum = orderedSections.indexOf(depSection) + 1;
|
|
239
|
+
if (depPhaseNum > 0) deps.push(depPhaseNum);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
sectionPrd._phaseInfo = { phase: phaseNum, type: 'section', sectionCode };
|
|
243
|
+
writeJSON(`.ralph/prd-${moduleCode}-section-${sectionCode}.json`, sectionPrd);
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 5c. Orphan Entities
|
|
248
|
+
|
|
249
|
+
Entities not mapped to any section are included in Phase 0 with their services/controllers:
|
|
250
|
+
|
|
251
|
+
```javascript
|
|
252
|
+
if (orphans.length > 0) {
|
|
253
|
+
// Add orphan application/api/frontend tasks to Phase 0 PRD
|
|
254
|
+
const orphanTasks = prd.tasks.filter(t =>
|
|
255
|
+
!t.section && (
|
|
256
|
+
t.category === 'application' ||
|
|
257
|
+
t.category === 'api' ||
|
|
258
|
+
t.category === 'frontend' ||
|
|
259
|
+
t.category === 'test'
|
|
260
|
+
)
|
|
261
|
+
);
|
|
262
|
+
phase0Prd.tasks.push(...orphanTasks);
|
|
263
|
+
writeJSON(`.ralph/prd-${moduleCode}-phase0.json`, phase0Prd);
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 6. Build _sectionSplit State
|
|
270
|
+
|
|
271
|
+
```javascript
|
|
272
|
+
const phases = [
|
|
273
|
+
{
|
|
274
|
+
phase: 0,
|
|
275
|
+
type: 'foundation',
|
|
276
|
+
sectionCode: undefined,
|
|
277
|
+
entities: allEntities,
|
|
278
|
+
prdFile: `.ralph/prd-${moduleCode}-phase0.json`,
|
|
279
|
+
status: 'pending',
|
|
280
|
+
dependsOn: []
|
|
281
|
+
}
|
|
282
|
+
];
|
|
283
|
+
|
|
284
|
+
for (let i = 0; i < orderedSections.length; i++) {
|
|
285
|
+
const sectionCode = orderedSections[i];
|
|
286
|
+
const sectionEntities = [];
|
|
287
|
+
const section = prd.architecture.sections.find(s => s.code === sectionCode);
|
|
288
|
+
for (const resource of section?.resources ?? []) {
|
|
289
|
+
if (resource.entity) sectionEntities.push(resource.entity);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const deps = [0]; // Phase 0
|
|
293
|
+
for (const depSection of sectionDeps[sectionCode] || []) {
|
|
294
|
+
const depPhaseNum = orderedSections.indexOf(depSection) + 1;
|
|
295
|
+
if (depPhaseNum > 0) deps.push(depPhaseNum);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
phases.push({
|
|
299
|
+
phase: i + 1,
|
|
300
|
+
type: 'section',
|
|
301
|
+
sectionCode,
|
|
302
|
+
entities: sectionEntities,
|
|
303
|
+
prdFile: `.ralph/prd-${moduleCode}-section-${sectionCode}.json`,
|
|
304
|
+
status: 'pending',
|
|
305
|
+
dependsOn: deps
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
prd._sectionSplit = {
|
|
310
|
+
enabled: true,
|
|
311
|
+
phases,
|
|
312
|
+
currentPhase: 0,
|
|
313
|
+
entityToSection
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
writeJSON('.ralph/prd.json', prd);
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## 7. Execution (in compact-loop.md)
|
|
322
|
+
|
|
323
|
+
```javascript
|
|
324
|
+
// Find next pending phase with dependencies met
|
|
325
|
+
const nextPhase = prd._sectionSplit.phases.find(p => p.status === 'pending');
|
|
326
|
+
if (!nextPhase) {
|
|
327
|
+
// All phases done — fall through to standard completion check
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Check phase dependencies
|
|
332
|
+
const depsOk = nextPhase.dependsOn.every(depIdx =>
|
|
333
|
+
prd._sectionSplit.phases[depIdx].status === 'completed'
|
|
334
|
+
);
|
|
335
|
+
if (!depsOk) {
|
|
336
|
+
// Phase blocked — this should not happen with topological sort
|
|
337
|
+
console.warn(`Phase ${nextPhase.phase} blocked by incomplete dependencies`);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// INVOKE /apex -d {nextPhase.prdFile}
|
|
342
|
+
// apex sees a normal PRD (smaller scope) and executes normally
|
|
343
|
+
|
|
344
|
+
// After apex returns:
|
|
345
|
+
nextPhase.status = 'completed';
|
|
346
|
+
prd._sectionSplit.currentPhase = nextPhase.phase;
|
|
347
|
+
writeJSON('.ralph/prd.json', prd);
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## 8. Result Merging
|
|
353
|
+
|
|
354
|
+
After each phase apex call, merge task statuses back into the master PRD:
|
|
355
|
+
|
|
356
|
+
```javascript
|
|
357
|
+
const phasePrd = readJSON(nextPhase.prdFile);
|
|
358
|
+
|
|
359
|
+
// Map phase task statuses back to master PRD
|
|
360
|
+
for (const phaseTask of phasePrd.tasks) {
|
|
361
|
+
const masterTask = prd.tasks.find(t => t.id === phaseTask.id);
|
|
362
|
+
if (masterTask) {
|
|
363
|
+
masterTask.status = phaseTask.status;
|
|
364
|
+
masterTask.completed_at = phaseTask.completed_at;
|
|
365
|
+
masterTask.commit_hash = phaseTask.commit_hash;
|
|
366
|
+
masterTask.files_changed = phaseTask.files_changed;
|
|
367
|
+
masterTask.error = phaseTask.error;
|
|
368
|
+
masterTask.validation = phaseTask.validation;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
writeJSON('.ralph/prd.json', prd);
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## 9. Phase Failure Handling
|
|
378
|
+
|
|
379
|
+
If a phase fails (apex returns with failed tasks):
|
|
380
|
+
|
|
381
|
+
```javascript
|
|
382
|
+
if (phasePrd.tasks.some(t => t.status === 'failed')) {
|
|
383
|
+
nextPhase.status = 'failed';
|
|
384
|
+
|
|
385
|
+
// Check retries
|
|
386
|
+
const retryCount = nextPhase._retryCount || 0;
|
|
387
|
+
if (retryCount < 2) {
|
|
388
|
+
nextPhase.status = 'pending';
|
|
389
|
+
nextPhase._retryCount = retryCount + 1;
|
|
390
|
+
console.log(`Phase ${nextPhase.phase} failed — retry ${retryCount + 1}/2`);
|
|
391
|
+
|
|
392
|
+
// Reset failed tasks in phase PRD
|
|
393
|
+
for (const task of phasePrd.tasks) {
|
|
394
|
+
if (task.status === 'failed') {
|
|
395
|
+
task.status = 'pending';
|
|
396
|
+
task.error = null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
writeJSON(nextPhase.prdFile, phasePrd);
|
|
400
|
+
} else {
|
|
401
|
+
console.error(`Phase ${nextPhase.phase} failed after 2 retries — marking failed`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## 10. Cleanup (in step-05-report.md)
|
|
409
|
+
|
|
410
|
+
After all phases complete or on final report:
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
if (prd._sectionSplit?.enabled) {
|
|
414
|
+
// Delete temporary phase PRD files
|
|
415
|
+
for (const phase of prd._sectionSplit.phases) {
|
|
416
|
+
if (fileExists(phase.prdFile) && phase.prdFile !== '.ralph/prd.json') {
|
|
417
|
+
deleteFile(phase.prdFile);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
// Remove split state from master PRD
|
|
421
|
+
delete prd._sectionSplit;
|
|
422
|
+
writeJSON('.ralph/prd.json', prd);
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Summary Table
|
|
429
|
+
|
|
430
|
+
| Phase | Content | Depends On | Entities |
|
|
431
|
+
|-------|---------|------------|----------|
|
|
432
|
+
| Phase 0 | ALL domain + infrastructure + migration + module seed data | — | ALL |
|
|
433
|
+
| Phase 1 | Section A: services, controllers, seed, pages, tests | Phase 0 | Section A entities |
|
|
434
|
+
| Phase 2 | Section B: idem | Phase 0 (+ Phase 1 if FK) | Section B entities |
|
|
435
|
+
| Phase N | Section N: idem | Phase 0 (+ earlier phases if FK) | Section N entities |
|
|
436
|
+
| Final | Cross-validation (dotnet build, typecheck, MCP) | All phases | — |
|
|
437
|
+
|
|
438
|
+
**Threshold:** `> 4 domain tasks` AND `> 1 section`
|
|
439
|
+
**Backward compatible:** `<= 4 domain tasks` OR `1 section` → no splitting, zero impact
|
|
@@ -82,29 +82,28 @@ for (const layer of layers) {
|
|
|
82
82
|
|
|
83
83
|
## 4. Teammate Prompt Template
|
|
84
84
|
|
|
85
|
-
Each teammate receives a self-contained prompt
|
|
85
|
+
Each teammate receives a self-contained prompt that delegates to `/apex`:
|
|
86
86
|
|
|
87
87
|
```
|
|
88
88
|
You are a Ralph Loop module worker for module "${moduleCode}".
|
|
89
89
|
|
|
90
90
|
## Your Mission
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
Execute ALL tasks in the PRD by delegating to /apex.
|
|
92
|
+
You are an ORCHESTRATOR — you NEVER generate code directly.
|
|
93
93
|
|
|
94
|
-
## PRD
|
|
95
|
-
${
|
|
94
|
+
## PRD File
|
|
95
|
+
${prdFile}
|
|
96
96
|
|
|
97
|
-
## Execution
|
|
98
|
-
1.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
7. Commit after each batch: git add + git commit
|
|
97
|
+
## Execution
|
|
98
|
+
1. INVOKE `/apex -d ${prdFile}`
|
|
99
|
+
→ Apex handles all layers: domain → infrastructure → migration → seed data → application → api → frontend → tests
|
|
100
|
+
→ Apex runs POST-CHECKs, MCP validation, build verification, and commits per layer
|
|
101
|
+
→ Apex updates task statuses in the PRD file directly
|
|
102
|
+
2. After apex returns, re-read ${prdFile} to check task statuses
|
|
103
|
+
3. If tasks remain pending/failed, re-invoke `/apex -d ${prdFile}`
|
|
105
104
|
|
|
106
105
|
## Communication Protocol
|
|
107
|
-
- After
|
|
106
|
+
- After apex completes and build passes:
|
|
108
107
|
SendMessage({ type: "message", recipient: "team-lead", content: "LAYER_READY:${moduleCode}", summary: "${moduleCode} foundation ready" })
|
|
109
108
|
- When ALL tasks complete:
|
|
110
109
|
SendMessage({ type: "message", recipient: "team-lead", content: "MODULE_COMPLETE:${moduleCode}", summary: "${moduleCode} complete" })
|
|
@@ -261,6 +261,33 @@ if (missingCategories.length > 0) {
|
|
|
261
261
|
|
|
262
262
|
**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.
|
|
263
263
|
|
|
264
|
+
### 4c. Section Split Detection (Large Module)
|
|
265
|
+
|
|
266
|
+
> **Purpose:** Modules with >4 domain entities overwhelm a single `/apex -d` call.
|
|
267
|
+
> Split execution into Phase 0 (foundation) + Phase 1..N (per section).
|
|
268
|
+
|
|
269
|
+
```javascript
|
|
270
|
+
const domainTasks = prd.tasks.filter(t => t.category === 'domain');
|
|
271
|
+
const sections = prd.architecture?.sections ?? [];
|
|
272
|
+
|
|
273
|
+
if (domainTasks.length > 4 && sections.length > 1 && !prd._sectionSplit?.enabled) {
|
|
274
|
+
console.log(`SECTION SPLIT: ${domainTasks.length} domain tasks, ${sections.length} sections → activating`);
|
|
275
|
+
|
|
276
|
+
// Read references/section-splitting.md for full logic
|
|
277
|
+
// 1. Build entity-to-section mapping from architecture.sections[].resources[].entity
|
|
278
|
+
// 2. Build section dependency graph from architecture.entities[].relationships[]
|
|
279
|
+
// 3. Topological sort sections by FK dependencies
|
|
280
|
+
// 4. Create Phase 0 PRD (all domain + infrastructure + module seed data)
|
|
281
|
+
// 5. Create Section PRDs (per-section: application + api + seedData + frontend + i18n + test)
|
|
282
|
+
// 6. Set prd._sectionSplit state
|
|
283
|
+
|
|
284
|
+
// LOAD references/section-splitting.md — execute sections 2-6
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Activation threshold:** `> 4 domain tasks` AND `> 1 architecture section`
|
|
289
|
+
**No-op when:** `<= 4 domain tasks` OR `<= 1 section` OR `_sectionSplit.enabled` already set
|
|
290
|
+
|
|
264
291
|
## 5. Find Current Task
|
|
265
292
|
|
|
266
293
|
```javascript
|