@atlashub/smartstack-cli 3.34.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.
Files changed (33) hide show
  1. package/.documentation/init.html +409 -0
  2. package/dist/index.js +32 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/mcp-entry.mjs +7 -24
  5. package/dist/mcp-entry.mjs.map +1 -1
  6. package/package.json +1 -2
  7. package/templates/mcp-scaffolding/controller.cs.hbs +5 -1
  8. package/templates/skills/apex/SKILL.md +3 -3
  9. package/templates/skills/apex/references/post-checks.md +225 -0
  10. package/templates/skills/apex/references/smartstack-api.md +29 -1
  11. package/templates/skills/apex/references/smartstack-frontend.md +27 -0
  12. package/templates/skills/apex/references/smartstack-layers.md +18 -2
  13. package/templates/skills/apex/steps/step-00-init.md +73 -0
  14. package/templates/skills/apex/steps/step-01-analyze.md +21 -0
  15. package/templates/skills/apex/steps/step-03-execute.md +72 -5
  16. package/templates/skills/apex/steps/step-04-examine.md +7 -1
  17. package/templates/skills/business-analyse/SKILL.md +4 -3
  18. package/templates/skills/business-analyse/_shared.md +9 -0
  19. package/templates/skills/business-analyse/schemas/application-schema.json +13 -0
  20. package/templates/skills/business-analyse/steps/step-00-init.md +190 -34
  21. package/templates/skills/business-analyse/steps/step-01-cadrage.md +129 -10
  22. package/templates/skills/business-analyse/steps/step-01b-applications.md +184 -13
  23. package/templates/skills/business-analyse/steps/step-03c-compile.md +14 -2
  24. package/templates/skills/business-analyse/steps/step-03d-validate.md +5 -1
  25. package/templates/skills/ralph-loop/SKILL.md +5 -0
  26. package/templates/skills/ralph-loop/references/category-rules.md +29 -0
  27. package/templates/skills/ralph-loop/references/compact-loop.md +85 -2
  28. package/templates/skills/ralph-loop/references/section-splitting.md +439 -0
  29. package/templates/skills/ralph-loop/steps/step-01-task.md +27 -0
  30. package/templates/skills/ralph-loop/steps/step-02-execute.md +45 -1
  31. package/templates/skills/ralph-loop/steps/step-05-report.md +19 -0
  32. package/scripts/health-check.sh +0 -168
  33. 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
@@ -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
@@ -52,13 +52,57 @@ writeJSON('.ralph/prd.json', prd);
52
52
 
53
53
  ### 3. Delegate to /apex
54
54
 
55
+ #### 3a. Section-Split Mode
56
+
57
+ ```javascript
58
+ if (prd._sectionSplit?.enabled) {
59
+ // Find next pending phase with dependencies met
60
+ const nextPhase = prd._sectionSplit.phases.find(p => p.status === 'pending');
61
+
62
+ if (!nextPhase) {
63
+ // All phases done — skip to step-04 completion check
64
+ goto STEP_04;
65
+ }
66
+
67
+ const depsOk = nextPhase.dependsOn.every(depIdx =>
68
+ prd._sectionSplit.phases[depIdx].status === 'completed'
69
+ );
70
+
71
+ if (!depsOk) {
72
+ console.warn(`Phase ${nextPhase.phase} blocked by incomplete dependencies`);
73
+ goto STEP_04;
74
+ }
75
+
76
+ const phaseLabel = nextPhase.type === 'foundation'
77
+ ? 'Phase 0: Foundation (all entities + migration + module seed data)'
78
+ : `Phase ${nextPhase.phase}: Section "${nextPhase.sectionCode}" (services + controllers + pages + tests)`;
79
+ console.log(`SECTION SPLIT: ${phaseLabel}`);
80
+
81
+ // INVOKE /apex -d {nextPhase.prdFile}
82
+ // Apex sees a normal (smaller) PRD and executes normally
83
+
84
+ // After apex returns: merge results into master PRD
85
+ // Read references/section-splitting.md sections 7-9 (execution, merging, failure handling)
86
+
87
+ nextPhase.status = 'completed';
88
+ prd._sectionSplit.currentPhase = nextPhase.phase;
89
+ writeJSON('.ralph/prd.json', prd);
90
+
91
+ // Continue to step-03 (commit), then step-04 loops back for next phase
92
+ }
93
+ ```
94
+
95
+ > See `references/section-splitting.md` for full detection, mapping, PRD construction, and merge logic.
96
+
97
+ #### 3b. Standard Mode (no split)
98
+
55
99
  **INVOKE `/apex -d .ralph/prd.json`**
56
100
 
57
101
  This delegates ALL remaining tasks for the current module to apex:
58
102
 
59
103
  - Apex reads the PRD, extracts context (`context_code`, `app_name`, `module_code`, `entities`, `sections`)
60
104
  - Apex executes ALL layers: domain → infrastructure → migration → application → api → seed data → frontend → tests
61
- - Apex runs full POST-CHECKs (43 checks from `references/post-checks.md`)
105
+ - Apex runs full POST-CHECKs (50 checks from `references/post-checks.md`)
62
106
  - Apex commits per layer (atomic commits)
63
107
  - Apex validates with MCP (`validate_conventions`)
64
108
  - Apex updates task statuses in the PRD file directly
@@ -97,6 +97,15 @@ Write to `.ralph/reports/{feature-slug}.md`:
97
97
  **{completedModules}/{totalModules} modules completed**
98
98
  {end if}
99
99
 
100
+ {if prd._sectionSplit?.enabled:}
101
+ ## Section Split Execution
102
+ | Phase | Type | Section | Entities | Status |
103
+ |-------|------|---------|----------|--------|
104
+ {for each phase:}
105
+ | {phase} | {type} | {sectionCode || '-'} | {entities.length} | {status} |
106
+
107
+ {end if}
108
+
100
109
  ## Test Metrics
101
110
  | Metric | Value |
102
111
  |--------|-------|
@@ -121,6 +130,16 @@ Write to `.ralph/reports/{feature-slug}.md`:
121
130
  ## 3. Finalize
122
131
 
123
132
  ```javascript
133
+ // Clean up section-split temporary files
134
+ if (prd._sectionSplit?.enabled) {
135
+ for (const phase of prd._sectionSplit.phases) {
136
+ if (fileExists(phase.prdFile) && phase.prdFile !== '.ralph/prd.json') {
137
+ deleteFile(phase.prdFile);
138
+ }
139
+ }
140
+ delete prd._sectionSplit;
141
+ }
142
+
124
143
  prd.updated_at = new Date().toISOString();
125
144
  writeJSON('.ralph/prd.json', prd);
126
145
  ```