@atlashub/smartstack-cli 4.75.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 (54) hide show
  1. package/dist/index.js +87 -41
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/templates/skills/apex/SKILL.md +2 -2
  5. package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
  6. package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
  7. package/templates/skills/apex/references/core-seed-data.md +20 -18
  8. package/templates/skills/apex/references/post-checks.md +18 -1
  9. package/templates/skills/apex/references/smartstack-api.md +4 -4
  10. package/templates/skills/apex/references/smartstack-frontend.md +1 -1
  11. package/templates/skills/apex/references/smartstack-layers.md +6 -6
  12. package/templates/skills/apex/steps/step-00-init.md +1 -1
  13. package/templates/skills/apex/steps/step-03b-layer1-seed.md +26 -0
  14. package/templates/skills/apex/steps/step-03d-layer3-frontend.md +124 -2
  15. package/templates/skills/apex/steps/step-04-examine.md +163 -0
  16. package/templates/skills/apex-verify/SKILL.md +110 -0
  17. package/templates/skills/apex-verify/references/audit-rules.md +50 -0
  18. package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
  19. package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
  20. package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
  21. package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
  22. package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
  23. package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
  24. package/templates/skills/application/templates-frontend.md +2 -2
  25. package/templates/skills/business-analyse/SKILL.md +3 -3
  26. package/templates/skills/business-analyse/_shared.md +37 -0
  27. package/templates/skills/business-analyse/references/03-json-schemas.md +11 -3
  28. package/templates/skills/business-analyse/references/03-post-check-validation.md +64 -0
  29. package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
  30. package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
  31. package/templates/skills/business-analyse/references/validation-checklist.md +5 -5
  32. package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +15 -4
  33. package/templates/skills/business-analyse/steps/step-03-specify.md +162 -4
  34. package/templates/skills/business-analyse/steps/step-04-consolidate.md +211 -1
  35. package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +3 -0
  36. package/templates/skills/business-analyse-html/html/ba-interactive.html +198 -16
  37. package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +64 -0
  38. package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
  39. package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
  40. package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +6 -3
  41. package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +46 -0
  42. package/templates/skills/business-analyse-html/references/02-feature-data-building.md +4 -2
  43. package/templates/skills/business-analyse-html/references/data-build.md +2 -0
  44. package/templates/skills/business-analyse-html/references/data-mapping.md +88 -21
  45. package/templates/skills/business-analyse-html/steps/step-02-build-data.md +6 -0
  46. package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
  47. package/templates/skills/business-analyse-quick/SKILL.md +807 -0
  48. package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
  49. package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
  50. package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
  51. package/templates/skills/dev-start/SKILL.md +7 -7
  52. package/templates/skills/sketch/SKILL.md +15 -153
  53. package/templates/skills/ui-components/SKILL.md +1 -1
  54. package/templates/skills/ui-components/patterns/data-table.md +1 -1
