@atlashub/smartstack-cli 3.1.0 → 3.2.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.
@@ -1071,8 +1071,7 @@ moduleSpecs[moduleCode] = {
1071
1071
  })),
1072
1072
  permissions: buildPermissionKeys(moduleFeature), // see below
1073
1073
  notes: "",
1074
- mockupNotes: (moduleFeature.specification?.uiWireframes || [])
1075
- .map(w => "[" + w.screen + "]\n" + w.mockup).join("\n\n")
1074
+ mockupNotes: "" // Deprecated: wireframes now embedded separately in EMBEDDED_ARTIFACTS
1076
1075
  }
1077
1076
  ```
1078
1077
 
@@ -1110,18 +1109,113 @@ Default: "daily"
1110
1109
  Default: "contributor"
1111
1110
  ```
1112
1111
 
1112
+ #### Step 2-bis: Build EMBEDDED_ARTIFACTS object (NEW)
1113
+
1114
+ > **NEW in v6.2:** Visual artifacts (wireframes, E2E diagrams) are now embedded as a separate JavaScript object in the HTML for client-side rendering and export.
1115
+
1116
+ Build a JSON object containing ALL visual artifacts from feature.json:
1117
+
1118
+ ```javascript
1119
+ {
1120
+ wireframes: {
1121
+ // FOR EACH module: extract all wireframes from module feature.json
1122
+ // [moduleCode]: [ {screen, format, content, elements, componentMapping, layout, description} ]
1123
+ },
1124
+ e2eFlows: [
1125
+ // Extract from master consolidation.e2eFlows[]
1126
+ // { name, diagram, steps, actors }
1127
+ ],
1128
+ dependencyGraph: {
1129
+ // Extract from master dependencyGraph
1130
+ // nodes: [ {id, label, type} ], edges: [ {from, to, description} ]
1131
+ }
1132
+ }
1133
+ ```
1134
+
1135
+ **Wireframes mapping** — for EACH module in `master.modules[]`:
1136
+
1137
+ ```javascript
1138
+ // Read the module feature.json
1139
+ const moduleFeature = readModuleFeature(moduleCode);
1140
+ const wireframes = (moduleFeature.specification?.uiWireframes || []).map(wf => ({
1141
+ screen: wf.screen, // e.g. "UM-list", "UM-form"
1142
+ section: wf.section, // e.g. "list", "form"
1143
+ format: wf.mockupFormat || "ascii", // "ascii" | "svg"
1144
+ content: wf.mockup, // ASCII art or SVG markup
1145
+ description: wf.description || "",
1146
+ elements: wf.elements || [], // ["DataGrid", "FilterBar", ...]
1147
+ actions: wf.actions || [], // ["filter", "sort", "create", ...]
1148
+ componentMapping: wf.componentMapping || [], // [{ wireframeElement, reactComponent }]
1149
+ layout: wf.layout || null, // { type, regions: [...] }
1150
+ permissionsRequired: wf.permissionsRequired || []
1151
+ }));
1152
+
1153
+ // Store in artifacts object
1154
+ EMBEDDED_ARTIFACTS.wireframes[moduleCode] = wireframes;
1155
+ ```
1156
+
1157
+ **E2E flows mapping** — from master consolidation:
1158
+
1159
+ ```javascript
1160
+ EMBEDDED_ARTIFACTS.e2eFlows = (master.consolidation?.e2eFlows || []).map(flow => ({
1161
+ name: flow.name,
1162
+ diagram: generateE2EDiagram(flow), // ASCII diagram from flow.steps[]
1163
+ steps: flow.steps || [], // [{ module, action, permission }]
1164
+ actors: Array.from(new Set((flow.steps || [])
1165
+ .map(s => s.permission?.split(".")[0]) // Extract role from permission path
1166
+ .filter(Boolean))).join(", "),
1167
+ modules: Array.from(new Set((flow.steps || [])
1168
+ .map(s => s.module))).join(" → ")
1169
+ }));
1170
+
1171
+ function generateE2EDiagram(flow) {
1172
+ // Generate ASCII diagram from flow.steps[]
1173
+ // Example output:
1174
+ // "Customer ──[read]──→ Order ──[create]──→ Invoice ──[send]──→"
1175
+ // " (Orders) (Orders) (Invoices)"
1176
+ // " Contributor Manager System"
1177
+
1178
+ const stepDiagrams = (flow.steps || []).map(s =>
1179
+ `${s.action}(${s.module})`
1180
+ ).join(" ──→ ");
1181
+
1182
+ return stepDiagrams;
1183
+ }
1184
+ ```
1185
+
1186
+ **Dependency graph mapping** — from master dependencyGraph:
1187
+
1188
+ ```javascript
1189
+ EMBEDDED_ARTIFACTS.dependencyGraph = {
1190
+ nodes: (master.modules || []).map(m => ({
1191
+ id: m.code,
1192
+ label: m.code,
1193
+ type: m.featureType || "data-centric"
1194
+ })),
1195
+ edges: (master.dependencyGraph?.edges || []).map(e => ({
1196
+ from: e.from,
1197
+ to: e.to,
1198
+ description: e.description || ""
1199
+ }))
1200
+ };
1201
+ ```
1202
+
1113
1203
  #### Step 3: Replace placeholders in template
