@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
@@ -1,34 +1,201 @@
1
1
  ---
2
2
  name: step-01b-applications
3
- description: Application decomposition - identify and define multiple applications within a project
3
+ description: Application identity confirmation (single-app) or full application decomposition (multi-app)
4
4
  model: opus
5
5
  next_step: steps/step-02-decomposition.md
6
6
  ---
7
7
 
8
8
  > **Context files:** `_shared.md`
9
9
 
10
- # Step 1b: Application Decomposition
10
+ # Step 1b: Application Identity & Decomposition
11
11
 
12
12
  ## MANDATORY EXECUTION RULES
13
13
 
14
14
  - ALWAYS use ULTRATHINK mode
15
- - This step is ONLY loaded when `workflow.mode === "project"` (multi-application mode)
16
15
  - ALL communication in `{language}`
17
- - This step takes the candidate applications identified during cadrage and formalizes them
16
+ - This step ALWAYS runs. It has two modes:
17
+ - **Single-app mode** (`workflow.mode !== "project"`): Lightweight application identity confirmation — name, code, context, route, icon, table prefix validation, application roles recap. Creates the `seedDataCore.navigationApplications` entry.
18
+ - **Multi-app mode** (`workflow.mode === "project"`): Full application decomposition as defined in sections 1-7 below.
18
19
 
19
- ## YOUR TASK
20
+ ---
20
21
 
21
- For each candidate application identified during step-01 cadrage, define its identity, context, roles, and scope. Establish inter-application dependencies and create per-application feature.json files.
22
+ ## MODE GATE
23
+
24
+ ```
25
+ IF workflow.mode === "project":
26
+ → Execute FULL multi-app flow (sections 1-7 below)
27
+ ELSE:
28
+ → Execute LIGHTWEIGHT single-app identity (sections 0a-0f below)
29
+ ```
22
30
 
23
31
  ---
24
32
 
