@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.
Files changed (65) 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 +414 -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/dist/index.js +32 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/mcp-entry.mjs +7 -24
  17. package/dist/mcp-entry.mjs.map +1 -1
  18. package/package.json +1 -2
  19. package/templates/agents/ba-writer.md +142 -15
  20. package/templates/mcp-scaffolding/controller.cs.hbs +5 -1
  21. package/templates/skills/apex/SKILL.md +9 -3
  22. package/templates/skills/apex/_shared.md +49 -4
  23. package/templates/skills/{ralph-loop → apex}/references/core-seed-data.md +20 -11
  24. package/templates/skills/{ralph-loop → apex}/references/error-classification.md +2 -1
  25. package/templates/skills/apex/references/post-checks.md +463 -3
  26. package/templates/skills/apex/references/smartstack-api.md +76 -8
  27. package/templates/skills/apex/references/smartstack-frontend.md +74 -1
  28. package/templates/skills/apex/references/smartstack-layers.md +21 -3
  29. package/templates/skills/apex/steps/step-00-init.md +121 -1
  30. package/templates/skills/apex/steps/step-01-analyze.md +58 -0
  31. package/templates/skills/apex/steps/step-02-plan.md +36 -0
  32. package/templates/skills/apex/steps/step-03-execute.md +114 -7
  33. package/templates/skills/apex/steps/step-04-examine.md +116 -2
  34. package/templates/skills/business-analyse/SKILL.md +31 -20
  35. package/templates/skills/business-analyse/_module-loop.md +68 -9
  36. package/templates/skills/business-analyse/_shared.md +80 -21
  37. package/templates/skills/business-analyse/questionnaire/00-application.md +4 -2
  38. package/templates/skills/business-analyse/questionnaire/00b-project.md +85 -0
  39. package/templates/skills/business-analyse/references/deploy-modes.md +69 -0
  40. package/templates/skills/business-analyse/references/team-orchestration.md +158 -7
  41. package/templates/skills/business-analyse/schemas/application-schema.json +15 -1
  42. package/templates/skills/business-analyse/schemas/project-schema.json +490 -0
  43. package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +2 -1
  44. package/templates/skills/business-analyse/steps/step-00-init.md +220 -38
  45. package/templates/skills/business-analyse/steps/step-01-cadrage.md +184 -5
  46. package/templates/skills/business-analyse/steps/step-01b-applications.md +423 -0
  47. package/templates/skills/business-analyse/steps/step-02-decomposition.md +23 -6
  48. package/templates/skills/business-analyse/steps/step-03c-compile.md +14 -2
  49. package/templates/skills/business-analyse/steps/step-03d-validate.md +32 -7
  50. package/templates/skills/business-analyse/steps/step-04a-collect.md +111 -0
  51. package/templates/skills/business-analyse/steps/step-05a-handoff.md +296 -103
  52. package/templates/skills/business-analyse/steps/step-05b-deploy.md +46 -14
  53. package/templates/skills/documentation/SKILL.md +92 -2
  54. package/templates/skills/ralph-loop/SKILL.md +14 -17
  55. package/templates/skills/ralph-loop/references/category-rules.md +63 -683
  56. package/templates/skills/ralph-loop/references/compact-loop.md +188 -428
  57. package/templates/skills/ralph-loop/references/section-splitting.md +439 -0
  58. package/templates/skills/ralph-loop/references/team-orchestration.md +13 -14
  59. package/templates/skills/ralph-loop/steps/step-01-task.md +27 -0
  60. package/templates/skills/ralph-loop/steps/step-02-execute.md +80 -691
  61. package/templates/skills/ralph-loop/steps/step-03-commit.md +38 -79
  62. package/templates/skills/ralph-loop/steps/step-04-check.md +39 -58
  63. package/templates/skills/ralph-loop/steps/step-05-report.md +31 -123
  64. package/scripts/health-check.sh +0 -168
  65. package/scripts/postinstall.js +0 -18
