@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
@@ -41,14 +41,31 @@ Phase 5: PERIMETRE → Bound scope with roles, coverage matrix (sections
41
41
 
42
42
  ---
43
43
 
44
+ ## PROJECT vs APPLICATION MODE
45
+
46
+ > **When `workflow_mode = "project"` (detected in step-00 from prompt structure):**
47
+ > The cadrage operates at the **PROJECT level**, not at a single application level.
48
+ > - Phase 1-3: Gather requirements for the ENTIRE project (all applications combined)
49
+ > - Phase 4: ANTICIPATION analyzes cross-application shared modules and suggests extracting them as dedicated applications
50
+ > - Phase 5: Coverage matrix maps requirements to APPLICATIONS first, then modules within each application
51
+ > - `candidate_applications` from step-00 are used as starting structure (can be modified during cadrage)
52
+ >
53
+ > **When `workflow_mode = "application"` (single-app):**
54
+ > The cadrage operates at the application level as before.
55
+
44
56
  ## PHASE 1: ECOUTE (Listen)
45
57
 
46
58
  ### 1. Read Current State
47
59
 
48
60
  ```
49
- ba-reader.findFeature({feature_id})
50
- → Read metadata: application, language, workflow.mode, useCase
51
- → Read cadrage section (if resuming)
61
+ IF workflow_mode === "project":
62
+ ba-reader.findProjectFeature({project_id})
63
+ → Read metadata: projectName, language, candidateApplications
64
+ → Read cadrage section (if resuming)
65
+ ELSE:
66
+ ba-reader.findFeature({feature_id})
67
+ → Read metadata: application, language, workflow.mode, useCase
68
+ → Read cadrage section (if resuming)
52
69
  ```
53
70
 
54
71
  IF cadrage already completed (status = "framed"):
@@ -507,6 +524,91 @@ options:
507
524
 
508
525
  6. Accepted suggestions enrich `coverageMatrix` and `globalScope`.
509
526
 
527
+ ### 5b. Cross-Application Shared Module Detection (PROJECT MODE ONLY)
528
+
529
+ > **When `workflow_mode = "project"`, analyze candidate applications for shared modules.**
530
+ > A module that appears in 2+ applications should be extracted as a dedicated transversal application.
531
+
532
+ **This section is SKIPPED if `workflow_mode !== "project"`.**
533
+
534
+ **Process:**
535
+
536
+ 1. Build module inventory per candidate application:
537
+
538
+ ```javascript
539
+ // From candidateApplications (step-00) + enriched by cadrage phases 1-4
540
+ const modulesByApp = {};
541
+ for (const app of candidate_applications) {
542
+ modulesByApp[app.name] = app.modules; // e.g., { "RH": ["Employes", "Conges", "Temps"], "Projet": ["Projets", "Temps"] }
543
+ }
544
+ ```
545
+
546
+ 2. Detect modules appearing in 2+ applications:
547
+
548
+ ```javascript
549
+ const allModules = Object.values(modulesByApp).flat();
550
+ const moduleCounts = {};
551
+ for (const mod of allModules) {
552
+ const normalized = normalizeModuleName(mod); // "Temps", "Saisie du temps", "Time Tracking" → same concept
553
+ moduleCounts[normalized] = (moduleCounts[normalized] || 0) + 1;
554
+ }
555
+ const sharedModules = Object.entries(moduleCounts)
556
+ .filter(([_, count]) => count >= 2)
557
+ .map(([name, count]) => ({ name, appearsIn: count, applications: findAppsContaining(name) }));
558
+ ```
559
+
560
+ 3. IF shared modules found, propose extraction:
561
+
562
+ ```
563
+ {language == "fr"
564
+ ? "### Modules partagés détectés\n\nCertains modules apparaissent dans plusieurs applications. Les extraire en application(s) dédiée(s) améliore la réutilisabilité et évite la duplication :"
565
+ : "### Shared modules detected\n\nSome modules appear in multiple applications. Extracting them as dedicated application(s) improves reusability and avoids duplication:"}
566
+
567
+ | Module | Présent dans | Suggestion |
568
+ |--------|-------------|-----------|
569
+ {for each shared: name | apps.join(", ") | "Extraire en application '{name}'" }
570
+
571
+ {language == "fr"
572
+ ? "**Exemple :** Si 'Gestion du temps' est dans RH et Projet, créer une application 'Temps' dédiée que les deux autres consomment."
573
+ : "**Example:** If 'Time Management' is in HR and Project, create a dedicated 'Time' application that the other two consume."}
574
+ ```
575
+
576
+ Ask via AskUserQuestion:
577
+ ```
578
+ question: "{language == 'fr' ? 'Souhaitez-vous extraire les modules partagés en application(s) dédiée(s) ?' : 'Do you want to extract shared modules as dedicated application(s)?'}"
579
+ header: "Extraction"
580
+ options:
581
+ - label: "{language == 'fr' ? 'Oui, extraire' : 'Yes, extract'}"
582
+ description: "{language == 'fr' ? 'Créer {sharedModules.length} application(s) transversale(s) supplémentaire(s)' : 'Create {sharedModules.length} additional cross-cutting application(s)'}"
583
+ - label: "{language == 'fr' ? 'Non, garder en l\'état' : 'No, keep as-is'}"
584
+ description: "{language == 'fr' ? 'Chaque application garde sa propre version du module' : 'Each application keeps its own version of the module'}"
585
+ ```
586
+
587
+ **IF "Oui, extraire":**
588
+ ```javascript
589
+ for (const shared of sharedModules) {
590
+ // Create new candidate application
591
+ candidate_applications.push({
592
+ name: shared.name,
593
+ description: `Application transversale pour ${shared.name}`,
594
+ modules: [shared.name],
595
+ context: "business",
596
+ isTransversal: true,
597
+ consumedBy: shared.applications
598
+ });
599
+
600
+ // Remove the shared module from original applications
601
+ for (const appName of shared.applications) {
602
+ const app = candidate_applications.find(a => a.name === appName);
603
+ app.modules = app.modules.filter(m => normalizeModuleName(m) !== shared.name);
604
+ app.dependencies = app.dependencies || [];
605
+ app.dependencies.push({ target: shared.name, type: "data-dependency" });
606
+ }
607
+ }
608
+ ```
609
+
610
+ Update `candidate_applications` in the project feature.json.
611
+
510
612
  ---
511
613
 
512
614
  ## PHASE 5: PERIMETRE (Bound Scope)
@@ -720,9 +822,25 @@ ba-writer.enrichSection({
720
822
  ba-writer.updateStatus({feature_id}, "framed")
721
823
  ```
722
824
 
723
- ### 9. Multi-Application Detection (NEW)
825
+ ### 9. Multi-Application Detection
724
826
 
725
827
  > **Analyze whether the project spans multiple independent applications.**
828
+ > **SKIP this section entirely if `workflow_mode = "project"`** — multi-app was already detected in step-00
829
+ > and cross-app analysis was done in section 5b above.
830
+
831
+ **IF `workflow_mode === "project"`:**
832
+ → SKIP section 9 entirely. Candidate applications are already identified and enriched.
833
+ → Write cadrage data to project-level feature.json:
834
+ ```
835
+ ba-writer.enrichSection({
836
+ projectId: {project_id},
837
+ section: "cadrage",
838
+ data: {cadrage data collected in phases 1-5 above}
839
+ })
840
+ ```
841
+ → Continue to section 10 (summary).
842
+
843
+ **IF `workflow_mode === "application"` (single-app mode):**
726
844
 
727
845
  After coverage matrix is validated, check if the identified functional domains suggest multiple applications:
728
846
 
@@ -763,15 +881,15 @@ ba-writer.createProjectFeature({
763
881
  metadata: {
764
882
  projectName: {project_name derived from feature_description},
765
883
  language: {language},
766
- featureDescription: {feature_description}
884
+ featureDescription: {feature_description},
885
+ candidateApplications: [{detected candidates}]
767
886
  },
768
887
  cadrage: {cadrage data already collected in this step}
769
888
  })
770
889
  ```
771
- 2. Set `workflow.mode = "project"` in project feature.json
890
+ 2. Set `workflow_mode = "project"` and `project_id = PROJ-NNN`
772
891
  3. Load `questionnaire/00b-project.md` for additional project-level questions
773
- 4. Load `steps/step-01b-applications.md` for application decomposition
774
- 5. **STOP — DO NOT load step-02**
892
+ 4. Continue to section 10 and then step-01b
775
893
 
776
894
  **IF "Single application":**
777
895
  → Continue to step 10 (display summary) and step-02 as usual.
@@ -807,5 +925,6 @@ ba-writer.createProjectFeature({
807
925
 
808
926
  ## NEXT STEP
809
927
 
810
- **IF workflow.mode === "project":** Load: `./step-01b-applications.md`
811
- **ELSE:** Load: `./step-02-decomposition.md`
928
+ Load: `./step-01b-applications.md`
929
+
930
+ > step-01b handles both single-app (lightweight identity confirmation) and multi-app (full application decomposition).
@@ -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 },