33
+ ## SINGLE-APP PATH (sections 0a-0f)
34
+
35
+ > This path runs when `workflow.mode !== "project"` (single application, most common case).
36
+ > It confirms the application-level identity (Level 2 of the navigation hierarchy) before proceeding to module decomposition.
37
+
38
+ ### 0a. Read Application Context
39
+
40
+ ```
41
+ appFeature = ba-reader.findFeature({feature_id})
42
+ → Read metadata: application, context, tablePrefix
43
+ → Read cadrage: applicationRoles, coverageMatrix, globalScope
44
+ → Read cadrage.codebaseContext (if exists)
45
+ ```
46
+
47
+ ### 0b. Derive Application Identity
48
+
49
+ From the cadrage data, derive the formal application identity:
50
+
51
+ ```javascript
52
+ applicationCode = toPascalCase(metadata.application) // e.g., "HumanResources"
53
+ applicationLabel = metadata.application // e.g., "Ressources Humaines" (human-readable)
54
+ context = metadata.context // "business" (validated in step-00)
55
+ applicationRoute = `/${context}/${toKebabCase(applicationCode)}` // e.g., "/business/human-resources"
56
+ applicationIcon = suggestIconFromDomain(applicationCode) // e.g., "users" for HR, "shopping-cart" for Sales
57
+ tablePrefix = metadata.tablePrefix // e.g., "rh_" (already defined in step-01)
58
+ sort = 1 // First (and only) application
59
+ ```
60
+
61
+ **Icon suggestion heuristics:**
62
+ | Domain pattern | Suggested icon |
63
+ |---------------|---------------|
64
+ | HR, Employee, Staff, Personnel | `users` |
65
+ | Sales, Commerce, Revenue | `shopping-cart` |
66
+ | Finance, Accounting, Budget | `wallet` |
67
+ | Inventory, Stock, Warehouse | `package` |
68
+ | CRM, Customer, Client | `contact` |
69
+ | Project, Task, Planning | `clipboard-list` |
70
+ | Support, Ticket, Help | `headphones` |
71
+ | Reporting, Analytics, Dashboard | `bar-chart-2` |
72
+ | Other | `layout-grid` |
73
+
74
+ ### 0c. Confirm Application Identity (BLOCKING)
75
+
76
+ Display the application identity:
77
+
78
+ ```
79
+ {language == "fr"
80
+ ? "## Identité de l'application\n\nVoici l'identité de votre application dans la hiérarchie de navigation SmartStack :"
81
+ : "## Application Identity\n\nHere is your application's identity in the SmartStack navigation hierarchy:"}
82
+
83
+ | {language == "fr" ? "Niveau" : "Level"} | {language == "fr" ? "Valeur" : "Value"} |
84
+ |-------|-------|
85
+ | Context | `{context}` |
86
+ | Application Code | `{applicationCode}` |
87
+ | Application Name | `{applicationLabel}` |
88
+ | Route | `{applicationRoute}` |
89
+ | Icon | `{applicationIcon}` |
90
+ | Table Prefix | `{tablePrefix}` |
91
+ | Sort Order | `{sort}` |
92
+
93
+ {language == "fr"
94
+ ? "### Rôles applicatifs\n\nCes rôles seront hérités par tous les modules :"
95
+ : "### Application Roles\n\nThese roles will be inherited by all modules:"}
96
+
97
+ | Role | Level | Permission Pattern |
98
+ |------|-------|--------------------|
99
+ {for each role in cadrage.applicationRoles: role.role | role.level | {context}.{kebab(applicationCode)}.* }
100
+ ```
101
+
102
+ Ask via AskUserQuestion:
103
+ ```
104
+ question: "{language == 'fr' ? 'Cette identité d\'application est-elle correcte ?' : 'Is this application identity correct?'}"
105
+ header: "Application"
106
+ options:
107
+ - label: "{language == 'fr' ? 'Oui, parfait' : 'Yes, perfect'}"
108
+ description: "{language == 'fr' ? 'Code, route, icône et rôles sont corrects' : 'Code, route, icon and roles are correct'}"
109
+ - label: "{language == 'fr' ? 'Modifier' : 'Modify'}"
110
+ description: "{language == 'fr' ? 'Changer le code, la route, l\'icône ou les rôles' : 'Change code, route, icon or roles'}"
111
+ ```
112
+
113
+ **IF "Modify":**
114
+ → Ask which field(s) to change via follow-up AskUserQuestion
115
+ → Apply changes
116
+ → Re-display and re-confirm (loop until validated)
117
+
118
+ ### 0d. Build Application SeedData Entries
119
+
120
+ Create the `navigationApplications` entry (Level 2 hierarchy entry):
121
+
122
+ ```json
123
+ {
124
+ "navigationApplications": [
125
+ {
126
+ "code": "{applicationCode}",
127
+ "label": "{applicationLabel}",
128
+ "icon": "{applicationIcon}",
129
+ "route": "{applicationRoute}",
130
+ "sort": 1
131
+ }
132
+ ]
133
+ }
134
+ ```
135
+
136
+ Create the `applicationRoles` entries (from cadrage.applicationRoles):
137
+
138
+ ```json
139
+ {
140
+ "applicationRoles": [
141
+ { "code": "admin", "name": "{applicationLabel} Admin", "permissions": "*" },
142
+ { "code": "manager", "name": "{applicationLabel} Manager", "permissions": "CRU" },
143
+ { "code": "contributor", "name": "{applicationLabel} Contributor", "permissions": "CR" },
144
+ { "code": "viewer", "name": "{applicationLabel} Viewer", "permissions": "R" }
145
+ ]
146
+ }
147
+ ```
148
+
149
+ > **NOTE:** The roles map from `cadrage.applicationRoles` levels. If the cadrage defined custom roles beyond the standard 4, include them here too.
150
+
151
+ ### 0e. Write Application Identity to Feature.json
152
+
153
+ ```
154
+ ba-writer.enrichSection({
155
+ featureId: {feature_id},
156
+ section: "metadata",
157
+ data: {
158
+ applicationCode: "{applicationCode}",
159
+ applicationRoute: "{applicationRoute}",
160
+ applicationIcon: "{applicationIcon}"
161
+ }
162
+ })
163
+ ```
164
+
165
+ > **NOTE:** The full `seedDataCore` arrays (`navigationApplications`, `applicationRoles`) will be written in step-03c when compiling the first module's specification. Here we only persist the identity fields in metadata so they are available to step-02 and step-03.
166
+
167
+ ### 0f. Summary and Continue
168
+
169
+ ```
170
+ {language == "fr"
171
+ ? "✅ Application **{applicationCode}** identifiée\n\n- Route : `{applicationRoute}`\n- Icône : `{applicationIcon}`\n- Préfixe : `{tablePrefix}`\n- Rôles : {roleCount} rôles applicatifs définis\n\n→ Prochaine étape : décomposition en modules"
172
+ : "✅ Application **{applicationCode}** identified\n\n- Route: `{applicationRoute}`\n- Icon: `{applicationIcon}`\n- Prefix: `{tablePrefix}`\n- Roles: {roleCount} application roles defined\n\n→ Next step: module decomposition"}
173
+ ```
174
+
175
+ → Load `steps/step-02-decomposition.md`
176
+
177
+ ---
178
+
179
+ ## MULTI-APP PATH (sections 1-7)
180
+
181
+ > This path runs when `workflow.mode === "project"` (multi-application mode).
182
+ > It takes the candidate applications identified during cadrage and formalizes them.
183
+
25
184
  ### 1. Load Project Context