@@ -0,0 +1,423 @@
1
+ ---
2
+ name: step-01b-applications
3
+ description: Application identity confirmation (single-app) or full application decomposition (multi-app)
4
+ model: opus
5
+ next_step: steps/step-02-decomposition.md
6
+ ---
7
+
8
+ > **Context files:** `_shared.md`
9
+
10
+ # Step 1b: Application Identity & Decomposition
11
+
12
+ ## MANDATORY EXECUTION RULES
13
+
14
+ - ALWAYS use ULTRATHINK mode
15
+ - ALL communication in `{language}`
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.
19
+
20
+ ---
21
+
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
+ ```
30
+
31
+ ---
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
+
184
+ ### 1. Load Project Context
185
+
186
+ ```
187
+ projectFeature = ba-reader.findProjectFeature()
188
+ globalRoles = projectFeature.cadrage.globalRoles (if defined)
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
199
+ ```
200
+
201
+ ### 2. Application Identity (per candidate)
202
+
203
+ For EACH candidate application from the cadrage analysis:
204
+
205
+ #### 2a. Confirm Application Name and Context
206
+
207
+ Display proposed application:
208
+ ```
209
+ {language == "fr"
210
+ ? "### Application : {suggestedName}\n\n**Contexte de navigation :** {suggestedContext}\n**Description :** {description based on coverage matrix items}"
211
+ : "### Application: {suggestedName}\n\n**Navigation context:** {suggestedContext}\n**Description:** {description based on coverage matrix items}"}
212
+ ```
213
+
214
+ Ask via AskUserQuestion:
215
+ ```
216
+ question: "{language == 'fr' ? 'Cette application et son contexte vous conviennent-ils ?' : 'Does this application and its context suit you?'}"
217
+ header: "{appName}"
218
+ options:
219
+ - label: "{language == 'fr' ? 'Oui, parfait' : 'Yes, perfect'}"
220
+ description: "{language == 'fr' ? 'Nom et contexte corrects' : 'Name and context are correct'}"
221
+ - label: "{language == 'fr' ? 'Modifier' : 'Modify'}"
222
+ description: "{language == 'fr' ? 'Changer le nom, le contexte ou la description' : 'Change name, context, or description'}"
223
+ ```
224
+
225
+ #### 2b. Table Prefix (per application)
226
+
227
+ Each application MUST have its own unique table prefix.
228
+
229
+ 1. Derive suggested prefix from application name (2-5 lowercase letters + `_`)
230
+ 2. Validate:
231
+ - Format: `^[a-z]{2,5}_$`
232
+ - No collision with platform prefixes: `auth_`, `nav_`, `usr_`, `ai_`, `cfg_`, `wkf_`, `support_`, `entra_`, `ref_`, `loc_`, `lic_`, `tenant_`
233
+ - No collision with other applications in this project
234
+ 3. Ask via AskUserQuestion for confirmation
235
+
236
+ #### 2c. Application-Specific Roles
237
+
238
+ Based on global roles from cadrage + application-specific needs:
239
+
240
+ ```
241
+ {language == "fr"
242
+ ? "### Rôles pour {appName}\n\nVoici les rôles hérités des rôles globaux, adaptés à cette application :"
243
+ : "### Roles for {appName}\n\nHere are the roles inherited from global roles, adapted for this application:"}
244
+
245
+ | Role | Level | Permission Pattern |
246
+ |------|-------|--------------------|
247
+ {for each role: name | admin/manager/contributor/viewer | context.app.* pattern}
248
+ ```
249
+
250
+ Ask via AskUserQuestion if additional roles are needed for this specific application.
251
+
252
+ #### 2d. Application Scope
253
+
254
+ Extract the subset of global scope that applies to this application:
255
+
256
+ ```
257
+ applicationScope = {
258
+ mustHave: globalScope.mustHave.filter(item => belongsToApp(item, appCode)),
259
+ shouldHave: globalScope.shouldHave.filter(item => belongsToApp(item, appCode)),
260
+ couldHave: globalScope.couldHave.filter(item => belongsToApp(item, appCode))
261
+ }
262
+ ```
263
+
264
+ Display and confirm with user.
265
+
266
+ ### 3. Inter-Application Dependency Analysis
267
+
268
+ After all applications are defined:
269
+
270
+ 1. **Identify dependencies:** For each pair of applications:
271
+ - Shared entities (e.g., Employee defined in HR, referenced by Self-Service)
272
+ - Event flows (e.g., HR publishes "EmployeeCreated", Self-Service subscribes)
273
+ - Authentication delegation (e.g., Self-Service uses HR's employee data for auth)
274
+ - Data dependencies (e.g., Self-Service reads HR data)
275
+
276
+ 2. **Build dependency graph:**
277
+ ```
278
+ applicationDependencyGraph = {
279
+ edges: [
280
+ { from: "EmployeeSelfService", to: "HumanResources", type: "data-dependency", description: "Reads employee data" }
281
+ ],
282
+ topologicalOrder: ["HumanResources", "EmployeeSelfService"],
283
+ layers: [
284
+ { layer: 0, applications: ["HumanResources"] },
285
+ { layer: 1, applications: ["EmployeeSelfService"] }
286
+ ]
287
+ }
288
+ ```
289
+
290
+ 3. **Cycle detection:** If circular dependencies detected → BLOCKING ERROR
291
+ - Display the cycle
292
+ - Ask user to resolve (merge applications or remove dependency)
293
+
294
+ ### 4. Client Checkpoint (BLOCKING)
295
+
296
+ Display the full application structure:
297
+
298
+ ```
299
+ {language == "fr"
300
+ ? "### Structure du projet\n\nVoici les applications identifiées :"
301
+ : "### Project Structure\n\nHere are the identified applications:"}
302
+
303
+ | # | Application | Contexte | Préfixe | Rôles | Dépendances |
304
+ |---|-------------|----------|---------|-------|-------------|
305
+ {for each app: index | name | context | prefix | role count | dependency list}
306
+
307
+ {language == "fr"
308
+ ? "### Ordre de traitement\n\nLes applications seront traitées dans cet ordre (fondations d'abord) :"
309
+ : "### Processing Order\n\nApplications will be processed in this order (foundations first):"}
310
+
311
+ {for each layer: "Layer {n}: {app1}, {app2}, ..."}
312
+ ```
313
+
314
+ Ask via AskUserQuestion:
315
+ ```
316
+ question: "{language == 'fr' ? 'Cette structure de projet vous convient-elle ?' : 'Does this project structure suit you?'}"
317
+ header: "Validation"
318
+ options:
319
+ - label: "{language == 'fr' ? 'Oui, continuer' : 'Yes, continue'}"
320
+ description: "{language == 'fr' ? 'Passer à la décomposition en modules' : 'Proceed to module decomposition'}"
321
+ - label: "{language == 'fr' ? 'Modifier' : 'Modify'}"
322
+ description: "{language == 'fr' ? 'Ajouter, supprimer ou modifier des applications' : 'Add, remove, or modify applications'}"
323
+ - label: "{language == 'fr' ? 'Fusionner en une seule' : 'Merge into one'}"
324
+ description: "{language == 'fr' ? 'Finalement, c\'est une seule application avec plusieurs modules' : 'Actually, this is one application with multiple modules'}"
325
+ ```
326
+
327
+ **IF "Merge into one":**
328
+ → Convert back to single-application mode
329
+ → Delete project-level feature.json
330
+ → Create application-level feature.json with all scope items
331
+ → Execute single-app path (sections 0a-0f above)
332
+
333
+ ### 5. Create Per-Application Feature.json Files
334
+
335
+ For EACH application in topological order:
336
+
337
+ ```
338
+ ba-writer.createApplicationFeature({
339
+ id: generate_feature_id(),
340
+ version: "1.0",
341
+ scope: "application",
342
+ status: "draft",
343
+ metadata: {
344
+ application: {app.code},
345
+ applicationCode: {toPascalCase(app.code)},
346
+ applicationRoute: {`/${app.context}/${toKebabCase(app.code)}`},
347
+ applicationIcon: {app.icon},
348
+ context: {app.context},
349
+ language: {language},
350
+ featureDescription: {app.description},
351
+ workflowType: "new",
352
+ analysisMode: "interactive",
353
+ mcpAvailable: true,
354
+ tablePrefix: {app.tablePrefix},
355
+ projectRef: {project_id},
356
+ workflow: {
357
+ mode: "application",
358
+ moduleOrder: [],
359
+ currentModuleIndex: 0,
360
+ completedModules: [],
361
+ currentModule: null
362
+ }
363
+ }
364
+ })
365
+ ```
366
+
367
+ Store the created feature.json path in the project feature.json `applications[].featureJsonPath`.
368
+
369
+ ### 6. Write to Project Feature.json
370
+
371
+ ```
372
+ ba-writer.enrichApplicationRegistry({
373
+ projectId: {project_id},
374
+ applications: [
375
+ {
376
+ code: {app1.code},
377
+ name: {app1.name},
378
+ context: {app1.context},
379
+ tablePrefix: {app1.tablePrefix},
380
+ icon: {app1.icon},
381
+ route: {app1.route},
382
+ description: {app1.description},
383
+ applicationRoles: [{...}],
384
+ scope: { mustHave: [...], shouldHave: [...], couldHave: [...] },
385
+ modules: [],
386
+ dependencies: [{...}],
387
+ status: "pending",
388
+ featureJsonPath: {app1.featureJsonPath},
389
+ estimatedComplexity: {app1.estimatedComplexity}
390
+ },
391
+ // ... more applications
392
+ ],
393
+ applicationDependencyGraph: {
394
+ edges: [...],
395
+ topologicalOrder: [...],
396
+ layers: [...]
397
+ }
398
+ })
399
+
400
+ ba-writer.updateStatus({project_id}, "decomposed")
401
+ ```
402
+
403
+ ### 7. Display Summary
404
+
405
+ ```
406
+ {language == "fr"
407
+ ? "### Décomposition en applications terminée\n\n"
408
+ : "### Application decomposition complete\n\n"}
409
+
410
+ | Application | Contexte | Préfixe | Route | Feature ID |
411
+ |-------------|----------|---------|-------|------------|
412
+ {for each app: name | context | prefix | route | FEAT-NNN}
413
+
414
+ {language == "fr"
415
+ ? "→ Prochaine étape : décomposition en modules pour **{firstApp}** (application {1}/{total})"
416
+ : "→ Next step: module decomposition for **{firstApp}** (application {1}/{total})"}
417
+ ```
418
+
419
+ ---
420
+
421
+ ## NEXT STEP
422
+
423
+ Load: `./step-02-decomposition.md` (for the first application in topological order, or the single application)
@@ -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()`.
@@ -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 },
@@ -548,8 +552,6 @@ IF currentModuleIndex < moduleOrder.length:
548
552
  Load: steps/step-03a1-setup.md
