@atlashub/smartstack-cli 3.32.0 → 3.34.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 (55) hide show
  1. package/.documentation/agents.html +5 -1
  2. package/.documentation/apex.html +644 -0
  3. package/.documentation/business-analyse.html +81 -1
  4. package/.documentation/cli-commands.html +5 -1
  5. package/.documentation/commands.html +5 -1
  6. package/.documentation/efcore.html +5 -1
  7. package/.documentation/gitflow.html +5 -1
  8. package/.documentation/hooks.html +5 -1
  9. package/.documentation/index.html +60 -2
  10. package/.documentation/init.html +5 -1
  11. package/.documentation/installation.html +5 -1
  12. package/.documentation/ralph-loop.html +365 -216
  13. package/.documentation/test-web.html +5 -1
  14. package/package.json +1 -1
  15. package/templates/agents/ba-writer.md +142 -15
  16. package/templates/skills/apex/SKILL.md +7 -1
  17. package/templates/skills/apex/_shared.md +49 -4
  18. package/templates/skills/{ralph-loop → apex}/references/core-seed-data.md +20 -11
  19. package/templates/skills/{ralph-loop → apex}/references/error-classification.md +2 -1
  20. package/templates/skills/apex/references/post-checks.md +238 -3
  21. package/templates/skills/apex/references/smartstack-api.md +47 -7
  22. package/templates/skills/apex/references/smartstack-frontend.md +47 -1
  23. package/templates/skills/apex/references/smartstack-layers.md +3 -1
  24. package/templates/skills/apex/steps/step-00-init.md +48 -1
  25. package/templates/skills/apex/steps/step-01-analyze.md +37 -0
  26. package/templates/skills/apex/steps/step-02-plan.md +36 -0
  27. package/templates/skills/apex/steps/step-03-execute.md +42 -2
  28. package/templates/skills/apex/steps/step-04-examine.md +110 -2
  29. package/templates/skills/business-analyse/SKILL.md +29 -19
  30. package/templates/skills/business-analyse/_module-loop.md +68 -9
  31. package/templates/skills/business-analyse/_shared.md +71 -21
  32. package/templates/skills/business-analyse/questionnaire/00-application.md +4 -2
  33. package/templates/skills/business-analyse/questionnaire/00b-project.md +85 -0
  34. package/templates/skills/business-analyse/references/deploy-modes.md +69 -0
  35. package/templates/skills/business-analyse/references/team-orchestration.md +158 -7
  36. package/templates/skills/business-analyse/schemas/application-schema.json +2 -1
  37. package/templates/skills/business-analyse/schemas/project-schema.json +490 -0
  38. package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +2 -1
  39. package/templates/skills/business-analyse/steps/step-00-init.md +30 -4
  40. package/templates/skills/business-analyse/steps/step-01-cadrage.md +62 -2
  41. package/templates/skills/business-analyse/steps/step-01b-applications.md +252 -0
  42. package/templates/skills/business-analyse/steps/step-02-decomposition.md +23 -6
  43. package/templates/skills/business-analyse/steps/step-03d-validate.md +27 -6
  44. package/templates/skills/business-analyse/steps/step-04a-collect.md +111 -0
  45. package/templates/skills/business-analyse/steps/step-05a-handoff.md +296 -103
  46. package/templates/skills/business-analyse/steps/step-05b-deploy.md +46 -14
  47. package/templates/skills/documentation/SKILL.md +92 -2
  48. package/templates/skills/ralph-loop/SKILL.md +9 -17
  49. package/templates/skills/ralph-loop/references/category-rules.md +43 -692
  50. package/templates/skills/ralph-loop/references/compact-loop.md +104 -427
  51. package/templates/skills/ralph-loop/references/team-orchestration.md +13 -14
  52. package/templates/skills/ralph-loop/steps/step-02-execute.md +49 -704
  53. package/templates/skills/ralph-loop/steps/step-03-commit.md +38 -79
  54. package/templates/skills/ralph-loop/steps/step-04-check.md +39 -58
  55. package/templates/skills/ralph-loop/steps/step-05-report.md +12 -123