26
185
 
27
186
  ```
28
187
  projectFeature = ba-reader.findProjectFeature()
29
- candidateApps = projectFeature.cadrage.coverageMatrix grouped by domain
30
188
  globalRoles = projectFeature.cadrage.globalRoles (if defined)
31
189
  globalScope = projectFeature.cadrage.globalScope
190
+
191
+ // Candidate applications: PRE-IDENTIFIED from step-00 prompt analysis + enriched by step-01 cadrage
192
+ candidateApps = projectFeature.metadata.candidateApplications
193
+ // These candidates already have: name, description, modules[], context, dependencies[]
194
+ // They may also include transversal apps extracted from shared modules (step-01 section 5b)
195
+
196
+ IF candidateApps is null or empty:
197
+ // Fallback: derive from coverage matrix (legacy path)
198
+ candidateApps = projectFeature.cadrage.coverageMatrix grouped by domain
32
199
  ```
33
200
 
34
201
  ### 2. Application Identity (per candidate)
@@ -154,14 +321,14 @@ options:
154
321
  - label: "{language == 'fr' ? 'Modifier' : 'Modify'}"
155
322
  description: "{language == 'fr' ? 'Ajouter, supprimer ou modifier des applications' : 'Add, remove, or modify applications'}"
156
323
  - label: "{language == 'fr' ? 'Fusionner en une seule' : 'Merge into one'}"
