@atlashub/smartstack-cli 4.74.0 → 4.76.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 (121) hide show
  1. package/dist/index.js +152 -31
  2. package/dist/index.js.map +1 -1
  3. package/dist/mcp-entry.mjs +14 -3
  4. package/dist/mcp-entry.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/templates/agents/ba-reader.md +17 -15
  7. package/templates/agents/ba-writer.md +49 -51
  8. package/templates/skills/apex/SKILL.md +2 -2
  9. package/templates/skills/apex/_shared.md +1 -1
  10. package/templates/skills/apex/references/checks/backend-checks.sh +21 -7
  11. package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
  12. package/templates/skills/apex/references/checks/infrastructure-checks.sh +47 -10
  13. package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
  14. package/templates/skills/apex/references/core-seed-data.md +20 -18
  15. package/templates/skills/apex/references/frontend-route-wiring-app-tsx.md +3 -0
  16. package/templates/skills/apex/references/post-checks.md +23 -3
  17. package/templates/skills/apex/references/smartstack-api.md +4 -4
  18. package/templates/skills/apex/references/smartstack-frontend.md +54 -8
  19. package/templates/skills/apex/references/smartstack-layers.md +6 -6
  20. package/templates/skills/apex/steps/step-00-init.md +75 -1
  21. package/templates/skills/apex/steps/step-03-execute.md +16 -4
  22. package/templates/skills/apex/steps/step-03b-layer1-seed.md +65 -6
  23. package/templates/skills/apex/steps/step-03c-layer2-backend.md +50 -5
  24. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +226 -4
  25. package/templates/skills/apex/steps/step-04-examine.md +163 -0
  26. package/templates/skills/apex-verify/SKILL.md +110 -0
  27. package/templates/skills/apex-verify/references/audit-rules.md +50 -0
  28. package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
  29. package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
  30. package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
  31. package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
  32. package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
  33. package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
  34. package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +3 -0
  35. package/templates/skills/application/templates-frontend.md +2 -2
  36. package/templates/skills/business-analyse/SKILL.md +17 -3
  37. package/templates/skills/business-analyse/_shared.md +64 -0
  38. package/templates/skills/business-analyse/patterns/suggestion-catalog.md +34 -26
  39. package/templates/skills/business-analyse/questionnaire/01-context.md +13 -9
  40. package/templates/skills/business-analyse/questionnaire/02-stakeholders-scope.md +20 -27
  41. package/templates/skills/business-analyse/questionnaire.md +86 -9
  42. package/templates/skills/business-analyse/references/03-json-schemas.md +221 -0
  43. package/templates/skills/business-analyse/references/03-post-check-validation.md +208 -0
  44. package/templates/skills/business-analyse/references/03-smartstack-entity-guards.md +32 -0
  45. package/templates/skills/business-analyse/references/04-cross-module-validation.md +95 -0
  46. package/templates/skills/business-analyse/references/04-file-allocation.md +162 -0
  47. package/templates/skills/business-analyse/references/04-naming-audit-checks.md +174 -0
  48. package/templates/skills/business-analyse/references/04-semantic-validation-matrix.md +118 -0
  49. package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
  50. package/templates/skills/business-analyse/references/domain-research-playbook.md +234 -0
  51. package/templates/skills/business-analyse/references/entity-sourcing-presentation.md +166 -0
  52. package/templates/skills/business-analyse/references/init-resume-logic.md +70 -0
  53. package/templates/skills/business-analyse/references/module-completeness-challenge.md +174 -0
  54. package/templates/skills/business-analyse/references/multi-app-detection.md +149 -0
  55. package/templates/skills/business-analyse/references/portal-classification.md +52 -0
  56. package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
  57. package/templates/skills/business-analyse/references/validation-checklist.md +35 -6
  58. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +50 -6
  59. package/templates/skills/business-analyse/steps/step-00-init.md +22 -190
  60. package/templates/skills/business-analyse/steps/step-01-cadrage.md +365 -269
  61. package/templates/skills/business-analyse/steps/step-02-structure.md +98 -20
  62. package/templates/skills/business-analyse/steps/step-03-specify.md +810 -229
  63. package/templates/skills/business-analyse/steps/step-04-consolidate.md +509 -278
  64. package/templates/skills/business-analyse-design/SKILL.md +10 -0
  65. package/templates/skills/business-analyse-design/references/screens-post-check.md +221 -0
  66. package/templates/skills/business-analyse-design/references/screens-type-mapping.md +138 -0
  67. package/templates/skills/business-analyse-design/references/smartcomponents-templates.md +225 -0
  68. package/templates/skills/{business-analyse → business-analyse-design}/references/spec-auto-inference.md +117 -117
  69. package/templates/skills/business-analyse-design/steps/step-01-screens.md +36 -162
  70. package/templates/skills/business-analyse-design/steps/step-02-wireframes.md +8 -7
  71. package/templates/skills/business-analyse-design/steps/step-03-navigation.md +89 -42
  72. package/templates/skills/business-analyse-develop/references/compact-loop.md +9 -0
  73. package/templates/skills/business-analyse-develop/references/handoff-quality-gate.md +132 -0
  74. package/templates/skills/business-analyse-develop/references/prd-v3-transformation.md +326 -0
  75. package/templates/skills/business-analyse-develop/references/report-reconciliation.md +140 -0
  76. package/templates/skills/business-analyse-develop/references/report-template.md +142 -0
  77. package/templates/skills/business-analyse-develop/steps/step-01-task.md +5 -177
  78. package/templates/skills/business-analyse-develop/steps/step-02-execute.md +17 -4
  79. package/templates/skills/business-analyse-develop/steps/step-03-commit.md +6 -2
  80. package/templates/skills/business-analyse-develop/steps/step-04-check.md +6 -0
  81. package/templates/skills/business-analyse-develop/steps/step-05-report.md +3 -269
  82. package/templates/skills/business-analyse-handoff/SKILL.md +10 -0
  83. package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +211 -0
  84. package/templates/skills/business-analyse-handoff/references/context-isolation-pattern.md +47 -0
  85. package/templates/skills/business-analyse-handoff/references/handoff-file-inventory.md +49 -0
  86. package/templates/skills/business-analyse-handoff/references/handoff-global-validation.md +142 -0
  87. package/templates/skills/business-analyse-handoff/references/prd-validation-checks.md +125 -0
  88. package/templates/skills/business-analyse-handoff/references/project-index-update.md +98 -0
  89. package/templates/skills/business-analyse-handoff/steps/step-01-transform.md +9 -160
  90. package/templates/skills/business-analyse-handoff/steps/step-02-export.md +10 -99
  91. package/templates/skills/business-analyse-html/SKILL.md +10 -0
  92. package/templates/skills/business-analyse-html/html/ba-interactive.html +504 -97
  93. package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +79 -2
  94. package/templates/skills/business-analyse-html/html/src/scripts/02-navigation.js +6 -46
  95. package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
  96. package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
  97. package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +94 -36
  98. package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +162 -0
  99. package/templates/skills/business-analyse-html/html/src/styles/10-diagrams.css +73 -0
  100. package/templates/skills/business-analyse-html/html/src/template.html +2 -0
  101. package/templates/skills/business-analyse-html/references/02-embedded-artifacts-building.md +144 -0
  102. package/templates/skills/business-analyse-html/references/02-feature-data-building.md +143 -0
  103. package/templates/skills/business-analyse-html/references/02-mapping-tables.md +442 -0
  104. package/templates/skills/business-analyse-html/references/02-normalization-helpers.md +139 -0
  105. package/templates/skills/business-analyse-html/references/02-screen-format-detection.md +283 -0
  106. package/templates/skills/business-analyse-html/references/02-self-check-validation.md +199 -0
  107. package/templates/skills/business-analyse-html/references/data-build.md +24 -1
  108. package/templates/skills/business-analyse-html/references/data-mapping.md +119 -17
  109. package/templates/skills/business-analyse-html/steps/step-02-build-data.md +18 -555
  110. package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
  111. package/templates/skills/business-analyse-quick/SKILL.md +807 -0
  112. package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
  113. package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
  114. package/templates/skills/business-analyse-review/SKILL.md +10 -0
  115. package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
  116. package/templates/skills/business-analyse-status/SKILL.md +8 -0
  117. package/templates/skills/dev-start/SKILL.md +143 -307
  118. package/templates/skills/efcore/SKILL.md +13 -0
  119. package/templates/skills/sketch/SKILL.md +15 -153
  120. package/templates/skills/ui-components/SKILL.md +1 -1
  121. package/templates/skills/ui-components/patterns/data-table.md +1 -1
