@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.
- package/.documentation/init.html +409 -0
- package/dist/index.js +35 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +118 -70
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -2
- package/templates/mcp-scaffolding/controller.cs.hbs +5 -1
- package/templates/skills/apex/SKILL.md +6 -3
- package/templates/skills/apex/references/post-checks.md +225 -0
- package/templates/skills/apex/references/smartstack-api.md +29 -1
- package/templates/skills/apex/references/smartstack-frontend.md +27 -0
- package/templates/skills/apex/references/smartstack-layers.md +18 -2
- package/templates/skills/apex/steps/step-00-init.md +77 -1
- package/templates/skills/apex/steps/step-01-analyze.md +21 -0
- package/templates/skills/apex/steps/step-03-execute.md +94 -5
- package/templates/skills/apex/steps/step-04-examine.md +7 -1
- package/templates/skills/business-analyse/SKILL.md +4 -3
- package/templates/skills/business-analyse/_shared.md +9 -0
- package/templates/skills/business-analyse/schemas/application-schema.json +13 -0
- package/templates/skills/business-analyse/steps/step-00-init.md +190 -34
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +129 -10
- package/templates/skills/business-analyse/steps/step-01b-applications.md +184 -13
- package/templates/skills/business-analyse/steps/step-03c-compile.md +14 -2
- package/templates/skills/business-analyse/steps/step-03d-validate.md +5 -1
- package/templates/skills/documentation/SKILL.md +175 -9
- package/templates/skills/efcore/steps/squash/step-03-create.md +6 -4
- package/templates/skills/gitflow/_shared.md +3 -1
- package/templates/skills/gitflow/steps/step-pr.md +34 -0
- package/templates/skills/ralph-loop/SKILL.md +31 -2
- package/templates/skills/ralph-loop/references/category-rules.md +29 -0
- package/templates/skills/ralph-loop/references/compact-loop.md +85 -2
- package/templates/skills/ralph-loop/references/section-splitting.md +439 -0
- package/templates/skills/ralph-loop/references/team-orchestration.md +331 -14
- package/templates/skills/ralph-loop/steps/step-00-init.md +4 -0
- package/templates/skills/ralph-loop/steps/step-01-task.md +27 -0
- package/templates/skills/ralph-loop/steps/step-02-execute.md +206 -1
- package/templates/skills/ralph-loop/steps/step-05-report.md +19 -0
- package/scripts/health-check.sh +0 -168
- 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
|
-
|
|
50
|
-
|
|
51
|
-
→ Read
|
|
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
|
|
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 `
|
|
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.
|
|
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
|
-
|
|
811
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
20
|
+
---
|
|
20
21
|
|
|
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
|
+
```
|
|
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
|
-
→
|
|
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
|
-
|
|
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
|
|
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 |
|
|
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 },
|