157
- description: "{language == 'fr' ? 'Finalement, c'est une seule application avec plusieurs modules' : 'Actually, this is one application with multiple modules'}"
324
+ description: "{language == 'fr' ? 'Finalement, c\'est une seule application avec plusieurs modules' : 'Actually, this is one application with multiple modules'}"
158
325
  ```
159
326
 
160
327
  **IF "Merge into one":**
161
328
  → Convert back to single-application mode
162
329
  → Delete project-level feature.json
163
330
  → Create application-level feature.json with all scope items
164
- Load `steps/step-02-decomposition.md` directly
331
+ Execute single-app path (sections 0a-0f above)
165
332
 
166
333
  ### 5. Create Per-Application Feature.json Files
167
334
 
@@ -175,6 +342,9 @@ ba-writer.createApplicationFeature({
175
342
  status: "draft",
176
343
  metadata: {
177
344
  application: {app.code},
345
+ applicationCode: {toPascalCase(app.code)},
346
+ applicationRoute: {`/${app.context}/${toKebabCase(app.code)}`},
347
+ applicationIcon: {app.icon},
178
348
  context: {app.context},
179
349
  language: {language},
180
350
  featureDescription: {app.description},
@@ -208,6 +378,7 @@ ba-writer.enrichApplicationRegistry({
208
378
  context: {app1.context},
209
379
  tablePrefix: {app1.tablePrefix},
210
380
  icon: {app1.icon},
381
+ route: {app1.route},
211
382
  description: {app1.description},
212
383
  applicationRoles: [{...}],
213
384
  scope: { mustHave: [...], shouldHave: [...], couldHave: [...] },
@@ -236,9 +407,9 @@ ba-writer.updateStatus({project_id}, "decomposed")
236
407
  ? "### Décomposition en applications terminée\n\n"
237
408
  : "### Application decomposition complete\n\n"}
238
409
 
239
- | Application | Contexte | Préfixe | Feature ID |
240
- |-------------|----------|---------|------------|
241
- {for each app: name | context | prefix | FEAT-NNN}
410
+ | Application | Contexte | Préfixe | Route | Feature ID |
411
+ |-------------|----------|---------|-------|------------|
412
+ {for each app: name | context | prefix | route | FEAT-NNN}
242
413
 
243
414
  {language == "fr"
244
415
  ? "→ Prochaine étape : décomposition en modules pour **{firstApp}** (application {1}/{total})"
@@ -249,4 +420,4 @@ ba-writer.updateStatus({project_id}, "decomposed")
249
420
 
250
421
  ## NEXT STEP
251
422
 
252
- Load: `./step-02-decomposition.md` (for the first application in topological order)
423
+ Load: `./step-02-decomposition.md` (for the first application in topological order, or the single application)
@@ -301,11 +301,20 @@ for (const entry of specification.navigation.entries) {
301
301
 
302
302
  #### 8f. SeedData Core
303
303
 
304
- 7 MANDATORY typed arrays — each with structured objects, NOT flat strings or objects.
304
+ 9 MANDATORY typed arrays — each with structured objects, NOT flat strings or objects.
305
305
 
306
306
  > **STRUCTURE CARD: specification.seedDataCore**
307
307
  > ```json
308
308
  > {
309
+ > "navigationApplications": [
310
+ > { "code": "{app}", "label": "{Application Name}", "icon": "{icon}", "route": "/business/{app-kebab}", "sort": 1 }
311
+ > ],
312
+ > "applicationRoles": [
313
+ > { "code": "admin", "name": "{App} Admin", "permissions": "*" },
314
+ > { "code": "manager", "name": "{App} Manager", "permissions": "CRU" },
315
+ > { "code": "contributor", "name": "{App} Contributor", "permissions": "CR" },
316
+ > { "code": "viewer", "name": "{App} Viewer", "permissions": "R" }
317
+ > ],
309
318
  > "navigationModules": [
310
319
  > { "code": "{module}", "label": "{Module Name}", "icon": "list", "route": "/business/{app}/{module}", "parentCode": "{app}", "sort": 1 }
311
320
  > ],
@@ -339,7 +348,8 @@ for (const entry of specification.navigation.entries) {
339
348
  > ]
340
349
  > }