1114
1204
 
1115
1205
  1. Serialize the FEATURE_DATA object as JSON (with 2-space indentation for readability)
1116
- 2. Replace `{{FEATURE_DATA}}` with the serialized JSON
1117
- 3. Replace `{{APPLICATION_NAME}}` `{application_name}` (still used in `<title>` and header)
1118
- 4. Replace `{{APPLICATION_ID}}` `{feature_id}` (still used in `APP_KEY`)
1119
- 5. Replace `{{VERSION}}` → `{version}`
1120
- 6. Replace `{{CREATED_AT}}` → `{ISO timestamp}`
1206
+ 2. Serialize the EMBEDDED_ARTIFACTS object as JSON (with 2-space indentation)
1207
+ 3. Replace `{{FEATURE_DATA}}` with the serialized FEATURE_DATA JSON
1208
+ 4. Replace `{{EMBEDDED_ARTIFACTS}}` with the serialized EMBEDDED_ARTIFACTS JSON
1209
+ 5. Replace `{{APPLICATION_NAME}}` → `{application_name}` (still used in `<title>` and header)
1210
+ 6. Replace `{{APPLICATION_ID}}` → `{feature_id}` (still used in `APP_KEY`)
1211
+ 7. Replace `{{VERSION}}` → `{version}`
1212
+ 8. Replace `{{CREATED_AT}}` → `{ISO timestamp}`
1121
1213
 
1122
1214
  > **NOTE:** `{{APPLICATION_NAME}}`, `{{APPLICATION_ID}}`, `{{VERSION}}`, `{{CREATED_AT}}` still appear
1123
1215
  > in the HTML body (`<title>`, header, `APP_KEY`). They MUST be replaced separately from FEATURE_DATA.
1124
1216
 
1217
+ > **NEW:** `{{EMBEDDED_ARTIFACTS}}` is a separate JavaScript variable in the HTML that stores all visual artifacts (wireframes, E2E diagrams, dependency graph) for client-side rendering and export.
1218
+
1125
1219
  #### Step 4: Write and confirm
1126
1220
 
1127
1221
  1. Write the populated HTML to the output directory
@@ -1132,6 +1226,7 @@ Default: "contributor"
1132
1226
  Path: docs/business/{app}/business-analyse/v{version}/ba-interactive.html
1133
1227
  Pre-populated with: {stakeholder_count} stakeholders, {module_count} modules,
1134
1228
  {total_uc} use cases, {total_br} business rules, {total_entity} entities
1229
+ Visual artifacts: {total_wireframes} wireframes, {e2e_flow_count} E2E diagrams
1135
1230
  Open in browser to review and edit the business analysis.
1136
1231
  Export JSON and re-import with: /business-analyse -x <exported-json-path>
1137
1232
  ```
@@ -1241,7 +1336,7 @@ options:
1241
1336
  - label: "Feature Full (Recommandé)"
1242
1337
  description: "Génération parallèle rapide (code + tests). Couverture 70-80%. ~{hours/3} heures."
1243
1338
  - label: "Ralph Loop"
1244
- description: "Développement task-driven séquentiel. Couverture 95-100%. ~{hours} heures."
1339
+ description: "Développement itératif task-par-task avec cycle : Analyse → Dev (backend + tests unitaires + tests non-régression + frontend + tests + documentation) → Validation → Correction → Test ... jusqu'à 100% tests pass. Couverture 95-100%. ~{hours} heures."
1245
1340
  - label: "Terminer le BA"
1246
1341
  description: "Finir l'analyse, développement manuel par l'équipe."
1247
1342
  ```