@@ -0,0 +1,252 @@
1
+ ---
2
+ name: step-01b-applications
3
+ description: Application decomposition - identify and define multiple applications within a project
4
+ model: opus
5
+ next_step: steps/step-02-decomposition.md
6
+ ---
7
+
8
+ > **Context files:** `_shared.md`
9
+
10
+ # Step 1b: Application Decomposition
11
+
12
+ ## MANDATORY EXECUTION RULES
13
+
14
+ - ALWAYS use ULTRATHINK mode
15
+ - This step is ONLY loaded when `workflow.mode === "project"` (multi-application mode)
16
+ - ALL communication in `{language}`
17
+ - This step takes the candidate applications identified during cadrage and formalizes them
18
+
19
+ ## YOUR TASK
20
+
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
+
23
+ ---
24
+
25
+ ### 1. Load Project Context
26
+
27
+ ```
28
+ projectFeature = ba-reader.findProjectFeature()
29
+ candidateApps = projectFeature.cadrage.coverageMatrix grouped by domain
30
+ globalRoles = projectFeature.cadrage.globalRoles (if defined)
31
+ globalScope = projectFeature.cadrage.globalScope
32
+ ```
33
+
34
+ ### 2. Application Identity (per candidate)
35
+
36
+ For EACH candidate application from the cadrage analysis:
37
+
38
+ #### 2a. Confirm Application Name and Context
39
+
40
+ Display proposed application:
41
+ ```
42
+ {language == "fr"
43
+ ? "### Application : {suggestedName}\n\n**Contexte de navigation :** {suggestedContext}\n**Description :** {description based on coverage matrix items}"
44
+ : "### Application: {suggestedName}\n\n**Navigation context:** {suggestedContext}\n**Description:** {description based on coverage matrix items}"}
45
+ ```
46
+
47
+ Ask via AskUserQuestion:
48
+ ```
49
+ question: "{language == 'fr' ? 'Cette application et son contexte vous conviennent-ils ?' : 'Does this application and its context suit you?'}"
50
+ header: "{appName}"
51
+ options:
52
+ - label: "{language == 'fr' ? 'Oui, parfait' : 'Yes, perfect'}"
53
+ description: "{language == 'fr' ? 'Nom et contexte corrects' : 'Name and context are correct'}"
54
+ - label: "{language == 'fr' ? 'Modifier' : 'Modify'}"
55
+ description: "{language == 'fr' ? 'Changer le nom, le contexte ou la description' : 'Change name, context, or description'}"
56
+ ```
57
+
58
+ #### 2b. Table Prefix (per application)
59
+
60
+ Each application MUST have its own unique table prefix.
61
+
62
+ 1. Derive suggested prefix from application name (2-5 lowercase letters + `_`)
63
+ 2. Validate:
64
+ - Format: `^[a-z]{2,5}_$`
65
+ - No collision with platform prefixes: `auth_`, `nav_`, `usr_`, `ai_`, `cfg_`, `wkf_`, `support_`, `entra_`, `ref_`, `loc_`, `lic_`, `tenant_`
66
+ - No collision with other applications in this project
67
+ 3. Ask via AskUserQuestion for confirmation
68
+
69
+ #### 2c. Application-Specific Roles
70
+
71
+ Based on global roles from cadrage + application-specific needs:
72
+
73
+ ```
74
+ {language == "fr"
75
+ ? "### Rôles pour {appName}\n\nVoici les rôles hérités des rôles globaux, adaptés à cette application :"
76
+ : "### Roles for {appName}\n\nHere are the roles inherited from global roles, adapted for this application:"}
77
+
78
+ | Role | Level | Permission Pattern |
79
+ |------|-------|--------------------|
80
+ {for each role: name | admin/manager/contributor/viewer | context.app.* pattern}
81
+ ```
82
+
83
+ Ask via AskUserQuestion if additional roles are needed for this specific application.
84
+
85
+ #### 2d. Application Scope
86
+
87
+ Extract the subset of global scope that applies to this application:
88
+
89
+ ```
90
+ applicationScope = {
91
+ mustHave: globalScope.mustHave.filter(item => belongsToApp(item, appCode)),
92
+ shouldHave: globalScope.shouldHave.filter(item => belongsToApp(item, appCode)),
93
+ couldHave: globalScope.couldHave.filter(item => belongsToApp(item, appCode))
94
+ }
95
+ ```
96
+
97
+ Display and confirm with user.
98
+
99
+ ### 3. Inter-Application Dependency Analysis
100
+
101
+ After all applications are defined:
102
+
103
+ 1. **Identify dependencies:** For each pair of applications:
104
+ - Shared entities (e.g., Employee defined in HR, referenced by Self-Service)
105
+ - Event flows (e.g., HR publishes "EmployeeCreated", Self-Service subscribes)
106
+ - Authentication delegation (e.g., Self-Service uses HR's employee data for auth)
107
+ - Data dependencies (e.g., Self-Service reads HR data)
108
+
109
+ 2. **Build dependency graph:**
110
+ ```
111
+ applicationDependencyGraph = {
112
+ edges: [
113
+ { from: "EmployeeSelfService", to: "HumanResources", type: "data-dependency", description: "Reads employee data" }
114
+ ],
115
+ topologicalOrder: ["HumanResources", "EmployeeSelfService"],
116
+ layers: [
117
+ { layer: 0, applications: ["HumanResources"] },
118
+ { layer: 1, applications: ["EmployeeSelfService"] }
119
+ ]
120
+ }
121
+ ```
122
+
123
+ 3. **Cycle detection:** If circular dependencies detected → BLOCKING ERROR
124
+ - Display the cycle
125
+ - Ask user to resolve (merge applications or remove dependency)
126
+
127
+ ### 4. Client Checkpoint (BLOCKING)
128
+
129
+ Display the full application structure:
130
+
131
+ ```
132
+ {language == "fr"
133
+ ? "### Structure du projet\n\nVoici les applications identifiées :"
134
+ : "### Project Structure\n\nHere are the identified applications:"}
135
+
136
+ | # | Application | Contexte | Préfixe | Rôles | Dépendances |
137
+ |---|-------------|----------|---------|-------|-------------|
138
+ {for each app: index | name | context | prefix | role count | dependency list}
139
+
140
+ {language == "fr"
141
+ ? "### Ordre de traitement\n\nLes applications seront traitées dans cet ordre (fondations d'abord) :"
142
+ : "### Processing Order\n\nApplications will be processed in this order (foundations first):"}
143
+
144
+ {for each layer: "Layer {n}: {app1}, {app2}, ..."}
145
+ ```
146
+
147
+ Ask via AskUserQuestion:
148
+ ```
149
+ question: "{language == 'fr' ? 'Cette structure de projet vous convient-elle ?' : 'Does this project structure suit you?'}"
150
+ header: "Validation"
151
+ options:
152
+ - label: "{language == 'fr' ? 'Oui, continuer' : 'Yes, continue'}"
153
+ description: "{language == 'fr' ? 'Passer à la décomposition en modules' : 'Proceed to module decomposition'}"
154
+ - label: "{language == 'fr' ? 'Modifier' : 'Modify'}"
155
+ description: "{language == 'fr' ? 'Ajouter, supprimer ou modifier des applications' : 'Add, remove, or modify applications'}"
156
+ - 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'}"
158
+ ```
159
+
160
+ **IF "Merge into one":**
161
+ → Convert back to single-application mode
162
+ → Delete project-level feature.json
163
+ → Create application-level feature.json with all scope items
164
+ → Load `steps/step-02-decomposition.md` directly
165
+
166
+ ### 5. Create Per-Application Feature.json Files
167
+
168
+ For EACH application in topological order:
169
+
170
+ ```
171
+ ba-writer.createApplicationFeature({
172
+ id: generate_feature_id(),
173
+ version: "1.0",
174
+ scope: "application",
175
+ status: "draft",
176
+ metadata: {
177
+ application: {app.code},
178
+ context: {app.context},
179
+ language: {language},
180
+ featureDescription: {app.description},
181
+ workflowType: "new",
182
+ analysisMode: "interactive",
183
+ mcpAvailable: true,
184
+ tablePrefix: {app.tablePrefix},
185
+ projectRef: {project_id},
186
+ workflow: {
187
+ mode: "application",
188
+ moduleOrder: [],
189
+ currentModuleIndex: 0,
190
+ completedModules: [],
191
+ currentModule: null
192
+ }
193
+ }
194
+ })
195
+ ```
196
+
197
+ Store the created feature.json path in the project feature.json `applications[].featureJsonPath`.
198
+
199
+ ### 6. Write to Project Feature.json
200
+
201
+ ```
202
+ ba-writer.enrichApplicationRegistry({
203
+ projectId: {project_id},
204
+ applications: [
205
+ {
206
+ code: {app1.code},
207
+ name: {app1.name},
208
+ context: {app1.context},
209
+ tablePrefix: {app1.tablePrefix},
210
+ icon: {app1.icon},
211
+ description: {app1.description},
212
+ applicationRoles: [{...}],
213
+ scope: { mustHave: [...], shouldHave: [...], couldHave: [...] },
214
+ modules: [],
215
+ dependencies: [{...}],
216
+ status: "pending",
217
+ featureJsonPath: {app1.featureJsonPath},
218
+ estimatedComplexity: {app1.estimatedComplexity}
219
+ },
220
+ // ... more applications
221
+ ],
222
+ applicationDependencyGraph: {
223
+ edges: [...],
224
+ topologicalOrder: [...],
225
+ layers: [...]
226
+ }
227
+ })
228
+
229
+ ba-writer.updateStatus({project_id}, "decomposed")
230
+ ```
231
+
232
+ ### 7. Display Summary
233
+
234
+ ```
235
+ {language == "fr"
236
+ ? "### Décomposition en applications terminée\n\n"
237
+ : "### Application decomposition complete\n\n"}
238
+
239
+ | Application | Contexte | Préfixe | Feature ID |
240
+ |-------------|----------|---------|------------|
241
+ {for each app: name | context | prefix | FEAT-NNN}
242
+
243
+ {language == "fr"
244
+ ? "→ Prochaine étape : décomposition en modules pour **{firstApp}** (application {1}/{total})"
245
+ : "→ Next step: module decomposition for **{firstApp}** (application {1}/{total})"}
246
+ ```
247
+
248
+ ---
249
+
250
+ ## NEXT STEP
251
+
252
+ Load: `./step-02-decomposition.md` (for the first application in topological order)
@@ -28,12 +28,27 @@ Decompose the application scope into ordered modules. Identify dependencies betw
28
28
  ### 1. Read Cadrage Results
29
29
 
30
30
  ```
31
- ba-reader.findFeature({feature_id})
32
- Read cadrage.globalScope (mustHave, shouldHave, couldHave)
33
- Read cadrage.stakeholders (roles, tasks, pain points)
34
- Read cadrage.applicationRoles
35
- Read cadrage.codebaseContext (existing modules found)
36
- Read metadata.workflow.mode
31
+ // Determine context source based on workflow mode
32
+ IF workflow.mode === "project":
33
+ projectFeature = ba-reader.findProjectFeature()
34
+ currentAppCode = projectFeature.metadata.workflow.currentApplication
35
+ currentApp = projectFeature.applications.find(a => a.code === currentAppCode)
36
+ appFeature = ba-reader.findFeature(currentApp.featureJsonPath)
37
+
38
+ // Scope decomposition to current application only
39
+ → Read appFeature.cadrage.globalScope (application-specific scope from step-01b)
40
+ → Read appFeature.cadrage.stakeholders (filtered to this application)
41
+ → Read appFeature.cadrage.applicationRoles
42
+ → Read appFeature.metadata.workflow.mode
43
+
44
+ Display: "═══ Module decomposition for application: {currentApp.name} ({currentAppIndex + 1}/{total}) ═══"
45
+ ELSE:
46
+ ba-reader.findFeature({feature_id})
47
+ → Read cadrage.globalScope (mustHave, shouldHave, couldHave)
48
+ → Read cadrage.stakeholders (roles, tasks, pain points)
49
+ → Read cadrage.applicationRoles
50
+ → Read cadrage.codebaseContext (existing modules found)
51
+ → Read metadata.workflow.mode
37
52
  ```
38
53
 
39
54
  IF status already "decomposed":
@@ -368,3 +383,5 @@ When `workflow.mode = "module"` or only 1 scope item identified:
368
383
 
369
384
  > **FORBIDDEN:** Asking any question between decomposition summary and step-03 load.
370
385
  > The client checkpoint was at step 6. Do NOT add another one.
386
+
387
+ > **PROJECT MODE NOTE:** In project mode, this step runs once per application. After all modules for the current application are specified (via the module loop in step-03d), the application loop in `_module-loop.md` will advance to the next application and re-load this step for module decomposition of the next application. Module registries and dependency graphs are written to the APPLICATION-level feature.json (not the project level). The project-level feature.json tracks application status via `ba-writer.updateApplicationStatus()`.
@@ -548,8 +548,6 @@ IF currentModuleIndex < moduleOrder.length:
548
548
  Load: steps/step-03a1-setup.md
549
549
 
550
550
  IF currentModuleIndex >= moduleOrder.length:
551
- Display: "═══ Tous les modules spécifiés! Passage à la consolidation (04a→04c) puis handoff (05a→05c)... ═══"
552
- Display: "⚠ NE PAS lancer /ralph-loop — la consolidation et le handoff sont encore nécessaires."
553
551
  ba-writer.updateStatus({feature_id}, "specified")
554
552
 
555
553
  // CHECKPOINT: Save progress BEFORE transition (protects against context exhaustion)
@@ -562,10 +560,33 @@ IF currentModuleIndex >= moduleOrder.length:
562
560
  }