@@ -31,6 +31,26 @@ function resolveModuleDir(appCode, moduleCode) {
31
31
  > **Module directory (ba-006):** `mod.dir = resolveModuleDir(applicationCode, module.code)`
32
32
  > Example: `docs/projet-rh/v1.0/human-resources/employees/`
33
33
 
34
+ ## ENFORCEMENT LANGUE (OBLIGATOIRE)
35
+
36
+ Toutes les valeurs textuelles générées dans les fichiers JSON de ce module DOIVENT être rédigées en `{language}` :
37
+
38
+ | Champ JSON | Langue | Exemple FR | Exemple EN |
39
+ |------------|--------|-----------|-----------|
40
+ | entity.description | `{language}` | "Représente un employé" | "Represents an employee" |
41
+ | attribute.description | `{language}` | "Code unique de l'employé" | "Unique employee code" |
42
+ | rule.statement | `{language}` | "Le code doit être unique" | "Code must be unique" |
43
+ | rule.examples[].input | `{language}` | "code='EMP-001', doublon" | "code='EMP-001', duplicate" |
44
+ | rule.examples[].expected | `{language}` | "Erreur : code déjà existant" | "Error: code already exists" |
45
+ | usecase.name | `{language}` | "Créer un employé" | "Create employee" |
46
+ | usecase.mainScenario[] | `{language}` | "L'utilisateur ouvre la page" | "User opens the page" |
47
+ | usecase.preconditions[] | `{language}` | "L'utilisateur est connecté" | "User is logged in" |
48
+ | permission.description | `{language}` | "Consulter la liste" | "View the list" |
49
+
50
+ **Identifiants techniques** (PascalCase) restent en anglais : `Employee`, `AbsenceBalance`, `status`, `BR-VAL-EMP-001`.
51
+
52
+ **VIOLATION = contenu rejeté à la consolidation (step-04).**
53
+
34
54
  ## Prerequisites
35
55
 
36
56
  - Step 02 (structure) completed
@@ -82,6 +102,11 @@ Display(`Scope locked: ${expectedApps} app(s), ${confirmedModules.length} module
82
102
  | - INTERDICTION de spawner des ba-writer pour des modules NON encore |
83
103
  | valides par le client |
84
104
  | |
105
+ | 6. JAMAIS "accelerer" en batchant des modules dans des sub-agents. |
106
+ | Sub-agents (Agent/Snipper) n'ont PAS acces au schema JSON canonique. |
107
+ | PREUVE: audit ba-012 → 4 dialectes JSON, conformite 95% → 35%. |
108
+ | Si > 10 modules: maintenir le sequentiel, le retravail coute plus. |
109
+ | |
85
110
  +==============================================================================+
86
111
 
87
112
  ## Sequential Execution
@@ -349,9 +374,12 @@ IF module has workflow states OR approval patterns:
349
374
  - BR-VAL-ABS-008: "Medical certificate required for absences > 3 days" [domainSpecific]
350
375
  - BR-SEC-ABS-001: "Manager cannot approve their own absences" [domainSpecific]
351
376
 
352
- ELSE (non-workflow module):
377
+ ELSE (non-workflow module with >= 2 entities):
378
+ SHOULD WebSearch: "{module_domain} data validation rules best practices"
353
379
  → Use entity patterns from A-bis for validation rules
354
380
  → Load archetype rules from module-completeness-challenge.md
381
+ → For each archetype rule relevant to this module type, ADD to proposal
382
+ → Mark rules from research with domainSpecific: true
355
383
  ```
356
384
 
357
385
  ### C-bis. Client Validation — Business Rules (OBLIGATOIRE)
@@ -363,13 +391,63 @@ ELSE (non-workflow module):
363
391
  - Patterns domaine (A-bis) : validations standards, calculs attendus
364
392
  - Cadrage : errorFlows, processus décrits
365
393
 
394
+ > **Schema:** ALL fields defined in `schemas/sections/analysis-schema.json` property `businessRules`. Use EXACT canonical field names (`id`, `name`, `category`, `statement`, `severity`, `entities`, `examples`, `sectionCode`, `domainSpecific`).
395
+
366
396
  **ENRICHISSEMENT OBLIGATOIRE pour chaque règle :**
367
397
 
368
398
  Pour chaque BR proposée, inclure ces champs :
369
399
  - `severity` : TOUJOURS spécifier (blocking/warning/info)
370
400
  - `sectionCode` : TOUJOURS spécifier (section où la règle s'applique)
371
- - `examples[]` : OBLIGATOIRE — au moins 1 exemple structuré `{input, expected}`
372
- Ex: `{ input: "debut=15/03 fin=12/03", expected: "Erreur : date de fin avant date de debut" }`
401
+ - `examples[]` : OBLIGATOIRE — format **test-ready** `{scenario, description, given, when, then}`
402
+ Chaque exemple est un **jeu de test** avec des valeurs concrètes (pas de prose).
403
+ Minimum **2 exemples** par règle : `happy_path` + `error` (ou `calculation` + `boundary`).
404
+
405
+ **Format par catégorie :**
406
+ - `validation` :
407
+ ```json
408
+ { "scenario": "happy_path", "description": "Code unique accepté",
409
+ "given": { "Employee.code": "EMP-00042", "existingCodes": [] },
410
+ "when": "create",
411
+ "then": { "result": "success" } }
412
+ { "scenario": "error", "description": "Code dupliqué rejeté",
413
+ "given": { "Employee.code": "EMP-00001", "existingCodes": ["EMP-00001"] },
414
+ "when": "create",
415
+ "then": { "result": "error", "message": "Code déjà existant", "field": "code" } }
416
+ ```
417
+ - `calculation` :
418
+ ```json
419
+ { "scenario": "calculation", "description": "Solde avec report",
420
+ "given": { "AbsenceBalance.entitled": 25, "AbsenceBalance.carriedForward": 3, "AbsenceBalance.taken": 12 },
421
+ "when": "calculate_remaining",
422
+ "then": { "AbsenceBalance.remaining": 16, "formula": "25 + 3 - 12 = 16" } }
423
+ { "scenario": "boundary", "description": "Solde à zéro",
424
+ "given": { "AbsenceBalance.entitled": 10, "AbsenceBalance.carriedForward": 0, "AbsenceBalance.taken": 10 },
425
+ "when": "calculate_remaining",
426
+ "then": { "AbsenceBalance.remaining": 0 } }
427
+ ```
428
+ - `workflow` :
429
+ ```json
430
+ { "scenario": "happy_path", "description": "Transition Draft→Submitted",
431
+ "given": { "Absence.status": "Draft" },
432
+ "when": "submit",
433
+ "then": { "Absence.status": "Submitted", "result": "success" } }
434
+ { "scenario": "error", "description": "Transition interdite Approved→Draft",
435
+ "given": { "Absence.status": "Approved" },
436
+ "when": "submit",
437
+ "then": { "result": "error", "message": "Transition non autorisée" } }
438
+ ```
439
+ - `security` :
440
+ ```json
441
+ { "scenario": "happy_path", "description": "Admin accède à toutes les fiches",
442
+ "given": { "user.role": "RH Admin", "Employee.departmentId": "any" },
443
+ "when": "read",
444
+ "then": { "result": "success", "scope": "all" } }
445
+ { "scenario": "error", "description": "Employé accède uniquement à sa fiche",
446
+ "given": { "user.role": "Employé", "target.employeeId": "other-user" },
447
+ "when": "read",
448
+ "then": { "result": "error", "message": "Accès limité à votre fiche" } }
449
+ ```
450
+ **Fallback** : `{input, expected}` accepté mais marqué WARNING "format non test-ready".
373
451
  - `conditions[]` : OBLIGATOIRE pour les règles conditionnelles (IF/THEN) — structuré `{entity, field, operator, value}`
374
452
  Ex: `[{ entity: "Absence", field: "type", operator: "==", value: "Maladie" }, { entity: "Absence", field: "duration", operator: ">", value: 3 }]`
375
453
  - `consequences[]` : OBLIGATOIRE pour les règles avec effets de bord
@@ -378,9 +456,20 @@ ELSE (non-workflow module):
378
456
  - `domainSpecific: true` si la règle vient de la recherche domaine (C-pre)
379
457
 
380
458
  **Minimums par module métier (hors Portal/config) :**
381
- - >= 4 règles avec `examples[]` structurés `{input, expected}`
459
+ - 100% des règles avec `examples[]` non vide (minimum 2 par règle : happy_path + error)
460
+ - >= 4 règles avec exemples test-ready `{scenario, given, when, then}`
382
461
  - >= 2 règles avec `conditions[]` structurés
383
462
  - >= 1 règle avec `consequences[]`
463
+ - Toute BR-CALC avec un exemple `calculation` contenant des valeurs numériques chiffrées
464
+
465
+ **Self-check AVANT présentation client :**
466
+ ```
467
+ domainCount = rules.filter(r => r.domainSpecific === true).length
468
+ IF domainCount < 2 AND module is NOT config/lookup/Portal:
469
+ SELF-CHECK: "Only {domainCount} domain-specific rules."
470
+ → Re-examine module-completeness-challenge.md archetypes for this module type
471
+ → Add at least 2 domain-specific rules before presenting to client
472
+ ```
384
473
 
385
474
  2. **Présenter le LOT complet** (markdown PUIS AskUserQuestion) :
386
475
  ```
@@ -398,6 +487,60 @@ ELSE (non-workflow module):
398
487
  - Q3.8 si les relations inter-champs sont complexes
399
488
  - Q3.9 si les données sensibles ne sont pas identifiées
400
489
 
490
+ 4. **POST-VALIDATION : Qualité test-ready des exemples (OBLIGATOIRE)**
491
+
492
+ Après validation client des règles métier, vérifier la qualité des exemples AVANT écriture :
493
+
494
+ ```javascript
495
+ // 1. Règles sans aucun exemple
496
+ const rulesWithoutExamples = module.rules.filter(r =>
497
+ !r.examples || r.examples.length === 0
498
+ );
499
+ if (rulesWithoutExamples.length > 0) {
500
+ // Auto-générer des exemples test-ready à partir du statement et des conditions
501
+ for (const rule of rulesWithoutExamples) {
502
+ rule.examples = generateTestReadyExamples(rule);
503
+ // Génère minimum: 1 happy_path + 1 error avec given/when/then
504
+ }
505
+ Display(`${rulesWithoutExamples.length} règle(s) complétée(s) avec exemples test-ready auto-générés.`);
506
+ }
507
+
508
+ // 2. Règles avec exemples en prose (ancien format {input, expected} sans given/when/then)
509
+ const proseExamples = module.rules.filter(r =>
510
+ r.examples?.length > 0 && !r.examples.some(e => e.given && e.when && e.then)
511
+ );
512
+ if (proseExamples.length > 0) {
513
+ // Convertir les exemples prose en format test-ready
514
+ for (const rule of proseExamples) {
515
+ rule.examples = rule.examples.map(e => convertToTestReady(e, rule));
516
+ }
517
+ Display(`${proseExamples.length} règle(s) converties du format prose au format test-ready.`);
518
+ }
519
+
520
+ // 3. BR-CALC sans exemple chiffré
521
+ const calcWithoutNumbers = module.rules.filter(r =>
522
+ r.category === 'calculation' &&
523
+ !r.examples?.some(e => e.scenario === 'calculation' || (e.then && Object.values(e.then).some(v => typeof v === 'number')))
524
+ );
525
+ if (calcWithoutNumbers.length > 0) {
526
+ Display(`⚠ ${calcWithoutNumbers.length} BR-CALC sans exemple chiffré — ajouter des valeurs numériques.`);
527
+ }
528
+
529
+ // 4. Vérification minimale
530
+ const testReady = module.rules.filter(r =>
531
+ r.examples?.some(e => e.given && e.when && e.then)
532
+ );
533
+ if (testReady.length < 4 && module.featureType !== 'portal') {
534
+ Display(`⚠ ${testReady.length}/4 règles au format test-ready {given, when, then}. Enrichir.`);
535
+ }
536
+ ```
537
+
538
+ **Minimum requis par module métier (hors Portal/config) :**
539
+ - 100% des règles avec `examples[]` non vide (minimum 2 par règle)
540
+ - >= 4 règles avec exemples test-ready `{scenario, given, when, then}`
541
+ - Toute BR-CALC avec un exemple `calculation` contenant des valeurs numériques
542
+ - Zéro exemple en prose vague (tout doit être en format `given/when/then`)
543
+
401
544
  INTERDICTION de passer à D (Use Cases) tant que le client n'a pas validé les règles.
402
545
 
403
546
  ### D. Use Cases
@@ -767,6 +910,21 @@ For EACH apiEndpoint in module:
767
910
 
768
911
  **criture :** Stocker dans `usecases.json` sous la cl `apiEndpoints[]` (enrichi).
769
912
 
913
+ ### H-pre. Schema Refresh (OBLIGATOIRE avant chaque ecriture)
914
+
915
+ AVANT d'ecrire les JSON pour le module courant :
916
+ → **Relire** `references/03-json-schemas.md` section "Business Rules Schema Format"
917
+ → Verifier que CHAQUE rule dans le buffer utilise les champs canoniques :
918
+ - `id` (pas `code`), `name` (pas `label`), `statement` (pas `description`/`rule`),
919
+ `category` (pas `type`), `severity` (blocking/warning/info, pas Error/error),
920
+ `examples[]` (array d'`{input, expected}`, pas string), `entities[]`, `sectionCode`
921
+ → SI un champ deprecated est detecte : corriger AVANT ecriture
922
+ → Ce refresh prend ~30 secondes et previent 100% des derives de schema
923
+
924
+ **POURQUOI ce refresh :** Apres 5+ modules, le contexte est compacte et les exemples
925
+ JSON du debut de conversation sont perdus. Ce refresh garantit que chaque module
926
+ est ecrit avec le format canonique, meme si le contexte a ete compresse.
927
+
770
928
  ### H. Write & Advance (SEULEMENT apres validation complete)
771
929
 
772
930
  > **GATE:** ba-writer ne peut etre lance QUE si le client a valide :
@@ -244,6 +244,120 @@ This includes:
244
244
  - 5 contract verification checks (BR formulas, computed attributes, UC counts, permission counts, versioned attributes)
245
245
  - Blocking rule: 0 ERROR → PASS, ≥1 ERROR → BLOCK (user must fix before proceeding)
246
246
 
247
+ **BR-EXAMPLES-COMPLETE check — qualité test-ready (NEW):**
248
+ ```javascript
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
+ );
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 = [];
327
+ for (const module of completedModules) {
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) });
333
+ }
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
+ });
349
+ }
350
+
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}.`);
357
+ }
358
+ }
359
+ ```
360
+
247
361
  ### 5. Data Model Consolidation