@@ -1253,6 +1348,179 @@ options:
1253
1348
 
1254
1349
  ---
1255
1350
 
1351
+ ### 10-bis. Execute User Choice (Automatic Skill Launch)
1352
+
1353
+ > **NEW in v6.1:** Automatically launch the chosen development approach for seamless transition.
1354
+
1355
+ **After receiving AskUserQuestion response:**
1356
+
1357
+ ```javascript
1358
+ const choice = userAnswer; // "Feature Full (Recommandé)" | "Ralph Loop" | "Terminer le BA"
1359
+
1360
+ // Extract choice label (remove "(Recommandé)" suffix)
1361
+ const choiceLabel = choice.replace(/\s*\(.*?\)\s*$/g, '').trim();
1362
+
1363
+ if (choiceLabel === "Ralph Loop") {
1364
+ // Launch ralph-loop skill automatically
1365
+ display("");
1366
+ display("🚀 Lancement de Ralph Loop - Développement itératif automatique");
1367
+ display("");
1368
+ display("╔══════════════════════════════════════════════════════════════╗");
1369
+ display("║ CYCLE RALPH LOOP - Comment ça fonctionne ? ║");
1370
+ display("╠══════════════════════════════════════════════════════════════╣");
1371
+ display("║ ║");
1372
+ display("║ Ralph Loop exécute un cycle itératif jusqu'à 100% tests: ║");
1373
+ display("║ ║");
1374
+ display("║ 1️⃣ ANALYSE → Charger task suivante du prd.json ║");
1375
+ display("║ 2️⃣ DÉVELOPPEMENT → Générer le code demandé ║");
1376
+ display("║ • Backend (Entities, Services, Controllers, Repos) ║");
1377
+ display("║ • Tests unitaires (xUnit) + non-régression ║");
1378
+ display("║ • Frontend (Pages, Components, Hooks) ║");
1379
+ display("║ • Tests frontend (React Testing Library) ║");
1380
+ display("║ • SeedData (Core RBAC + business data) ║");
1381
+ display("║ • Documentation utilisateur (inline + tooltips) ║");
1382
+ display("║ 3️⃣ VALIDATION → Commit + MCP conventions check ║");
1383
+ display("║ 4️⃣ TEST → Exécuter dotnet test + npm test ║");
1384
+ display("║ 5️⃣ CORRECTION → Si échec, analyser et corriger ║");
1385
+ display("║ 6️⃣ BOUCLE → Répéter 4-5 jusqu'à 100% tests pass ║");
1386
+ display("║ 7️⃣ NEXT TASK → Passer à la task suivante (retour à 1) ║");
1387
+ display("║ ║");
1388
+ display("║ 🎯 Objectif: ZÉRO ERREUR avant de passer à la task suivante║");
1389
+ display("║ 📊 Couverture: 95-100% (tests générés automatiquement) ║");
1390
+ display("║ ║");
1391
+ display("╚══════════════════════════════════════════════════════════════╝");
1392
+ display("");
1393
+ display(" Configuration du projet:");
1394
+ display(" ┌────────────────────────────────────────────────────────────┐");
1395
+ display(" │ Modules: " + modules.map(m => m.code).join(", "));
1396
+ display(" │ Stratégie: " + implementationStrategy);
1397
+ display(" │ PRD: " + (modules.length > 1 ? ".ralph/prd-{module}.json (per module)" : ".ralph/prd.json"));
1398
+ display(" │ Progress: .ralph/progress.txt");
1399
+ display(" │ Ordre: " + (modules.length > 1 ? "topologique (dépendances d'abord)" : "module unique"));
1400
+ display(" └────────────────────────────────────────────────────────────┘");
1401
+ display("");
1402
+ display(" Modules à traiter (dans l'ordre):");
1403
+ for (let i = 0; i < modules.length; i++) {
1404
+ display(" " + (i+1) + ". " + modules[i].code + " (" + modules[i].complexity + ") - " + modules[i].tasksCount + " tasks");
1405
+ }
1406
+ display("");
1407
+ display(" 📂 Fichiers générés par le BA (inputs pour Ralph):");
1408
+ display(" ├─ feature.json (master + modules) → Spécification source");
1409
+ display(" ├─ prd.json (ou prd-{module}.json) → Task breakdown avec UC/FR/BR");
1410
+ display(" ├─ progress.txt → Tracker hiérarchique (module → layer → tasks)");
1411
+ display(" └─ ba-interactive.html → Revue client (mockups, wireframes)");
1412
+ display("");
1413
+ display(" 🔄 Ralph Loop va maintenant:");
1414
+ display(" 1. Détecter les prd-*.json par module (si multi-module)");
1415
+ display(" 2. Créer modules-queue.json avec l'ordre de traitement");
1416
+ display(" 3. Traiter module par module dans l'ordre topologique");
1417
+ display(" 4. Pour chaque module: parcourir les tasks (domain → seeddata → application → infrastructure → api → frontend → i18n → tests)");
1418
+ display(" 5. Pour chaque task: générer code → commit → tests → correction si échec → re-test → next task");
1419
+ display(" 6. Passer au module suivant quand 100% tasks du module actuel = completed");
1420
+ display(" 7. Générer rapport final avec métriques de couverture");
1421
+ display("");
1422
+ display("═══════════════════════════════════════════════════════════════");
1423
+ display("");
1424
+ display("➡️ Transition vers Ralph Loop...");
1425
+ display("");
1426
+
1427
+ // Call the ralph-loop skill (no arguments needed - prd.json exists)
1428
+ Skill({ skill: "ralph-loop" });
1429
+
1430
+ // EXIT - ralph-loop takes over
1431
+ // The skill does NOT return here - ralph-loop continues in a new context
1432
+
1433
+ } else if (choiceLabel === "Feature Full") {
1434
+ // Launch feature-full skill automatically
1435
+ display("");
1436
+ display("🚀 Lancement de Feature Full...");
1437
+ display("");
1438
+ display(" Configuration:");
1439
+ display(" - Mode: Parallel generation");
1440
+ display(" - Couverture: 70-80%");
1441
+ display(" - Durée estimée: ~" + Math.round(totalHours / 3) + " heures");
1442
+ display("");
1443
+ display("═══════════════════════════════════════════════════════════════");
1444
+ display("");
1445
+
1446
+ Skill({ skill: "feature-full" });
1447
+
1448
+ // EXIT
1449
+
1450
+ } else {
1451
+ // "Terminer le BA" - no skill launch, end gracefully
1452
+ display("");
1453
+ display("✅ Business Analysis terminée.");
1454
+ display("");
1455
+ display(" Les équipes peuvent commencer le développement manuel.");
1456
+ display(" Tous les artefacts sont prêts pour l'implémentation.");
1457
+ display("");
1458
+ display("📂 Fichiers générés:");
1459
+ display(" - feature.json (master + modules) - Spécification complète");
1460
+ display(" - .ralph/prd.json (ou prd-{module}.json) - Task breakdown");
1461
+ display(" - .ralph/progress.txt - Tracker de progression");
1462
+ display(" - ba-interactive.html - Document de revue client");
1463
+ display("");
1464
+ display("📊 Métriques:");
1465
+ display(" - Modules: " + modules.length);
1466
+ display(" - Entités: " + totalEntities);
1467
+ display(" - Use cases: " + totalUseCases);
1468
+ display(" - Business rules: " + totalBusinessRules);
1469
+ display(" - Fichiers à créer: " + totalFiles);
1470
+ display("");
1471
+ display("🎯 Prochaines étapes recommandées:");
1472
+ display(" 1. Ouvrir ba-interactive.html dans le navigateur");
1473
+ display(" 2. Partager avec les stakeholders pour validation finale");
1474
+ display(" 3. Utiliser progress.txt comme guide de développement");
1475
+ display(" 4. Implémenter module par module selon l'ordre topologique");
1476
+ display("");
1477
+ display("💡 Pour lancer le développement assisté plus tard:");
1478
+ display(" - Ralph Loop: /ralph-loop (détecte automatiquement .ralph/prd.json)");
1479
+ display(" - Feature Full: /feature-full");
1480
+ display("");
1481
+ display("═══════════════════════════════════════════════════════════════");
1482
+
1483
+ // EXIT gracefully - BA workflow complete
1484
+ }
1485
+ ```
1486
+
1487
+ **Key behaviors:**
1488
+
1489
+ 1. **Ralph Loop launch:**
1490
+ - No arguments needed (prd.json already exists)
1491
+ - Ralph automatically detects single vs multi-module
1492
+ - Ralph reads modules-queue.json if multi-module
1493
+ - Seamless transition - user sees continuous flow
1494
+
1495
+ 2. **Feature Full launch:**
1496
+ - Passes application context
1497
+ - Parallel generation starts immediately
1498
+
1499
+ 3. **Manual development:**
1500
+ - Clear summary of generated artifacts
1501
+ - Actionable next steps
1502
+ - Instructions for later skill launch
1503
+
1504
+ **Error handling:**
1505
+
1506
+ ```javascript
1507
+ try {
1508
+ Skill({ skill: "ralph-loop" });
1509
+ } catch (error) {
1510
+ display("⚠️ Échec du lancement automatique de Ralph Loop");
1511
+ display("");
1512
+ display(" Veuillez lancer manuellement:");
1513
+ display(" /ralph-loop");
1514
+ display("");
1515
+ display(" Si le problème persiste:");
1516
+ display(" 1. Vérifier que la skill ralph-loop est installée");
1517
+ display(" 2. Vérifier les permissions Claude Code");
1518
+ display(" 3. Consulter les logs: .ralph/logs/");
1519
+ }
1520
+ ```
1521
+
1522
+ ---
1523
+
1256
1524
  ## MODE SUPPORT
