@atlashub/smartstack-cli 3.34.0 → 3.36.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 (39) hide show
  1. package/.documentation/init.html +409 -0
  2. package/dist/index.js +35 -3
  3. package/dist/index.js.map +1 -1
  4. package/dist/mcp-entry.mjs +118 -70
  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 +6 -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 +77 -1
  14. package/templates/skills/apex/steps/step-01-analyze.md +21 -0
  15. package/templates/skills/apex/steps/step-03-execute.md +94 -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/documentation/SKILL.md +175 -9
  26. package/templates/skills/efcore/steps/squash/step-03-create.md +6 -4
  27. package/templates/skills/gitflow/_shared.md +3 -1
  28. package/templates/skills/gitflow/steps/step-pr.md +34 -0
  29. package/templates/skills/ralph-loop/SKILL.md +31 -2
  30. package/templates/skills/ralph-loop/references/category-rules.md +29 -0
  31. package/templates/skills/ralph-loop/references/compact-loop.md +85 -2
  32. package/templates/skills/ralph-loop/references/section-splitting.md +439 -0
  33. package/templates/skills/ralph-loop/references/team-orchestration.md +331 -14
  34. package/templates/skills/ralph-loop/steps/step-00-init.md +4 -0
  35. package/templates/skills/ralph-loop/steps/step-01-task.md +27 -0
  36. package/templates/skills/ralph-loop/steps/step-02-execute.md +206 -1
  37. package/templates/skills/ralph-loop/steps/step-05-report.md +19 -0
  38. package/scripts/health-check.sh +0 -168
  39. 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