341
350
  > ```
342
- > **MANDATORY:** All 7 arrays must be present. Each element must be an object, NOT a string.
351
+ > **MANDATORY:** All 9 arrays must be present. Each element must be an object, NOT a string.
352
+ > **NOTE:** `navigationApplications` and `applicationRoles` are populated from the application identity confirmed in step-01b. They are written ONCE for the first module processed and remain empty `[]` for subsequent modules.
343
353
  > **CRITICAL:** `navigationSections` and `navigationResources` are DERIVED from `specification.sections[]` — use the transform algorithm below (section 8f-bis).
344
354
  > **IMPORTANT:** `create` and `edit` are NEVER sections — they are action pages reached via buttons. Do NOT include them in `navigationSections`. Only include actual sidebar sections (list, dashboard, approve, import, etc.) and hidden route sections (detail).
345
355
  > **FORBIDDEN:** Do NOT use `navigationModule` (singular string), `permissions` as flat string array, `rolePermissions` as flat object, `permissionsConstants` as comma-separated string.
@@ -388,6 +398,8 @@ ba-writer.enrichSection({
388
398
  section: "specification",
389
399
  data: {
390
400
  seedDataCore: {
401
+ navigationApplications: [ ... ], // FROM step-01b identity (only for first module, else [])
402
+ applicationRoles: [ ... ], // FROM cadrage.applicationRoles (only for first module, else [])
391
403
  navigationModules: [ ... ],
392
404
  navigationSections: navigationSections, // DERIVED
393
405
  navigationResources: navigationResources, // DERIVED
@@ -73,7 +73,7 @@ Validate the module specification for completeness and consistency, write to fea
73
73
  | messages | 4 | `specification.messages.length >= 4` | PASS/FAIL |
74
74
  | messageFormat | `message` field present (BLOCKING) | `specification.messages.every(m => m.message)` | PASS/FAIL |
75
75
  | lifeCycles | 1 (if entity has status) | `specification.lifeCycles.length >= 1` (if any entity has status/state field) | PASS/FAIL |
76
- | seedDataCore | 7 arrays present with content | See detailed check below | PASS/FAIL (BLOCKING) |
76
+ | seedDataCore | 9 arrays present with content | See detailed check below | PASS/FAIL (BLOCKING) |
77
77
  | apiEndpoints | 1 | `specification.apiEndpoints.length >= 1` | PASS/FAIL |
78
78
  | i18nKeys | present | `specification.i18nKeys !== undefined && specification.i18nKeys !== null` | PASS/FAIL |
79
79
  | navigationIcons | non-null | `specification.seedDataCore.navigationModules.every(m => m.icon !== null)` | PASS/FAIL |
@@ -81,7 +81,11 @@ Validate the module specification for completeness and consistency, write to fea
81
81
  **seedDataCore detailed check (BLOCKING):**
82
82
  ```javascript
83
83
  const sdc = specification.seedDataCore;