@@ -14,7 +14,7 @@ next_step: none
14
14
  - ALWAYS validate permission coherence across modules
15
15
  - ALWAYS execute semantic validation checks
16
16
  - NEVER duplicate module content - work with summaries only
17
- - Single-module shortcut: auto-approve consolidation if only 1 module
17
+ - ALWAYS ask for client approval, even for single-module projects (play devil's advocate)
18
18
 
19
19
  ## Context Recovery
20
20
 
@@ -50,98 +50,151 @@ IF status already "consolidated":
50
50
  STOP
51
51
  ```
52
52
 
53
- ### 2. Cross-Module Interaction Analysis
54
-
55
- **2a. Load Module Index Files**
53
+ ### 1b. Analysis Quality Self-Assessment (MANDATORY)
56
54
 
57
- For each completed module, read `index.json` and discover granular files:
55
+ > **Score the analysis quality BEFORE presenting to the client.**
56
+ > This catches low-quality output before it reaches consolidation.
58
57
 
58
+ #### 1b-i. Research Coverage
59
59
  ```javascript
60
- const moduleFiles = {
61
- entities: "entities.json",
62
- rules: "rules.json",
63
- usecases: "usecases.json",
64
- permissions: "permissions.json",
65
- screens: "screens.json"
66
- };
67
-
68
- for (const module of completedModules) {
69
- const index = ba-reader.readSection(module.code, "index");
70
- module.files = index.files;
71
- // Load only files needed for consolidation
72
- module.entities = ba-reader.readSection(module.code, "entities");
73
- module.permissions = ba-reader.readSection(module.code, "permissions");
60
+ let researchScore = 0;
61
+ if (domain_research?.source === "built-in-only") {
62
+ researchScore = 2;
63
+ } else {
64
+ // +2 per application with Level 1 research
65
+ for (const app of applications) {
66
+ if (domain_research?.applications?.[app.code]) researchScore += 2;
67
+ }
68
+ // +1 per module with Level 2 research (cap at 4)
69
+ let moduleResearchCount = 0;
70
+ for (const module of completedModules) {
71
+ if (module.domainResearchSource !== "built-in-only") moduleResearchCount++;
72
+ }
73
+ researchScore += Math.min(moduleResearchCount, 4);
74
74
  }
75
+ researchScore = Math.min(10, researchScore);
75
76
  ```
76
77
 
77
- **2b. Foreign Key References & Shared Entities**
78
-
79
- Build entity registry and validate cross-module references:
80
-
78
+ #### 1b-ii. Module Completeness (per app)
81
79
  ```javascript
82
- const entityRegistry = {};
83
- for (const module of completedModules) {
84
- entityRegistry[module.code] = {
85
- entities: module.entities.map(e => ({
86
- name: e.name,
87
- attributes: e.attributes.map(a => a.name),
88
- pk: e.attributes.find(a => a.name === "Id" || a.unique)?.name || "Id"
89
- }))
90
- };
80
+ for (const app of applications) {
81
+ const archetype = loadArchetype(app.type); // from module-completeness-challenge.md
82
+ const corePresent = archetype.coreModules.filter(m => appHasModule(app, m)).length;
83
+ const coreTotal = archetype.coreModules.length;
84
+ app.completenessScore = (corePresent / coreTotal) * 10;
85
+ // Penalty if CORE excluded without justification
86
+ const unjustified = archetype.coreModules.filter(m =>
87
+ !appHasModule(app, m) && !isExplicitlyExcluded(app, m)
88
+ );
89
+ if (unjustified.length > 0) app.completenessScore -= 3;
91
90
  }
91
+ const avgModuleScore = average(applications.map(a => a.completenessScore));
92
+ ```
92
93
 
93
- // Detect shared entities
94
- const sharedEntities = [];
94
+ #### 1b-iii. Section Depth (per module)
95
+ ```javascript
95
96
  for (const module of completedModules) {
96
- for (const entity of module.entities) {
97
- for (const rel of entity.relationships || []) {
98
- if (rel.target.includes(".")) {
99
- const [targetModule, targetEntity] = rel.target.split(".");
100
- if (!entityRegistry[targetModule]) {
101
- BLOCKING_ERROR(`Module ${module.code}: references non-existent module "${targetModule}"`);
102
- }
103
- sharedEntities.push({
104
- entity: targetEntity,
105
- definedIn: targetModule,
106
- referencedBy: module.code,
107
- referenceType: rel.type
108
- });
109
- }
110
- }
97
+ const sectionCount = module.anticipatedSections?.length || 0;
98
+ if (module.featureType === "workflow") {
99
+ if (sectionCount < 5) module.sectionScore = 4;
100
+ else if (sectionCount < 7) module.sectionScore = 7;
101
+ else module.sectionScore = 9;
102
+ // Bonus for contextual views
103
+ const hasContextual = module.anticipatedSections?.some(s =>
104
+ ["open-requests", "rejected", "history", "my-balance", "my-tasks", "overdue"].includes(s.code)
105
+ );
106
+ if (hasContextual) module.sectionScore = Math.min(10, module.sectionScore + 1);
107
+ } else {
108
+ module.sectionScore = sectionCount >= 3 ? 8 : sectionCount >= 2 ? 6 : 4;
111
109
  }
112
110
  }
111
+ const avgSectionScore = average(completedModules.map(m => m.sectionScore));
113
112
  ```
114
113
 
115
- **2c. Detect Circular Dependencies**
116
-
114
+ #### 1b-iv. Business Rule Depth (per module)
117
115
  ```javascript
118
- const cycles = detectCycles(dependencyGraph);
119
- if (cycles.length > 0) {
120
- BLOCKING_ERROR("Circular dependencies detected:");
121
- for (const cycle of cycles) {
122
- ERROR(` ${cycle.join(" ")}`);
123
- }
124
- ACTIONS: Review dependencies, move shared entities to Core module, or use event-driven communication
125
- STOP - DO NOT PROCEED
116
+ for (const module of completedModules) {
117
+ const rules = module.rules || [];
118
+ const domainSpecific = rules.filter(r => r.domainSpecific === true || r.source === "domain-research").length;
119
+ const total = rules.length;
120
+ if (total === 0) { module.ruleScore = 0; continue; }
121
+ const genericRatio = (total - domainSpecific) / total;
122
+ if (genericRatio > 0.8) module.ruleScore = 3; // >80% generic = poor
123
+ else if (genericRatio > 0.5) module.ruleScore = 6; // 50-80% generic = acceptable
124
+ else module.ruleScore = 9; // <50% generic = excellent
126
125
  }
126
+ const avgRuleScore = average(completedModules.map(m => m.ruleScore));
127
127
  ```
128
128
 
129
- **2d. Display Cross-Module Interaction Map**
129
+ #### 1b-v. Global Quality Score
130
+ ```javascript
131
+ const qualityScore = Math.round(
132
+ (researchScore + avgModuleScore + avgSectionScore + avgRuleScore) / 4
133
+ );
134
+ ```
130
135
 
136
+ Display:
131
137
  ```
132
- ═══════════════════════════════════════════════════════════════
133
- CROSS-MODULE INTERACTIONS VALIDATED
134
- ═══════════════════════════════════════════════════════════════
138
+ ### Auto-evaluation qualite de l'analyse
135
139
 
136
- | Source Module | Target | Type | Status |
137
- |---------------|--------|------|--------|
138
- | Orders | Customers.Customer | FK (N:1) | RESOLVED |
139
- | Invoices | Customers.Customer | FK (N:1) | RESOLVED |
140
+ | Dimension | Score | Detail |
141
+ |-----------|-------|--------|
142
+ | Recherche web | {researchScore}/10 | {N} apps recherchees, {M} modules recherches |
143
+ | Completude modules | {avgModuleScore}/10 | {details per app} |
144
+ | Profondeur sections | {avgSectionScore}/10 | {details per module} |
145
+ | Profondeur regles metier | {avgRuleScore}/10 | {domainSpecific}/{total} regles specifiques domaine |
146
+ | **GLOBAL** | **{qualityScore}/10** | |
147
+ ```
140
148
 
141
- Total: {count} cross-module references
142
- Shared entities: {count}
143
- ═══════════════════════════════════════════════════════════════
144
149
  ```
150
+ IF qualityScore < 7:
151
+ WARNING: "Score qualite inferieur a 7/10.
152
+ Recommandation : relancer les etapes deficientes avec WebSearch active."
153
+ Display: "Dimensions a ameliorer: {list dimensions < 7}"
154
+ ```
155
+
156
+ Store in `validation.json` as `qualityAssessment`.
157
+
158
+ ### 1c. Business Process Coverage Validation (BLOCKING for multi-app)
159
+
160
+ > **Verify that all identified business processes are covered by modules.**
161
+ > A process step without a covering module = a GAP in the application.
162
+
163
+ ```javascript
164
+ IF cadrage._preAnalysis?.businessProcesses?.length > 0:
165
+ const gaps = [];
166
+ for (const process of cadrage._preAnalysis.businessProcesses) {
167
+ for (const step of process.steps) {
168
+ if (!step.covered) {
169
+ gaps.push({ process: process.name, step: step.name, apps: process.apps });
170
+ }
171
+ }
172
+ }
173
+
174
+ Display:
175
+ ### Couverture des processus metier
176
+ | Processus | Etapes | Couvertes | Gaps |
177
+ |-----------|--------|-----------|------|
178
+ {for each process: name, steps.length, covered count, gap names}
179
+
180
+ IF gaps.length > 0:
181
+ WARNING: "{gaps.length} etapes de processus non couvertes."
182
+ // Not BLOCKING at consolidation (should have been caught in step-01/02)
183
+ // But display as warning for final visibility
184
+ ```
185
+
186
+ Store in `validation.json` as `processCoverage`.
187
+
188
+ ### 2. Cross-Module Interaction Analysis
189
+
190
+ → **Load** `references/04-cross-module-validation.md` for FK validation rules, cycle detection, and interaction mapping.
191
+
192
+ **Execution:**
193
+ - Load module index files and granular JSON (entities, permissions)
194
+ - Build entity registry and detect shared entities
195
+ - Validate foreign key references across modules
196
+ - Detect circular dependencies (blocking error if any found)
197
+ - Display cross-module interaction map with validation status
145
198
 
146
199
  ### 3. Permission Coherence Check
147
200
 
@@ -184,43 +237,124 @@ const hierarchy = ["Admin", "Manager", "Contributor", "Viewer"];
184
237
 
185
238
  ### 4. Semantic Validation (MANDATORY)
186
239
 
187
- Execute 13 semantic checks per module:
240
+ **Load** `references/04-semantic-validation-matrix.md` and execute ALL semantic validation checks.
241
+
242
+ This includes:
243
+ - 13 semantic checks per module (permission orphans, UC coverage, entity doubles, permission ghosts, etc.)
244
+ - 5 contract verification checks (BR formulas, computed attributes, UC counts, permission counts, versioned attributes)
245
+ - Blocking rule: 0 ERROR → PASS, ≥1 ERROR → BLOCK (user must fix before proceeding)
188
246
 
247
+ **BR-EXAMPLES-COMPLETE check — qualité test-ready (NEW):**
189
248
  ```javascript
190
- const semanticChecks = [
191
- { check: "permission-orpheline", severity: "WARNING" },
192
- { check: "uc-sans-fr", severity: "WARNING" },
193
- { check: "entity-sans-endpoint", severity: "WARNING" },
194
- { check: "uc-sans-scenario", severity: "WARNING" },
195
- { check: "role-sans-permissions", severity: "WARNING" },
196
- { check: "navigation-sans-traduction", severity: "ERROR" },
197
- { check: "lifecycle-terminal", severity: "WARNING" },
198
- { check: "schema-conformity", severity: "ERROR" },
199
- { check: "wireframe-coverage", severity: "ERROR" },
200
- { check: "entity-doublon", severity: "ERROR" },
201
- { check: "permission-fantome", severity: "ERROR" },
202
- { check: "dashboard-coverage", severity: "WARNING" },
203
- { check: "permission-uc-alignment", severity: "WARNING" }
204
- ];
249
+ for (const module of completedModules) {
250
+ const rules = module.rules || [];
251
+ if (rules.length === 0) continue;
252
+
253
+ // 1. Couverture : toutes les règles doivent avoir des exemples
254
+ const withExamples = rules.filter(r => r.examples?.length > 0);
255
+ const exampleRate = withExamples.length / rules.length;
256
+
257
+ if (exampleRate < 0.4) {
258
+ ERROR(`Module ${module.code}: ${withExamples.length}/${rules.length} BRs ont des exemples (${Math.round(exampleRate*100)}% < 40% minimum) — BLOQUANT`);
259
+ } else if (exampleRate < 0.7) {
260
+ WARNING(`Module ${module.code}: ${withExamples.length}/${rules.length} BRs ont des exemples (${Math.round(exampleRate*100)}% — cible: 100%)`);
261
+ }
262
+
263
+ // 2. Format test-ready : exemples avec given/when/then
264
+ const testReady = rules.filter(r =>
265
+ r.examples?.some(e => e.given && e.when && e.then)
266
+ );
267
+ const proseOnly = rules.filter(r =>
268
+ r.examples?.length > 0 && !r.examples.some(e => e.given && e.when && e.then)
269
+ );
205
270
 
271
+ if (testReady.length < 4 && module.featureType !== 'portal') {
272
+ WARNING(`Module ${module.code}: ${testReady.length}/4 BRs au format test-ready {given, when, then}`);
273
+ }
274
+ if (proseOnly.length > 0) {
275
+ WARNING(`Module ${module.code}: ${proseOnly.length} BRs avec exemples en prose — convertir en format test-ready pour génération de tests`);
276
+ }
277
+
278
+ // 3. BR-CALC : formule + exemple chiffré obligatoires
279
+ const calcRules = rules.filter(r => r.category === 'calculation');
280
+ const calcWithoutFormula = calcRules.filter(r => !r.formula);
281
+ const calcWithoutNumericExample = calcRules.filter(r =>
282
+ !r.examples?.some(e =>
283
+ (e.scenario === 'calculation') ||
284
+ (e.then && Object.values(e.then).some(v => typeof v === 'number'))
285
+ )
286
+ );
287
+
288
+ if (calcWithoutFormula.length > 0) {
289
+ ERROR(`Module ${module.code}: ${calcWithoutFormula.length} BR-CALC sans champ formula`);
290
+ }
291
+ if (calcWithoutNumericExample.length > 0) {
292
+ WARNING(`Module ${module.code}: ${calcWithoutNumericExample.length} BR-CALC sans exemple chiffré — les tests de calcul nécessitent des valeurs numériques`);
293
+ }
294
+
295
+ // 4. Couverture scénarios : chaque règle devrait avoir happy_path + error
296
+ const withBothScenarios = rules.filter(r =>
297
+ r.examples?.some(e => e.scenario === 'happy_path' || e.then?.result === 'success') &&
298
+ r.examples?.some(e => e.scenario === 'error' || e.then?.result === 'error')
299
+ );
300
+ if (withBothScenarios.length < rules.length * 0.5 && module.featureType !== 'portal') {
301
+ WARNING(`Module ${module.code}: ${withBothScenarios.length}/${rules.length} BRs couvrent happy_path + error — cible: 100%`);
302
+ }
303
+ }
304
+ ```
305
+
306
+ ### 4b. Language Coherence Check (MANDATORY)
307
+
308
+ Verify that ALL generated content respects `{language}` from config:
309
+
310
+ ```javascript
311
+ const expectedLang = language; // from config.json
312
+
313
+ // Heuristics for language detection
314
+ const frIndicators = /\b(le|la|les|un|une|des|du|de|est|sont|doit|peut|dans|pour|avec|sur|par|cette|tout|qui)\b/i;
315
+ const enIndicators = /\b(the|a|an|is|are|must|can|in|for|with|on|by|this|all|who|should|when|each)\b/i;
316
+
317
+ function detectLang(text) {
318
+ if (!text || text.length < 10) return null;
319
+ const frScore = (text.match(frIndicators) || []).length;
320
+ const enScore = (text.match(enIndicators) || []).length;
321
+ if (frScore > enScore * 1.5) return 'fr';
322
+ if (enScore > frScore * 1.5) return 'en';
323
+ return null; // ambiguous
324
+ }
325
+
326
+ const langIssues = [];
206
327
  for (const module of completedModules) {
207
- for (const check of semanticChecks) {
208
- const result = executeSemanticCheck(module, check);
209
- if (result.status === "ERROR") {
210
- errors.push({module: module.code, check: check.check, details: result.details});
211
- } else if (result.status === "WARNING") {
212
- warnings.push({module: module.code, check: check.check, details: result.details});
328
+ // Sample entity descriptions
329
+ (module.entities || []).forEach(e => {
330
+ const detected = detectLang(e.description);
331
+ if (detected && detected !== expectedLang) {
332
+ langIssues.push({ module: module.code, type: 'entity', name: e.name, detected, text: e.description?.substring(0, 60) });
213
333
  }
214
- }
334
+ });
335
+ // Sample BR statements
336
+ (module.rules || []).forEach(r => {
337
+ const detected = detectLang(r.statement);
338
+ if (detected && detected !== expectedLang) {
339
+ langIssues.push({ module: module.code, type: 'rule', name: r.id, detected, text: r.statement?.substring(0, 60) });
340
+ }
341
+ });
342
+ // Sample UC names
343
+ (module.useCases || module.usecases || []).forEach(u => {
344
+ const detected = detectLang(u.name);
345
+ if (detected && detected !== expectedLang) {
346
+ langIssues.push({ module: module.code, type: 'useCase', name: u.id || u.name, detected, text: u.name });
347
+ }
348
+ });
215
349
  }
216
350
 
217
- // Blocking rule: 0 ERROR → PASS, ≥1 ERROR → BLOCK (ask user for each)
218
- if (errors.length > 0) {
219
- BLOCKING_ERROR("Semantic validation failed");
220
- for (const error of errors) {
221
- ERROR(` [${error.module}] ${error.check}: ${error.details}`);
351
+ if (langIssues.length > 0) {
352
+ WARNING(`${langIssues.length} content items detected in wrong language (expected: ${expectedLang})`);
353
+ Display(langIssues.slice(0, 10).map(i => ` - ${i.module}/${i.type} "${i.name}": detected ${i.detected}, text: "${i.text}"`).join('\n'));
354
+
355
+ if (langIssues.length > completedModules.length * 3) {
356
+ BLOCKING_ERROR(`Too many language violations (${langIssues.length}). Re-specify affected modules in ${expectedLang}.`);
222
357
  }
223
- STOP - User must fix before proceeding
224
358
  }
225
359
  ```
226
360
 
@@ -259,6 +393,234 @@ Display("Global Entity Count: {count}")
259
393
  Display("Global Relationships: {count}")
260
394
  ```
261
395
 
396
+ ### 5a. Cross-Module Contracts (MANDATORY)
397
+
398
+ > **Generate explicit contracts for every cross-module FK dependency.**
399
+ > These contracts ensure both producer and consumer modules agree on the data shape.
400
+ > They prevent the #1 cross-module integration failure: wrong field type/name/nullability.
401
+
402
+ ```javascript
403
+ const crossModuleContracts = [];
404
+
405
+ for (const rel of globalRelationships) {
406
+ // Only for cross-module relationships
407
+ if (rel.sourceModule !== rel.targetModule) {
408
+ // Resolve the FK field from the source entity's attributes
409
+ const sourceEntity = findEntity(rel.sourceModule, rel.sourceEntity);
410
+ const fkField = sourceEntity.attributes.find(a =>
411
+ a.name.toLowerCase() === (rel.targetEntity.toLowerCase() + 'id') ||
412
+ a.description?.includes('FK vers ' + rel.targetEntity)
413
+ );
414
+
415
+ crossModuleContracts.push({
416
+ consumer: `${rel.sourceModule}.${rel.sourceEntity}`,
417
+ provider: `${rel.targetModule}.${rel.targetEntity}`,
418
+ field: fkField?.name || `${rel.targetEntity.charAt(0).toLowerCase() + rel.targetEntity.slice(1)}Id`,
419
+ type: fkField?.type || "guid",
420
+ required: fkField?.required || false,
421
+ nullable: !fkField?.required,
422
+ resolvedVia: `FK → ${rel.targetEntity}.Id`,
423
+ cardinality: rel.cardinality,
424
+ cascadeDelete: rel.cascadeDelete,
425
+ description: `${rel.sourceEntity} references ${rel.targetEntity} from ${rel.targetModule}`
426
+ });
427
+ }
428
+ }
429
+ ```
430
+
431
+ Display:
432
+ ```
433
+ ### Contrats cross-module ({crossModuleContracts.length})
434
+
435
+ | Consumer | Provider | FK Field | Type | Required | Cascade |
436
+ |----------|----------|----------|------|----------|---------|
437
+ {for each contract: | {consumer} | {provider} | {field} | {type} | {required} | {cascadeDelete} |}
438
+ ```
439
+
440
+ Store in `consolidation.json` as `crossModuleContracts[]`.
441
+
442
+ > These contracts are consumed by `/business-analyse-handoff` and `/business-analyse-develop`
443
+ > to ensure correct FK generation, DTO mapping, and navigation property creation across modules.
444
+
445
+ ### 5b. Mermaid Diagram Generation (MANDATORY)
446
+
447
+ > Generate 3 types of Mermaid diagrams as text, stored in consolidation.json.
448
+ > These diagrams are rendered in the interactive HTML and exported with handoff.
449
+
450
+ #### 5b-i. ERD (Entity-Relationship Diagram)
451
+
452
+ From `globalEntities` and `globalRelationships` built in section 5, generate Mermaid ERD:
453
+
454
+ ```javascript
455
+ let erd = "erDiagram\n";
456
+ for (const entity of globalEntities) {
457
+ erd += ` ${entity.name} {\n`;
458
+ for (const attr of entity.topAttributes) { // top 5-8 attributes
459
+ erd += ` ${attr.type} ${attr.name}\n`;
460
+ }
461
+ erd += " }\n";
462
+ }
463
+ for (const rel of globalRelationships) {
464
+ const card = rel.cardinality === "1:N" ? "||--o{" :
465
+ rel.cardinality === "N:1" ? "}o--||" :
466
+ rel.cardinality === "N:M" ? "}o--o{" : "||--||";
467
+ erd += ` ${rel.sourceEntity} ${card} ${rel.targetEntity} : "${rel.description}"\n`;
468
+ }
469
+ ```
470
+
471
+ Store as `consolidation.mermaidDiagrams.erd` (string).
472
+
473
+ #### 5b-ii. State Machine Diagrams (per entity lifecycle)
474
+
475
+ For EACH `lifeCycle` found in all modules' `usecases.json`:
476
+
477
+ ```javascript
478
+ let sm = "stateDiagram-v2\n";
479
+ sm += ` [*] --> ${lifecycle.initialState}\n`;
480
+ for (const transition of lifecycle.transitions) {
481
+ sm += ` ${transition.from} --> ${transition.to} : ${transition.action}\n`;
482
+ }
483
+ for (const state of lifecycle.states.filter(s => s.isTerminal)) {
484
+ sm += ` ${state.id} --> [*]\n`;
485
+ }
486
+ ```
487
+
488
+ Store as `consolidation.mermaidDiagrams.stateMachines[{entity}]` (string).
489
+
490
+ #### 5b-iii. Sequence Diagrams (per E2E flow)
491
+
492
+ For EACH e2eFlow identified in section 6:
493
+
494
+ ```javascript
495
+ let seq = "sequenceDiagram\n";
496
+ for (const step of flow.steps) {
497
+ seq += ` ${step.actor || step.module}->>+${step.targetModule || "System"}: ${step.action}\n`;
498
+ if (step.result) {
499
+ seq += ` ${step.targetModule || "System"}-->>-${step.actor || step.module}: ${step.result}\n`;
500
+ }
501
+ }
502
+ ```
503
+
504
+ Store as `consolidation.mermaidDiagrams.sequences[{flowName}]` (string).
505
+
506
+ #### 5b-iv. Use Case Diagrams (per module)
507
+
508
+ For EACH module, generate a Mermaid use case diagram showing actors and their interactions:
509
+
510
+ ```javascript
511
+ const useCaseDiagrams = {};
512
+
513
+ for (const module of completedModules) {
514
+ const usecases = module.useCases || module.usecases || [];
515
+ if (usecases.length === 0) continue;
516
+
517
+ // Collect unique actors
518
+ const actors = [...new Set(usecases.map(uc => uc.primaryActor || uc.actor).filter(Boolean))];
519
+
520
+ // Build flowchart-based UC diagram (Mermaid does not have native usecase syntax)
521
+ let diagram = "flowchart LR\n";
522
+
523
+ // Add actors (left side)
524
+ actors.forEach((actor, i) => {
525
+ const actorId = 'actor_' + actor.replace(/[^a-zA-Z0-9]/g, '_');
526
+ diagram += ` ${actorId}[/"👤 ${actor}"\\]\n`;
527
+ diagram += ` style ${actorId} fill:#e1f5fe,stroke:#0288d1,color:#01579b\n`;
528
+ });
529
+
530
+ // Group UCs by section
531
+ const sections = [...new Set(usecases.map(uc => uc.sectionCode).filter(Boolean))];
532
+
533
+ sections.forEach(section => {
534
+ const sectionUCs = usecases.filter(uc => uc.sectionCode === section);
535
+ diagram += ` subgraph ${section}["${section}"]\n`;
536
+ sectionUCs.forEach(uc => {
537
+ const ucId = (uc.id || uc.name).replace(/[^a-zA-Z0-9]/g, '_');
538
+ diagram += ` ${ucId}(("${uc.name}"))\n`;
539
+ });
540
+ diagram += ` end\n`;
541
+ });
542
+
543
+ // UCs without section
544
+ const noSectionUCs = usecases.filter(uc => !uc.sectionCode);
545
+ noSectionUCs.forEach(uc => {
546
+ const ucId = (uc.id || uc.name).replace(/[^a-zA-Z0-9]/g, '_');
547
+ diagram += ` ${ucId}(("${uc.name}"))\n`;
548
+ });
549
+
550
+ // Connect actors to UCs
551
+ usecases.forEach(uc => {
552
+ const actor = uc.primaryActor || uc.actor;
553
+ if (!actor) return;
554
+ const actorId = 'actor_' + actor.replace(/[^a-zA-Z0-9]/g, '_');
555
+ const ucId = (uc.id || uc.name).replace(/[^a-zA-Z0-9]/g, '_');
556
+ diagram += ` ${actorId} --> ${ucId}\n`;
557
+ });
558
+
559
+ useCaseDiagrams[module.code] = diagram;
560
+ }
561
+ ```
562
+
563
+ Store as `consolidation.mermaidDiagrams.useCases` (object: moduleCode → diagram string).
564
+
565
+ #### 5b-v. MCD (Modèle Conceptuel de Données)
566
+
567
+ Generate a simplified conceptual data model showing ONLY entity names and named relationships with cardinalities. Unlike the ERD which shows all attributes, the MCD focuses on the conceptual structure.
568
+
569
+ ```javascript
570
+ let mcd = "erDiagram\n";
571
+
572
+ // Entities: name + PK only (no attributes)
573
+ for (const ent of globalEntities) {
574
+ mcd += ` ${ent.name} {\n`;
575
+ mcd += ` ${ent.pk || 'guid'} Id PK\n`;
576
+ mcd += ` }\n`;
577
+ }
578
+
579
+ // Named relationships with cardinalities
580
+ for (const rel of globalRelationships) {
581
+ const card = rel.cardinality === "1:N" ? "||--o{" :
582
+ rel.cardinality === "N:1" ? "}o--||" :
583
+ rel.cardinality === "N:M" ? "}o--o{" :
584
+ rel.cardinality === "1:1" ? "||--||" : "||--o{";
585
+
586
+ // Use description as relationship label, or derive from cardinality
587
+ const label = rel.description ||
588
+ (rel.cardinality === "1:N" ? "contient" :
589
+ rel.cardinality === "N:1" ? "appartient" : "associe");
590
+
591
+ mcd += ` ${rel.sourceEntity} ${card} ${rel.targetEntity} : "${label}"\n`;
592
+ }
593
+ ```
594
+
595
+ Store as `consolidation.mermaidDiagrams.mcd` (string).
596
+
597
+ #### 5b-vi. Storage
598
+
599
+ Write to `consolidation.json`:
600
+
601
+ ```json
602
+ {
603
+ "mermaidDiagrams": {
604
+ "erd": "erDiagram\n Employee {\n guid id\n string code\n ...\n }\n ...",
605
+ "mcd": "erDiagram\n Employee {\n guid Id PK\n }\n Employee ||--o{ Contract : \"contient\"\n ...",
606
+ "useCases": {
607
+ "Employees": "flowchart LR\n actor_RH[/\"👤 RH Admin\"\\]\n ...",
608
+ "Absences": "flowchart LR\n ..."
609
+ },
610
+ "stateMachines": {
611
+ "Absence": "stateDiagram-v2\n [*] --> Draft\n Draft --> Submitted : submit\n ...",
612
+ "Invoice": "stateDiagram-v2\n [*] --> Draft\n ..."
613
+ },
614
+ "sequences": {
615
+ "CycleCommercial": "sequenceDiagram\n Commercial->>+CRM: Creer opportunite\n ...",
616
+ "CycleFacturation": "sequenceDiagram\n ..."
617
+ }
618
+ }
619
+ }
620
+ ```
621
+
622
+ Display diagrams as markdown Mermaid code blocks for the client to preview before approval (section 9).
623
+
262
624
  ### 6. E2E Flow Validation
263
625
 
264
626
  Identify business processes spanning multiple modules:
@@ -334,172 +696,15 @@ Les critères d'acceptation sont écrits dans `validation.json` au même niveau
334
696
 
335
697
  ### 7ter. Naming Audit (MANDATORY)
336
698
 
337
- > **Challenge all names before final approval.** Every code, label, and route generated
338
- > during the BA must be reviewed for coherence, convention compliance, and clarity.
699
+ **Load** `references/04-naming-audit-checks.md` for all naming validation rules and user interaction flows.
339
700
 
340
- **7ter-a. Collect all names from the BA:**
341
-
342
- ```javascript
343
- const namingRegistry = {
344
- application: {
345
- label: application_name,
346
- code: applicationCode, // PascalCase
347
- route: toKebabCase(applicationCode) // kebab-case
348
- },
349
- modules: completedModules.map(m => ({
350
- label: m.name,
351
- code: m.code, // PascalCase
352
- route: toKebabCase(m.code) // kebab-case
353
- })),
354
- entities: completedModules.flatMap(m =>
355
- m.entities.map(e => ({
356
- name: e.name, // PascalCase (C# class name)
357
- module: m.code,
358
- tableName: e.tableName || pluralize(e.name) // Plural convention
359
- }))
360
- ),
361
- permissionRoots: [...new Set(
362
- permissionPaths.map(p => p.path.split('.').slice(0, 2).join('.'))
363
- )]
364
- };
365
- ```
366
-
367
- **7ter-b. Display naming recap table:**
368
-
369
- ```
370
- ═══════════════════════════════════════════════════════════════
371
- NAMING AUDIT — {application_name}
372
- ═══════════════════════════════════════════════════════════════
373
-
374
- APPLICATION
375
- | Label | Code (PascalCase) | Route (kebab-case) |
376
- |-------|-------------------|--------------------|
377
- | {application_name} | {applicationCode} | /{route} |
378
-
379
- MODULES
380
- | # | Label | Code (PascalCase) | Route (kebab-case) |
381
- |---|-------|-------------------|--------------------|
382
- {for each module: index | label | code | /app-route/module-route}
383
-
384
- ENTITIES
385
- | Entity (PascalCase) | Module | Table (plural) |
386
- |----------------------|--------|----------------|
387
- {for each entity: name | module | tableName}
388
-
389
- PERMISSION ROOTS
390
- {for each root: path}
391
- ═══════════════════════════════════════════════════════════════
392
- ```
393
-
394
- **7ter-c. Coherence checks:**
395
-
396
- ```javascript
397
- const namingIssues = [];
398
-
399
- // 1. Duplicate entity names across modules
400
- const entityNames = namingRegistry.entities.map(e => e.name);
401
- const duplicates = entityNames.filter((n, i) => entityNames.indexOf(n) !== i);
402
- if (duplicates.length > 0) {
403
- namingIssues.push({ severity: "ERROR", issue: `Duplicate entity names: ${[...new Set(duplicates)].join(', ')}` });
404
- }
405
-
406
- // 2. Module code vs label coherence (code should derive logically from label)
407
- for (const mod of namingRegistry.modules) {
408
- if (!mod.code || mod.code.length < 2) {
409
- namingIssues.push({ severity: "ERROR", issue: `Module "${mod.label}" has invalid code: "${mod.code}"` });
410
- }
411
- }
412
-
413
- // 3. Permission root alignment with module codes
414
- for (const root of namingRegistry.permissionRoots) {
415
- const [appSegment, moduleSegment] = root.split('.');
416
- const matchingModule = namingRegistry.modules.find(m =>
417
- m.code.toLowerCase() === moduleSegment.toLowerCase()
418
- );
419
- if (!matchingModule) {
420
- namingIssues.push({ severity: "WARNING", issue: `Permission root "${root}" has no matching module code` });
421
- }
422
- }
423
-
424
- // 4. Route collision detection
425
- const allRoutes = namingRegistry.modules.map(m =>
426
- `/${namingRegistry.application.route}/${m.route}`
427
- );
428
- const routeDuplicates = allRoutes.filter((r, i) => allRoutes.indexOf(r) !== i);
429
- if (routeDuplicates.length > 0) {
430
- namingIssues.push({ severity: "ERROR", issue: `Route collisions: ${routeDuplicates.join(', ')}` });
431
- }
432
- ```
433
-
434
- **7ter-d. MCP validation:**
435
-
436
- ```
437
- mcp__smartstack__validate_conventions({
438
- checks: ["tables"],
439
- context: {
440
- applicationCode: applicationCode,
441
- modules: namingRegistry.modules.map(m => m.code),
442
- entities: namingRegistry.entities.map(e => e.name)
443
- }
444
- })
445
-
446
- → Merge MCP findings into namingIssues[]
447
- ```
448
-
449
- **7ter-e. Present findings and confirm:**
450
-
451
- ```
452
- IF namingIssues.length > 0:
453
- Display issues table:
454
- | # | Severity | Issue |
455
- |---|----------|-------|
456
- {for each issue}
457
- ```
458
-
459
- Ask via AskUserQuestion:
460
- ```
461
- question: "{language == 'fr'
462
- ? 'Validez-vous les noms ci-dessus pour l\\'ensemble de l\\'application ?'
463
- : 'Do you approve all the names above for the entire application?'}"
464
- header: "Naming Audit"
465
- options:
466
- - label: "{language == 'fr' ? 'Approuvé' : 'Approved'}"
467
- description: "{language == 'fr' ? 'Tous les noms sont corrects' : 'All names are correct'}"
468
- - label: "{language == 'fr' ? 'Renommer certains éléments' : 'Rename some elements'}"
469
- description: "{language == 'fr' ? 'Corriger des noms avant de finaliser' : 'Fix names before finalizing'}"
470
- ```
471
-
472
- **IF "Renommer certains éléments":**
473
-
474
- Ask via AskUserQuestion (open-ended):
475
- ```
476
- question: "{language == 'fr'
477
- ? 'Quels éléments souhaitez-vous renommer ? (ex: \"Module Ventes → Commerce\", \"Entity Invoice → BillingDocument\")'
478
- : 'Which elements do you want to rename? (e.g., \"Module Sales → Commerce\", \"Entity Invoice → BillingDocument\")'}"
479
- header: "Renaming"
480
- ```
481
-
482
- Process user response:
483
- 1. Parse rename instructions
484
- 2. Update `applicationCode`, module codes, entity names, permission paths accordingly in JSON files via ba-writer
485
- 3. Re-run 7ter-c and 7ter-d checks on updated names
486
- 4. Re-display the naming recap table for final confirmation
487
-
488
- **IF "Approuvé":**
489
- → Store naming audit result in `validation.json`:
490
- ```javascript
491
- ba-writer.enrichSection({
492
- featureId: {feature_id},
493
- section: "namingAudit",
494
- data: {
495
- auditedAt: now(),
496
- issues: namingIssues,
497
- approved: true,
498
- renames: [] // or list of applied renames
499
- }
500
- });
501
- ```
502
- → Continue to section 8
701
+ This includes:
702
+ - Collect all application, module, entity, and permission names into a registry
703
+ - Display naming recap table (application code, module routes, entity tables, permission roots)
704
+ - Execute coherence checks (duplicate entities, route collisions, permission root alignment)
705
+ - Run MCP `validate_conventions` on entity/module names
706
+ - Present findings via AskUserQuestion with approval and rename options
707
+ - Process rename instructions if needed, re-validate, and continue to section 8
503
708
 
504
709
  ### 8. Consolidation Summary Display
505
710
 
@@ -537,16 +742,10 @@ RISKS
537
742
 
538
743
  ### 9. Client Approval (BLOCKING)
539
744
 
540
- **Single-Module Mode:** Auto-approve (no cross-module concerns)
745
+ **Toujours demander l'approbation** meme pour un module unique, jouer l'avocat du diable :
746
+ proposer au moins 1 cas non mentionne ou 1 question de completude avant validation.
541
747
 
542
- ```
543
- IF completedModules.length === 1:
544
- Display: "Module unique - consolidation automatique ✓"
545
- approval = { approved: true, reason: "Single module", approvedAt: now() }
546
- SKIP to section 10
547
- ```
548
-
549
- **Multi-Module Mode:** Ask via AskUserQuestion:
748
+ Ask via AskUserQuestion:
550
749
 
551
750
  ```
552
751
  question: "La consolidation est complète. Validez-vous l'ensemble ?"
@@ -568,6 +767,14 @@ IF "Modifier les interactions":
568
767
 
569
768
  ### 10. Write Consolidation Data
570
769
 
770
+ → **Load** `references/04-file-allocation.md` for strict data allocation rules.
771
+
772
+ **MANDATORY:** Data must be written to correct files:
773
+ - `consolidation.json`: crossModuleInteractions, sharedEntities, permissionCoherence, e2eFlows, decision
774
+ - `validation.json`: semanticChecks, contractChecks, namingAudit, acceptanceCriteria, globalRiskAssessment
775
+
776
+ Mixing contents across files = BLOCKING ERROR.
777
+
571
778
  ```javascript
572
779
  ba-writer.enrichSection({
573
780
  featureId: {feature_id},
@@ -594,10 +801,6 @@ ba-writer.enrichSection({
594
801
  warnings: []
595
802
  },
596
803
  e2eFlows: e2eFlows,
597
- globalRiskAssessment: risks,
598
- semanticChecks: [
599
- { check: "permission-orpheline", status: "PASS|WARNING", details: "..." }
600
- ],
601
804
  decision: {
602
805
  approved: approval.approved,
603
806
  reason: approval.reason,
@@ -607,6 +810,34 @@ ba-writer.enrichSection({
607
810
  }
608
811
  });
609
812
 
813
+ // Write validation.json — quality/audit data for downstream skills (C1)
814
+ ba-writer.enrichSection({
815
+ featureId: {feature_id},
816
+ section: "validation",
817
+ data: {
818
+ semanticChecks: semanticChecks.map(check => ({
819
+ check: check.check,
820
+ status: errors.some(e => e.check === check.check) ? "ERROR"
821
+ : warnings.some(w => w.check === check.check) ? "WARNING" : "PASS",
822
+ details: [...errors, ...warnings]
823
+ .filter(r => r.check === check.check)
824
+ .map(r => `[${r.module}] ${r.details}`)
825
+ })),
826
+ contractChecks: [...errors, ...warnings].filter(r =>
827
+ ["br-calc-sans-formula", "computed-sans-br-calc", "uc-count-critical",
828
+ "uc-count-low", "permission-count-low", "missing-versioned-attributes"].includes(r.check)
829
+ ),
830
+ namingAudit: {
831
+ auditedAt: now(),
832
+ issues: namingIssues,
833
+ approved: true,
834
+ renames: []
835
+ },
836
+ acceptanceCriteria: acceptanceCriteria, // from section 7bis
837
+ globalRiskAssessment: risks // from section 7
838
+ }
839
+ });
840
+
610
841
  // Add changelog entry
611
842
  ba-writer.enrichSection({
612
843
  featureId: {feature_id},
@@ -681,7 +912,7 @@ BA workflow complete. Next steps:
681
912
  - ✓ Permission coherence validated
682
913
  - ✓ Semantic checks: 0 errors
683
914
  - ✓ Naming audit completed and approved
684
- - ✓ Client approval obtained (or auto-approved for single module)
915
+ - ✓ Client approval obtained
685
916
  - ✓ Consolidation section written to validation.json
686
917
  - ✓ Status updated to "consolidated"
687
918
  - ✓ Workflow state saved for resume support