563
561
  })
564
562
 
565
- // MANDATORY TRANSITION DO NOT STOP HERE
566
- // If context is near exhaustion, the checkpoint above ensures the user
567
- // can resume with /business-analyse and it will route to step-04a automatically.
568
- Load: steps/step-04a-collect.md
563
+ // CHECK: Are we in project mode (multi-application)?
564
+ IF workflow.mode === "project":
565
+ // APPLICATION LOOP: Check if more applications remain
566
+ projectFeature = ba-reader.findProjectFeature()
567
+ ba-writer.updateApplicationStatus(projectId, currentApp.code, "specified")
568
+ ba-writer.advanceApplicationLoop(projectId)
569
+
570
+ IF projectFeature.metadata.workflow.currentApplicationIndex + 1 < projectFeature.metadata.workflow.applicationOrder.length:
571
+ nextApp = projectFeature.metadata.workflow.applicationOrder[currentApplicationIndex + 1]
572
+ Display: "═══ Application {currentApp.name} terminée ({N}/{total}). Prochaine : {nextApp} ═══"
573
+ Display: "⚠ NE PAS lancer /ralph-loop — d'autres applications et la consolidation sont encore nécessaires."
574
+ // Load step-02 for next application's module decomposition
575
+ Load: steps/step-02-decomposition.md
576
+ ELSE:
577
+ Display: "═══ Toutes les applications spécifiées! Passage à la consolidation cross-application... ═══"
578
+ Display: "⚠ NE PAS lancer /ralph-loop — la consolidation et le handoff sont encore nécessaires."
579
+ ba-writer.updateStatus(projectId, "specified")
580
+ Load: steps/step-04a-collect.md
581
+ ELSE:
582
+ // SINGLE APPLICATION: Current behavior
583
+ Display: "═══ Tous les modules spécifiés! Passage à la consolidation (04a→04c) puis handoff (05a→05c)... ═══"
584
+ Display: "⚠ NE PAS lancer /ralph-loop — la consolidation et le handoff sont encore nécessaires."
585
+
586
+ // MANDATORY TRANSITION — DO NOT STOP HERE
587
+ // If context is near exhaustion, the checkpoint above ensures the user
588
+ // can resume with /business-analyse and it will route to step-04a automatically.
589
+ Load: steps/step-04a-collect.md
569
590
  ```
570
591
 
571
592
  ---
@@ -279,6 +279,117 @@ Identify shared lookup/reference tables:
279
279
 
280
280
  ---
281
281
 
282
+ ### 2f. Cross-Application Validation (Project Mode Only)
283
+
284
+ > **Only runs when `workflow.mode === "project"`** (multi-application feature).
285
+ > Validates cross-application interactions identified during step-01b.
286
+
287
+ ```
288
+ IF workflow.mode !== "project":
289
+ SKIP this section entirely
290
+ → Proceed to section 6 storage
291
+ ```
292
+
293
+ **Process:**
294
+
295
+ 1. **Load project feature.json:**
296
+ ```
297
+ projectFeature = ba-reader.findProjectFeature()
298
+ applications = projectFeature.applications
299
+ applicationGraph = projectFeature.applicationDependencyGraph
300
+ ```
301
+
302
+ 2. **Cross-Application Shared Entities:**
303
+ ```javascript
304
+ const crossAppSharedEntities = [];
305
+ for (const app1 of applications) {
306
+ for (const app2 of applications) {
307
+ if (app1.code === app2.code) continue;
308
+ for (const mod1 of app1.modules) {
309
+ for (const mod2 of app2.modules) {
310
+ // Detect entity name overlap or FK references across apps
311
+ const shared = findSharedEntities(mod1.entities, mod2.entities);
312
+ if (shared.length > 0) {
313
+ crossAppSharedEntities.push({
314
+ entity: shared[0].name,
315
+ definedInApp: app1.code,
316
+ definedInModule: mod1.code,
317
+ referencedByApp: app2.code,
318
+ referencedByModule: mod2.code,
319
+ referenceType: shared[0].type
320
+ });
321
+ }
322
+ }
323
+ }
324
+ }
325
+ }
326
+ ```
327
+
328
+ 3. **Cross-Application Permission Path Consistency:**
329
+ ```javascript
330
+ // Verify permission paths use correct context per application
331
+ for (const app of applications) {
332
+ const expectedPrefix = `${app.context}.${toKebabCase(app.code)}`;
333
+ for (const mod of app.modules) {
334
+ for (const perm of mod.permissions || []) {
335
+ if (!perm.path.startsWith(expectedPrefix)) {
336
+ WARNING(`Permission path mismatch in ${app.code}/${mod.code}: "${perm.path}" should start with "${expectedPrefix}"`);
337
+ }
338
+ }
339
+ }
340
+ }
341
+ ```
342
+
343
+ 4. **Cross-Application Role Name Consistency:**
344
+ ```javascript
345
+ // Check for role name conflicts across applications
346
+ const allRoles = {};
347
+ for (const app of applications) {
348
+ for (const role of app.applicationRoles || []) {
349
+ if (allRoles[role.role] && allRoles[role.role].level !== role.level) {
350
+ WARNING(`Role "${role.role}" has different levels: ${allRoles[role.role].app}=${allRoles[role.role].level} vs ${app.code}=${role.level}`);
351
+ }
352
+ allRoles[role.role] = { app: app.code, level: role.level };
353
+ }
354
+ }
355
+ ```
356
+
357
+ 5. **Store Cross-Application Data:**
358
+ ```javascript
359
+ ba-writer.enrichSection({
360
+ featureId: {project_id},
361
+ section: "consolidation.crossApplicationInteractions",
362
+ data: crossAppSharedEntities.map(shared => ({
363
+ fromApplication: shared.definedInApp,
364
+ fromModule: shared.definedInModule,
365
+ toApplication: shared.referencedByApp,
366
+ toModule: shared.referencedByModule,
367
+ interactionType: shared.referenceType,
368
+ entity: shared.entity,
369
+ description: `${shared.entity} defined in ${shared.definedInApp}/${shared.definedInModule}, referenced by ${shared.referencedByApp}/${shared.referencedByModule}`
370
+ }))
371
+ });
372
+ ```
373
+
374
+ 6. **Display Cross-Application Interaction Map:**
375
+
376
+ ```
377
+ ═══════════════════════════════════════════════════════════════
378
+ CROSS-APPLICATION INTERACTIONS
379
+ ═══════════════════════════════════════════════════════════════
380
+
381
+ | Source App | Source Module | Target App | Target Module | Entity | Type |
382
+ |------------|-------------|------------|--------------|--------|------|
383
+ | HR | Employees | SelfService | LeaveRequests | Employee | FK |
384
+
385
+ Shared entities: {count}
386
+ Permission paths: {valid_count}/{total_count} consistent
387
+ Role conflicts: {conflict_count}
388
+ ═══════════════════════════════════════════════════════════════
389
+ ```
390
+
391
+ ---
392
+
282
393
  ## SINGLE-MODULE MODE
283
394
 
284
395
  When only 1 module: