@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.
- package/dist/index.js +87 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/skills/apex/SKILL.md +2 -2
- package/templates/skills/apex/references/checks/frontend-checks.sh +26 -0
- package/templates/skills/apex/references/checks/seed-checks.sh +47 -7
- package/templates/skills/apex/references/core-seed-data.md +20 -18
- package/templates/skills/apex/references/post-checks.md +18 -1
- package/templates/skills/apex/references/smartstack-api.md +4 -4
- package/templates/skills/apex/references/smartstack-frontend.md +1 -1
- package/templates/skills/apex/references/smartstack-layers.md +6 -6
- package/templates/skills/apex/steps/step-00-init.md +1 -1
- package/templates/skills/apex/steps/step-03b-layer1-seed.md +26 -0
- package/templates/skills/apex/steps/step-03d-layer3-frontend.md +124 -2
- package/templates/skills/apex/steps/step-04-examine.md +163 -0
- package/templates/skills/apex-verify/SKILL.md +110 -0
- package/templates/skills/apex-verify/references/audit-rules.md +50 -0
- package/templates/skills/apex-verify/steps/step-00-init.md +119 -0
- package/templates/skills/apex-verify/steps/step-01-nav-audit.md +92 -0
- package/templates/skills/apex-verify/steps/step-02-crud-audit.md +127 -0
- package/templates/skills/apex-verify/steps/step-03-perm-audit.md +119 -0
- package/templates/skills/apex-verify/steps/step-04-route-audit.md +98 -0
- package/templates/skills/apex-verify/steps/step-05-report.md +110 -0
- package/templates/skills/application/templates-frontend.md +2 -2
- package/templates/skills/business-analyse/SKILL.md +3 -3
- package/templates/skills/business-analyse/_shared.md +37 -0
- package/templates/skills/business-analyse/references/03-json-schemas.md +11 -3
- package/templates/skills/business-analyse/references/03-post-check-validation.md +64 -0
- package/templates/skills/business-analyse/references/canonical-json-formats.md +7 -3
- package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
- package/templates/skills/business-analyse/references/validation-checklist.md +5 -5
- package/templates/skills/business-analyse/schemas/sections/analysis-schema.json +15 -4
- package/templates/skills/business-analyse/steps/step-03-specify.md +162 -4
- package/templates/skills/business-analyse/steps/step-04-consolidate.md +211 -1
- package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md +3 -0
- package/templates/skills/business-analyse-html/html/ba-interactive.html +198 -16
- package/templates/skills/business-analyse-html/html/src/scripts/01-data-init.js +64 -0
- package/templates/skills/business-analyse-html/html/src/scripts/05-render-specs.js +80 -11
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-consolidation.js +2 -2
- package/templates/skills/business-analyse-html/html/src/scripts/06-render-mockups.js +6 -3
- package/templates/skills/business-analyse-html/html/src/scripts/12-render-diagrams.js +46 -0
- package/templates/skills/business-analyse-html/references/02-feature-data-building.md +4 -2
- package/templates/skills/business-analyse-html/references/data-build.md +2 -0
- package/templates/skills/business-analyse-html/references/data-mapping.md +88 -21
- package/templates/skills/business-analyse-html/steps/step-02-build-data.md +6 -0
- package/templates/skills/business-analyse-html/steps/step-04-verify.md +92 -3
- package/templates/skills/business-analyse-quick/SKILL.md +807 -0
- package/templates/skills/{sketch → business-analyse-quick}/references/domain-heuristics.md +59 -3
- package/templates/skills/business-analyse-quick/references/prd-schema.md +268 -0
- package/templates/skills/business-analyse-review/references/review-data-mapping.md +6 -0
- package/templates/skills/dev-start/SKILL.md +7 -7
- package/templates/skills/sketch/SKILL.md +15 -153
- package/templates/skills/ui-components/SKILL.md +1 -1
- 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 —
|
|
372
|
-
|
|
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
|
-
-
|
|
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.
|
|
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 ..."
|
package/templates/skills/business-analyse-handoff/references/agent-handoff-transform-prompt.md
CHANGED
|
@@ -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.
|