84
+ const currentModuleIndex = metadata.workflow?.currentModuleIndex || 0;
84
85
  const checks = [
86
+ // navigationApplications and applicationRoles: required for first module (index 0), can be empty for subsequent modules
87
+ { key: "navigationApplications", actual: sdc.navigationApplications?.length || 0, min: currentModuleIndex === 0 ? 1 : 0 },
88
+ { key: "applicationRoles", actual: sdc.applicationRoles?.length || 0, min: currentModuleIndex === 0 ? 1 : 0 },
85
89
  { key: "navigationModules", actual: sdc.navigationModules?.length || 0, min: 1 },
86
90
  { key: "navigationSections", actual: sdc.navigationSections?.length || 0, min: 1 }, // EVERY module needs ≥1 section
87
91
  { key: "navigationResources", actual: sdc.navigationResources?.length || 0, min: 1 },
@@ -79,6 +79,8 @@ LOAD → DELEGATE TO /apex -d → VERIFY PRD STATE → COMMIT PRD → NEXT MODUL
79
79
  | `{current_iteration}` | number | Current iteration |
80
80
  | `{current_module}` | string\|null | Current module (multi-module) |
81
81
  | `{modules_queue}` | object\|null | Module queue (multi-module) |
82
+ | `{section_split_mode}` | boolean | Section splitting active for current module (>4 entities + >1 section) |
83
+ | `{section_phases}` | SectionPhase[] | Phase execution plan (Phase 0: foundation, Phase 1..N: per-section) |
82
84
  </state_variables>
83
85
 
84
86
  <mcp_requirements>
@@ -141,6 +143,7 @@ When the user invokes `/ralph-loop`, they are giving you the instruction to:
141
143
  |------|-------------|
142
144
  | `references/category-rules.md` | Step-01 and compact loop (category ordering and dependency rules) |
143
145
  | `references/compact-loop.md` | Step-04 section 5 (module loop with /apex delegation) |
146
+ | `references/section-splitting.md` | Step-01 section 4c when module has >4 entities + >1 section |
144
147
  | `references/team-orchestration.md` | Step-00 when multi-module detected (2+ PRDs) |
145
148
  </step_files>
146
149
 
@@ -151,6 +154,8 @@ When the user invokes `/ralph-loop`, they are giving you the instruction to:
151
154
  ├── progress.txt # Persistent memory
152
155
  ├── modules-queue.json # Multi-module tracking (if applicable)
153
156
  ├── prd-{module}.json # Per-module PRDs (from ss derive-prd, unified v3 format)
157
+ ├── prd-{module}-phase0.json # Section split: Foundation (domain+infra+migration)
158
+ ├── prd-{module}-section-{code}.json # Section split: Per-section (app+api+seed+frontend+tests)
154
159
  ├── logs/
155
160
  └── reports/
156
161
  └── {feature}.md
@@ -65,3 +65,32 @@ A valid PRD MUST contain tasks in these categories:
65
65
  - `test` — at least 1 test task
66
66
 
67
67
  If any category is missing, ralph step-01 injects a guardrail task.
68
+
69
+ ---
70
+
71
+ ## Section-Level Splitting (>4 Entities)
72
+
73
+ When a module has many entities, a single `/apex -d` call saturates the context window.
74
+ Section-level splitting breaks the module into smaller, manageable phases.
75
+
76
+ **Activation threshold:** `> 4 domain tasks` AND `> 1 architecture section`
77
+
78
+ | Phase | Content | Depends On |
79
+ |-------|---------|------------|
80
+ | Phase 0 | ALL domain + infrastructure + migration + module seed data | — |
81
+ | Phase 1 | Section A: services, controllers, section seed, pages, i18n, tests | Phase 0 |
82
+ | Phase 2 | Section B: idem | Phase 0 (+ Phase 1 if FK cross-section) |
83
+ | Phase N | Section N: idem | Phase 0 (+ earlier phases if FK) |
84
+ | Final | Cross-validation (dotnet build, typecheck, MCP validate) | All phases |
85
+
86
+ **Key rules:**
87
+ - Phase 0 creates ALL entity classes + ALL EF configs + ONE migration (avoids ModelSnapshot conflicts)
88
+ - Section phases NEVER create entities or migrations — only services, controllers, pages on top
89
+ - FK cross-section dependencies determine section execution order (topological sort)
90
+ - Orphan entities (not in any section) are included in Phase 0 with their services/controllers
91
+ - Each phase produces a temporary PRD file: `.ralph/prd-{module}-phase0.json`, `.ralph/prd-{module}-section-{code}.json`
92
+ - Temporary PRD files are cleaned up in step-05 (report)
93
+
94
+ **Backward compatible:** Modules with `<= 4 domain tasks` or `1 section` → standard execution, zero impact.
95
+
96
+ See `references/section-splitting.md` for full detection, mapping, and execution logic.
@@ -23,10 +23,93 @@ Display compact progress:
23
23
 
24
24
  ---
25
25
 
26
- ## A. Find Eligible Tasks
26
+ ## A0. Section-Split Mode (checked BEFORE standard batching)
27
27
 
28
28
  ```javascript
29
29
  const prd = readJSON('.ralph/prd.json');
30
+
31
+ if (prd._sectionSplit?.enabled) {
32
+ // SECTION-SPLIT: Delegate per-phase instead of per-category batch
33
+
34
+ // Find next pending phase with dependencies met
35
+ const nextPhase = prd._sectionSplit.phases.find(p => p.status === 'pending');
36
+
37
+ if (!nextPhase) {
38
+ // All phases done — check if master PRD tasks are all complete
39
+ goto CHECK_COMPLETION;
40
+ }
41
+
42
+ const depsOk = nextPhase.dependsOn.every(depIdx =>
43
+ prd._sectionSplit.phases[depIdx].status === 'completed'
44
+ );
45
+
46
+ if (!depsOk) {
47
+ // Phase blocked — should not happen with topological sort
48
+ console.warn(`Phase ${nextPhase.phase} blocked — dependencies incomplete`);
49
+ goto CHECK_COMPLETION;
50
+ }
51
+
52
+ const phaseLabel = nextPhase.type === 'foundation'
53
+ ? `Phase 0: Foundation (${nextPhase.entities.length} entities + migration)`
54
+ : `Phase ${nextPhase.phase}: Section "${nextPhase.sectionCode}" (${nextPhase.entities.length} entities)`;
55
+ console.log(`[{iteration}/{max}] SECTION SPLIT: ${phaseLabel}`);
56
+
57
+ // INVOKE /apex -d {nextPhase.prdFile}
58
+ // Apex sees a normal (smaller) PRD and executes normally
59
+
60
+ // After apex returns: merge results back into master PRD
61
+ // Read references/section-splitting.md sections 7-9
62
+ const phasePrd = readJSON(nextPhase.prdFile);
63
+ for (const phaseTask of phasePrd.tasks) {
64
+ const masterTask = prd.tasks.find(t => t.id === phaseTask.id);
65
+ if (masterTask) {
66
+ masterTask.status = phaseTask.status;
67
+ masterTask.completed_at = phaseTask.completed_at;
68
+ masterTask.commit_hash = phaseTask.commit_hash;
69
+ masterTask.files_changed = phaseTask.files_changed;
70
+ masterTask.error = phaseTask.error;
71
+ masterTask.validation = phaseTask.validation;
72
+ }
73
+ }
74
+
75
+ // Handle phase failure
76
+ if (phasePrd.tasks.some(t => t.status === 'failed')) {
77
+ const retryCount = nextPhase._retryCount || 0;
78
+ if (retryCount < 2) {
79
+ nextPhase.status = 'pending';
80
+ nextPhase._retryCount = retryCount + 1;
81
+ console.log(`Phase ${nextPhase.phase} had failures — retry ${retryCount + 1}/2`);
82
+ // Reset failed tasks in phase PRD for retry
83
+ for (const task of phasePrd.tasks) {
84
+ if (task.status === 'failed') { task.status = 'pending'; task.error = null; }
85
+ }
86
+ writeJSON(nextPhase.prdFile, phasePrd);
87
+ } else {
88
+ nextPhase.status = 'failed';
89
+ console.error(`Phase ${nextPhase.phase} failed after 2 retries`);
90
+ }
91
+ } else {
92
+ nextPhase.status = 'completed';
93
+ }
94
+
95
+ prd._sectionSplit.currentPhase = nextPhase.phase;
96
+ writeJSON('.ralph/prd.json', prd);
97
+
98
+ // → Skip to section C (commit PRD state), then D (loop back)
99
+ goto COMMIT_PRD;
100
+ }
101
+ ```
102
+
103
+ > **IMPORTANT:** When `_sectionSplit.enabled`, the standard category-based batching (section A below)
104
+ > is SKIPPED. The loop delegates per-phase to apex instead of per-category-batch.
105
+
106
+ ---
107
+
108
+ ## A. Find Eligible Tasks
109
+
110
+ > **Note:** This section is SKIPPED when `prd._sectionSplit?.enabled` (handled by A0 above).
111
+
112
+ ```javascript
30
113
  const MAX_RETRIES = 3;
31
114
 
32
115
  // RETRY: Reset failed tasks to pending if retries remain (max 3 attempts per task)
@@ -115,7 +198,7 @@ writeJSON('.ralph/prd.json', prd);
115
198
  Apex handles everything for the current module:
116
199
  - Reads PRD, extracts context (context_code, app_name, module_code, entities, sections)
117
200
  - Executes ALL layers: domain → infrastructure → migration → application → api → seed data → frontend → tests
118
- - Runs full POST-CHECKs (43 checks from `references/post-checks.md`)
201
+ - Runs full POST-CHECKs (50 checks from `references/post-checks.md`)
119
202
  - Commits per layer (atomic commits)
120
203
  - Validates with MCP (`validate_conventions`)
121
204
  - Updates task statuses in the PRD file directly