248
362
 
249
363
  Generate global entity relationship diagram:
@@ -389,7 +503,98 @@ for (const step of flow.steps) {
389
503
 
390
504
  Store as `consolidation.mermaidDiagrams.sequences[{flowName}]` (string).
391
505
 
392
- #### 5b-iv. Storage
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
393
598
 
394
599
  Write to `consolidation.json`:
395
600
 
@@ -397,6 +602,11 @@ Write to `consolidation.json`:
397
602
  {
398
603
  "mermaidDiagrams": {
399
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
+ },
400
610
  "stateMachines": {
401
611
  "Absence": "stateDiagram-v2\n [*] --> Draft\n Draft --> Submitted : submit\n ...",
402
612
  "Invoice": "stateDiagram-v2\n [*] --> Draft\n ..."
@@ -47,6 +47,9 @@ When reading flat files, prefer canonical keys but fall back to alternatives:
47
47
  - `primaryActor`: `uc.primaryActor || uc.actor`
48
48
  - `mainScenario`: `uc.mainScenario || uc.steps` (if `steps[]` contains objects, extract `.action`)
49
49
  - `rules`: `data.rules || data.businessRules || []`
50
+ - `rules[].examples`: prefer `rule.examples[]` (`{input, expected}`); fallback: wrap `rule.example` string as `[{input: rule.example, expected: ""}]`
51
+ - `rules[].statement`: prefer `rule.statement`; fallback: `rule.description`
52
+ - `rules[].id`: prefer `rule.id`; fallback: `rule.code`
50
53
  - `screens`: `data.sections || data.screens` (if `screens[]` exists, treat each screen as a section with 1 resource)
51
54
 
52
55
  This safety net handles pre-4.52 BA outputs. It should become unnecessary once step-03-specify enforces canonical keys.