1257
1525
 
1258
1526
  ### Standard Mode
@@ -67,13 +67,27 @@ Lire le fichier passe en argument : {extract_path}
67
67
  "businessRules": [{ "id": "", "name": "", "category": "", "statement": "", "example": "" }],
68
68
  "entities": [{ "name": "", "description": "", "attributes": [{ "name": "", "description": "" }], "relationships": [] }],
69
69
  "permissions": [],
70
- "notes": "",
71
- "mockupNotes": ""
70
+ "notes": ""
72
71
  }
73
72
  },
74
73
  "consolidation": {
75
74
  "interactions": [],
76
- "e2eFlows": [{ "name": "", "steps": [{ "module": "", "action": "" }], "actors": "" }]
75
+ "e2eFlows": [{ "name": "", "steps": [{ "module": "", "action": "" }], "actors": "", "diagram": "" }]
76
+ },
77
+ "artifacts": {
78
+ "wireframes": {
79
+ "{moduleCode}": [
80
+ { "screen": "", "section": "", "format": "ascii|svg", "content": "", "description": "",
81
+ "elements": [], "actions": [], "componentMapping": [], "layout": {}, "permissionsRequired": [] }
82
+ ]
83
+ },
84
+ "e2eFlows": [
85
+ { "name": "", "diagram": "", "steps": [], "actors": "", "modules": "" }
86
+ ],
87
+ "dependencyGraph": {
88
+ "nodes": [{ "id": "", "label": "", "type": "" }],
89
+ "edges": [{ "from": "", "to": "", "description": "" }]
90
+ }
77
91
  }