549
553
 
550
554
  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
555
  ba-writer.updateStatus({feature_id}, "specified")
554
556
 
555
557
  // CHECKPOINT: Save progress BEFORE transition (protects against context exhaustion)
@@ -562,10 +564,33 @@ IF currentModuleIndex >= moduleOrder.length:
562
564
  }
563
565
  })
564
566
 
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
567
+ // CHECK: Are we in project mode (multi-application)?
568
+ IF workflow.mode === "project":
569
+ // APPLICATION LOOP: Check if more applications remain
570
+ projectFeature = ba-reader.findProjectFeature()
571
+ ba-writer.updateApplicationStatus(projectId, currentApp.code, "specified")
572
+ ba-writer.advanceApplicationLoop(projectId)
573
+
574
+ IF projectFeature.metadata.workflow.currentApplicationIndex + 1 < projectFeature.metadata.workflow.applicationOrder.length:
575
+ nextApp = projectFeature.metadata.workflow.applicationOrder[currentApplicationIndex + 1]
576
+ Display: "═══ Application {currentApp.name} terminée ({N}/{total}). Prochaine : {nextApp} ═══"
577
+ Display: "⚠ NE PAS lancer /ralph-loop — d'autres applications et la consolidation sont encore nécessaires."
578
+ // Load step-02 for next application's module decomposition
579
+ Load: steps/step-02-decomposition.md
580
+ ELSE:
581
+ Display: "═══ Toutes les applications spécifiées! Passage à la consolidation cross-application... ═══"
582
+ Display: "⚠ NE PAS lancer /ralph-loop — la consolidation et le handoff sont encore nécessaires."
583
+ ba-writer.updateStatus(projectId, "specified")
584
+ Load: steps/step-04a-collect.md
585
+ ELSE:
586
+ // SINGLE APPLICATION: Current behavior
587
+ Display: "═══ Tous les modules spécifiés! Passage à la consolidation (04a→04c) puis handoff (05a→05c)... ═══"
588
+ Display: "⚠ NE PAS lancer /ralph-loop — la consolidation et le handoff sont encore nécessaires."
589
+
590
+ // MANDATORY TRANSITION — DO NOT STOP HERE
591
+ // If context is near exhaustion, the checkpoint above ensures the user
592
+ // can resume with /business-analyse and it will route to step-04a automatically.
593
+ Load: steps/step-04a-collect.md
569
594
  ```
570
595
 
571
596
  ---