78
92
  }
79
93
  ```
@@ -407,6 +421,113 @@ ba-writer.enrichSection({
407
421
  ba-writer.updateStatus({feature_id}, "consolidated")
408
422
  ```
409
423
 
424
+ ### 6-bis. Extraire et reconstituer les artefacts visuels (NEW)
425
+
426
+ > **NEW in v6.2:** Visual artifacts (wireframes, E2E diagrams, dependency graph) are now extracted from the exported JSON and reconstituted into feature.json.
427
+
428
+ **IF** `artifacts` exists in exported JSON:
429
+
430
+ #### Extract wireframes per module
431
+
432
+ Pour chaque module dans `artifacts.wireframes`:
433
+
434
+ ```
435
+ FOR EACH moduleCode IN artifacts.wireframes:
436
+ moduleWireframes = artifacts.wireframes[moduleCode]
437
+
438
+ // Locate the module feature.json
439
+ moduleFeature = ba-reader.findModuleFeature({feature_id}, moduleCode)
440
+
441
+ // Map wireframes to specification.uiWireframes[]
442
+ uiWireframes = moduleWireframes.map(wf => ({
443
+ "screen": wf.screen,
444
+ "section": wf.section,
445
+ "mockupFormat": wf.format,
446
+ "mockup": wf.content,
447
+ "description": wf.description || "",
448
+ "elements": wf.elements || [],
449
+ "actions": wf.actions || [],
450
+ "permissionsRequired": wf.permissionsRequired || [],
451
+ "componentMapping": wf.componentMapping || [],
452
+ "layout": wf.layout || null
453
+ }))
454
+
455
+ // Write to module feature.json (enriching specification section)
456
+ ba-writer.enrichSection({
457
+ featureId: moduleFeature.id,
458
+ section: "specification",
459
+ data: { uiWireframes: uiWireframes }
460
+ })
461
+ ```
462
+
463
+ **Validation:** For each wireframe:
464
+ - `screen` must be unique within module
465
+ - `mockupFormat` must be "ascii" or "svg"
466
+ - `mockup` content must not be empty
467
+ - `componentMapping` array must have at least one entry
468
+ - `layout` object must have `type` and `regions` if present
469
+
470
+ #### Extract E2E flow diagrams
471
+
472
+ IF `artifacts.e2eFlows` exists:
473
+
474
+ ```
475
+ e2eFlows = artifacts.e2eFlows.map(flow => ({
476
+ "name": flow.name,
477
+ "modules": flow.modules.split(" → ").map(m => m.trim()), // "OrderManagement → Invoicing" → ["OrderManagement", "Invoicing"]
478
+ "steps": flow.steps || [],
479
+ "diagram": flow.diagram, // NEW: ASCII diagram preserved
480
+ "actors": flow.actors.split(", ").map(a => a.trim())
481
+ }))
482
+
483
+ // Merge with existing e2eFlows from consolidation.e2eFlows
484
+ ba-writer.enrichSection({
485
+ featureId: {feature_id},
486
+ section: "consolidation",
487
+ data: { e2eFlows: e2eFlows }
488
+ })
489
+ ```
490
+
491
+ **Validation:** For each E2E flow:
492
+ - `diagram` must not be empty if present
493
+ - `modules` array must match modules defined in master feature.json
494
+ - `steps` must have at least 2 entries (cross-module flow)
495
+
496
+ #### Extract dependency graph (optional)
497
+
498
+ IF `artifacts.dependencyGraph` exists:
499
+
500
+ ```
501
+ dependencyGraph = {
502
+ "nodes": artifacts.dependencyGraph.nodes.map(n => ({
503
+ "id": n.id,
504
+ "label": n.label || n.id,
505
+ "type": n.type || "data-centric"
506
+ })),
507
+ "edges": artifacts.dependencyGraph.edges.map(e => ({
508
+ "from": e.from,
509
+ "to": e.to,
510
+ "description": e.description || ""
511
+ }))
512
+ }
513
+
514
+ // Write to master feature.json
515
+ ba-writer.enrichSection({
516
+ featureId: {feature_id},
517
+ section: "decomposition",
518
+ data: { dependencyGraph: dependencyGraph }
519
+ })
520
+ ```
521
+
522
+ **Post-extraction summary:**
523
+
524
+ ```
525
+ ✓ Artefacts visuels extraits:
526
+ - Wireframes: {total_wireframe_count} across {module_count} modules
527
+ - E2E diagrams: {e2e_flow_count}
528
+ - Dependency graph: {node_count} nodes, {edge_count} edges
529
+ ```
530
+
410
531
  ### 7. Resume de l'extraction
411
532
 
412
533
  Afficher un resume comparatif :
@@ -432,11 +553,18 @@ Afficher un resume comparatif :
432
553
  | Regles metier | {total} | analysis.businessRules |
433
554
  | Entites | {total} | analysis.entities |
434
555
  | Parcours E2E | {count} | consolidation.e2eFlows |
556
+ | Maquettes (wireframes) | {total} | specification.uiWireframes[] |
557
+ | Diagrammes E2E | {count} | consolidation.e2eFlows[].diagram |
435
558
 
436
559
  Fichiers generes :
437
560
  - docs/business/{app}/business-analyse/v1.0/feature.json (master)
438
561
  - docs/business/{app}/{module}/business-analyse/v1.0/feature.json (par module)
439
562
 
563
+ Artefacts visuels preserves :
564
+ - Toutes les maquettes ASCII/SVG validees lors de la specification
565
+ - Tous les diagrammes E2E pour les flux cross-module
566
+ - Graphe de dependances entre modules
567
+
440
568
  ═══════════════════════════════════════════════════════════════
441
569
  ```
442
570