---
name: step-03-specify
description: Sequential deep dive per module - entities, BRs, UCs, permissions
model: opus
next_step: steps/step-04-consolidate.md
---

# Step 03 — Specify (Deep Dive per Module)

## Objective

For each module (in dependency order), produce complete specifications: entities, business rules, use cases, and permissions. Interface specs (screens, wireframes, navigation) are handled by Phase 2 (`/business-analyse-design`).

## Context Recovery

Re-read session variables from `.business-analyse/config.json`:
```javascript
const config = readJSON('.business-analyse/config.json').currentFeature;
const { id: feature_id, docsDir: docs_dir, projectSlug, application: application_name,
        applicationCode, workflowMode: workflow_mode, language } = config;

// Path helpers (ba-006)
function toKebabCase(code) {
  return code.replace(/([a-z0-9])([A-Z])/g, '$1-$2').replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2').toLowerCase();
}
function resolveModuleDir(appCode, moduleCode) {
  return `${docs_dir}/${toKebabCase(appCode)}/${toKebabCase(moduleCode)}`;
}
```

> **Module directory (ba-006):** `mod.dir = resolveModuleDir(applicationCode, module.code)`
> Example: `docs/projet-rh/v1.0/human-resources/employees/`

## ENFORCEMENT LANGUE (OBLIGATOIRE)

Toutes les valeurs textuelles générées dans les fichiers JSON de ce module DOIVENT être rédigées en `{language}` :

| Champ JSON | Langue | Exemple FR | Exemple EN |
|------------|--------|-----------|-----------|
| entity.description | `{language}` | "Représente un employé" | "Represents an employee" |
| attribute.description | `{language}` | "Code unique de l'employé" | "Unique employee code" |
| rule.statement | `{language}` | "Le code doit être unique" | "Code must be unique" |
| rule.examples[].input | `{language}` | "code='EMP-001', doublon" | "code='EMP-001', duplicate" |
| rule.examples[].expected | `{language}` | "Erreur : code déjà existant" | "Error: code already exists" |
| usecase.name | `{language}` | "Créer un employé" | "Create employee" |
| usecase.mainScenario[] | `{language}` | "L'utilisateur ouvre la page" | "User opens the page" |
| usecase.preconditions[] | `{language}` | "L'utilisateur est connecté" | "User is logged in" |
| permission.description | `{language}` | "Consulter la liste" | "View the list" |

**Identifiants techniques** (PascalCase) restent en anglais : `Employee`, `AbsenceBalance`, `status`, `BR-VAL-EMP-001`.

**VIOLATION = contenu rejeté à la consolidation (step-04).**

## Prerequisites

- Step 02 (structure) completed
- Applications, modules, sections, dependencies identified

## Scope Verification (BLOCKING — before any module processing)

Re-read `.business-analyse/config.json` and verify scope has not drifted:

```javascript
const config = readJSON('.business-analyse/config.json').currentFeature;
const expectedApps = config.candidateApplications?.length || 1;
const confirmedModules = /* read from step-02 output: application index.json modules[] */;

// BLOCKING if scope drifted
if (config.workflowMode === "project") {
  const appIndexFiles = GLOB(`${config.docsDir}/*/index.json`);
  if (appIndexFiles.length > expectedApps) {
    BLOCKING_ERROR(`Scope drift: expected ${expectedApps} applications but found ${appIndexFiles.length}`);
  }
}

// Log confirmed scope for traceability
Display(`Scope locked: ${expectedApps} app(s), ${confirmedModules.length} module(s)`);
```

## INTERDICTIONS ABSOLUES (step-03)

+==============================================================================+
|  INTERDICTIONS — Violation = analyse invalide                                |
+==============================================================================+
|                                                                              |
|  1. JAMAIS lancer ba-writer pour un module AVANT validation client           |
|     des entites, regles, use cases ET permissions de ce module.              |
|                                                                              |
|  2. JAMAIS lancer des ba-writer en PARALLELE pour des modules differents.    |
|     Chaque module est traite SEQUENTIELLEMENT avec interaction client.        |
|                                                                              |
|  3. JAMAIS persister des entites, regles, volumetries, filtres              |
|     ou notifications sans VALIDATION CLIENT (confirmation du lot propose).   |
|     L'auto-deduction avec confirmation groupee est une validation valide.    |
|                                                                              |
|  4. JAMAIS sauter la VALIDATION CLIENT meme si le domaine semble "evident". |
|     Auto-deduction + confirmation groupee = OK. Pas de confirmation = KO.    |
|                                                                              |
|  5. SUBAGENT DISCIPLINE :                                                    |
|     - MAX 1 ba-writer par module (ecriture finale apres validation)          |
|     - Total ba-writer pour step-03 = nombre_de_modules (pas plus)            |
|     - INTERDICTION de spawner des ba-writer pour des modules NON encore      |
|       valides par le client                                                  |
|                                                                              |
|  6. JAMAIS "accelerer" en batchant des modules dans des sub-agents.          |
|     Sub-agents (Agent/Snipper) n'ont PAS acces au schema JSON canonique.     |
|     PREUVE: audit ba-012 → 4 dialectes JSON, conformite 95% → 35%.          |
|     Si > 10 modules: maintenir le sequentiel, le retravail coute plus.       |
|                                                                              |
+==============================================================================+

## Sequential Execution

**No team mode.** Each module receives 100% of LLM attention for maximum specification quality.

Process **application by application**, modules in dependency order within each app:

```
IF workflow_mode = "project" (multi-app):
  // Group by application first, then topological sort within each app
  // Cross-app dependencies: if Module A (App1) depends on Module B (App2),
  // App2 must be processed before App1. Adjust application order accordingly.

  applicationOrder = resolveApplicationOrder(modules, dependencies)
  // e.g., [Rh, Crm, GestionDeProjet, Facturation, Portal]

  For EACH application in applicationOrder:
    Display application banner:
    ═══════════════════════════════════════════════════════
      APPLICATION {appIndex}/{appTotal} : {appName}
    ═══════════════════════════════════════════════════════

    appModules = modules.filter(m => m.applicationCode === app.code)
    sortedAppModules = topologicalSort(appModules)

    For EACH module in sortedAppModules:
      specify(module)  // Full cycle: A-bis → A-ter → B → C-pre → C → D → E → F → H

ELSE (single-app):
  For module in topologicalSort(modules):
    specify(module)
```

## Per-Module Specification Loop — STRICT MODE

### VALIDATION MINIMALE (per module — ba-009 fix)

Chaque module metier (hors Portal et config) requiert AU MINIMUM 2 AskUserQuestion :
  1. Entites (B-ter) : "Voici les entites pour {module}. Corrections ?"
  2. Regles + UCs + Permissions (C-bis + E-ter groupes) : "Voici les regles, UCs et permissions pour {module}. Corrections ?"

Exceptions :
- Modules **Portal** (read-only, 0 entites propres) : 1 validation groupee suffit.
- Modules **config/lookup** (< 2 entites) : 1 validation groupee suffit.

**INTERDICTION** de tout bundler en 1 seule question "Module complet, validez-vous ?" pour les modules metier.

For each module (in topological order), execute the complete cycle below.

### Banniere de progression

Afficher au debut de chaque module :
```
═══════════════════════════════════════════════════════════════
  MODULE {index}/{total} : {moduleName} ({applicationName})
═══════════════════════════════════════════════════════════════
```

Execute these sub-steps for the current module:

### A. Context Loading

Load via ba-reader:
- Module's `index.json` (if exists from previous run)
- Application `cadrage.json` for stakeholder context (including `_preAnalysis.domain_research`)
- Completed modules' summaries (compact, <100 lines each)

### A-bis. Module Domain Research (MANDATORY for modules with >= 2 entities)

> **MANDATORY — BLOCKING** pour les modules metier (>= 2 entites).
> SKIP ONLY: Portal (0 entites propres), config (1 entite reference pure).
> Uses domain_research from step-01 as baseline, then does targeted searches for this specific module.
> → **Load** `references/domain-research-playbook.md` Level 2 for per-module search strategy.
> Si WebSearch indisponible: s'appuyer sur `references/module-completeness-challenge.md` comme fallback.

```
IF module is Portal OR (module has exactly 1 config/lookup entity):
  → SKIP WebSearch (use step-01 domain_research only)
ELSE:
  SHOULD WebSearch: "{module_domain} entity data model best practices"
  (e.g., "invoice entity design ERP", "timesheet data model best practices")

IF cadrage._preAnalysis.domain_research exists:
  → Load as baseline complement

OPTIONAL: FOR complex entities (max 1-2 additional searches):
  Query: "{entity_name} entity design pattern {domain}"

  EXTRACT (ULTRATHINK — internal only):
  - Standard attributes for this entity type
  - Common relationships and cardinality
  - Versioned vs. inline data patterns (e.g., salary history)
  - Workflow states if applicable
  - Known anti-patterns to avoid

  CROSS-CHECK against SmartStack conventions (B-bis rules):
  - Would this entity trigger Person Extension Pattern?
  - Are there attributes that should be versioned (salary, rate)?
  - Does this entity already exist in the SmartStack socle DB?
```

> **CRITICAL:** This research informs entity design but does NOT override SmartStack conventions.
> If web research suggests putting firstName on Employee, the B-bis guard still blocks it
> because SmartStack uses auth_Users for personal data.

**TRACK 2 — Section & View Completeness (MANDATORY):**

```
WebSearch: "{module_domain} management system features views screens UX"
(e.g., "absence management system features views screens",
       "invoice management application features views UX")

EXTRACT (ULTRATHINK — internal only):
  - Standard views/sections for this module type
  - Contextual filtered views (open requests, rejected, my X, history)
  - Dashboard/reporting expectations
  - Standard UX patterns (approval queue, batch actions, etc.)

CROSS-CHECK against anticipatedSections from step-02:
  - List sections found in research but NOT in anticipatedSections
  - For each missing section: prepare proposal for A-ter validation
```

### A-ter. Section Completeness Validation (MANDATORY)

> **Ensure this module has all the sections a user would expect.**
> Workflow modules without contextual views (open-requests, rejected, history) are incomplete.
> → **Load** `references/module-completeness-challenge.md` for section archetypes per pattern.

Compare module's `anticipatedSections` against:
1. Domain research (Track 2 above)
2. Module archetype from `module-completeness-challenge.md`
3. Section archetypes per module pattern (workflow vs data-centric)

Display comparison table:
```
### Sections pour {moduleName}

| Section actuelle | Section standard (recherche/archetype) | Statut |
|-----------------|---------------------------------------|--------|
| list            | list                                   | ✓ OK   |
| detail          | detail                                 | ✓ OK   |
| approve         | approve                                | ✓ OK   |
| calendar        | calendar                               | ✓ OK   |
| —               | open-requests                          | ✗ MANQUANTE — vue filtree des demandes en attente |
| —               | rejected                               | ✗ MANQUANTE — vue filtree des demandes rejetees |
| —               | history                                | ✗ MANQUANTE — historique des absences passees |
| —               | my-balance                             | ✗ MANQUANTE — solde personnel de l'employe |
```

AskUserQuestion (multiSelect: true):
```
question: "{language == 'fr'
  ? 'Voici les sections standards trouvees pour un module {moduleType}. Quelles sections manquantes souhaitez-vous ajouter ?'
  : 'Here are the standard sections found for a {moduleType} module. Which missing sections would you like to add?'}"
header: "Sections {moduleName}"
multiSelect: true
options:
  - For each missing section: label: "{section_code}", description: "{description}"
  - label: "{language == 'fr' ? 'Aucune' : 'None'}"
    description: "{language == 'fr' ? 'Garder les sections actuelles' : 'Keep current sections'}"
```

Update `anticipatedSections` with accepted additions. For each added section:
- Determine `sectionType` (functional for filtered views, embedded for dashboards)
- Determine `permissionMode` (read-only for history/reporting, custom for approval queues)
- Add appropriate resources

### B. Entities

For each entity identified in step 02:

> → **Load** `references/03-json-schemas.md` section "Entity Schema Format" for the canonical JSON format.

### B-bis. SmartStack Entity Convention Guards (MANDATORY)

> → **Load** `references/03-smartstack-entity-guards.md` for all convention rules before finalizing each entity.

### B-ter. Client Validation — Entities (OBLIGATOIRE)

**Mode : Auto-déduction + Confirmation groupée**

> L'IA propose les entités basées sur le cadrage + recherche domaine (A-bis).
> Le client valide PAR LOT au lieu de répondre aux questions individuellement.
> Les questions détaillées (Q3.1-Q3.5) ne sont posées QUE si le cadrage est ambigu.

1. **Construire la proposition d'entités** à partir de :
   - `cadrage.json` (coverageMatrix, stakeholders, globalScope)
   - Recherche domaine (A-bis : standard attributes, versioning patterns)
   - Modules déjà spécifiés (dépendances, entités partagées)

2. **Présenter la proposition COMPLÈTE** (markdown PUIS AskUserQuestion) :
   ```
   ### Entités proposées pour {moduleName}

   | Entité | Attributs clés | Relations | Volume estimé |
   |--------|---------------|-----------|---------------|
   | {entity} | {top 5 attrs with flags} | {relations} | {monthly/total} |
   ```
   AskUserQuestion : "Voici les entités que je propose pour {moduleName}. Corrections ?"

3. SI le client corrige → intégrer et re-présenter
4. SI le client valide → continuer vers C

5. **SI le cadrage est ambigu** sur un point spécifique → poser la question ciblée :
   - Q3.1 si les entités ne sont pas claires
   - Q3.5 si la stratégie de code est ambiguë

6. APRES validation entites, AVANT passage aux regles (C) — poser en 2 batches max :

   **Batch recherche/filtres** (Q3.19 + Q3.20) :
   - Q3.19 : "Par quels criteres cherchez-vous un {entite} ? (nom, date, statut, code...)"
   - Q3.20 : "Quels filtres rapides sont indispensables dans la liste ?"

   **RACCOURCI module 3+ :** Proposer "Meme logique de recherche/filtres que {module_precedent} ?"
   → Si le client confirme, copier searchableFields et defaultFilters du module precedent.
   → Sinon, poser les questions normalement.

   **SKIP si:** module Portal (pas d'entites propres) ou module config (filtres evidents : name, code).

INTERDICTION de passer à C (Business Rules) tant que le client n'a pas validé les entités ET les champs recherche/filtres.

### C. Business Rules

For each entity/process, identify rules. **Each rule MUST specify `sectionCode`** matching a code from `anticipatedSections[]`:

> → **Load** `references/03-json-schemas.md` section "Business Rules Schema Format" for the canonical JSON format and all validation requirements.

### C-pre. Business Rules Domain Research (MANDATORY for workflow modules)

> **Domain-specific business rules are what separate a 2/10 analysis from a 9/10 analysis.**
> Generic rules (date coherence, required fields) are the MINIMUM. Domain rules are the VALUE.
> → **Load** `references/module-completeness-challenge.md` for domain-specific rule examples.

```
IF module has workflow states OR approval patterns:
  WebSearch: "{module_domain} business rules constraints validation best practices"
  (e.g., "absence leave management business rules HR constraints",
         "invoice billing business rules validation constraints",
         "CRM opportunity management business rules")

  EXTRACT (ULTRATHINK — internal only):
    - Domain-specific validation rules (NOT just date coherence / required fields)
    - Calculation rules with formulas
    - Workflow constraints (capacity limits, notice periods, deadlines)
    - Edge cases (half-day, probation, carry-over, expiry, partial payments)
    - Regulatory constraints (legal leave types, mandatory approvals, numbering)
    - Separation of duties rules (cannot approve own request)

  CROSS-CHECK proposed rules (from B-ter entity analysis):
    For each rule found in research but NOT already proposed:
      → Add to proposal with source: "domain-research"
      → Category: detected category (VAL, CALC, WF, SEC, DATA)
      → Mark with domainSpecific: true

  Example enrichments for Absences module:
    Current generic rules: date coherence, no overlap, balance check, workflow transitions
    Research-based additions:
    - BR-VAL-ABS-004: "Half-day absence (morning/afternoon)" [domainSpecific]
    - BR-VAL-ABS-005: "Minimum 48h notice for planned absences" [domainSpecific]
    - BR-VAL-ABS-006: "Max 30% of team can be absent simultaneously" [domainSpecific]
    - BR-VAL-ABS-007: "Restricted leave types during probation period" [domainSpecific]
    - BR-DATA-ABS-001: "Carry-over days expire after March 31" [domainSpecific]
    - BR-VAL-ABS-008: "Medical certificate required for absences > 3 days" [domainSpecific]
    - BR-SEC-ABS-001: "Manager cannot approve their own absences" [domainSpecific]

ELSE (non-workflow module with >= 2 entities):
  SHOULD WebSearch: "{module_domain} data validation rules best practices"
  → Use entity patterns from A-bis for validation rules
  → Load archetype rules from module-completeness-challenge.md
  → For each archetype rule relevant to this module type, ADD to proposal
  → Mark rules from research with domainSpecific: true
```

### C-bis. Client Validation — Business Rules (OBLIGATOIRE)

**Mode : Auto-déduction + Confirmation groupée**

1. **Construire les règles métier** à partir de :
   - Entités validées en B-ter (attributs, types, contraintes)
   - Patterns domaine (A-bis) : validations standards, calculs attendus
   - Cadrage : errorFlows, processus décrits

   > **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`).

   **ENRICHISSEMENT OBLIGATOIRE pour chaque règle :**

   Pour chaque BR proposée, inclure ces champs :
   - `severity` : TOUJOURS spécifier (blocking/warning/info)
   - `sectionCode` : TOUJOURS spécifier (section où la règle s'applique)
   - `examples[]` : OBLIGATOIRE — format **test-ready** `{scenario, description, given, when, then}`
     Chaque exemple est un **jeu de test** avec des valeurs concrètes (pas de prose).
     Minimum **2 exemples** par règle : `happy_path` + `error` (ou `calculation` + `boundary`).

     **Format par catégorie :**
     - `validation` :
       ```json
       { "scenario": "happy_path", "description": "Code unique accepté",
         "given": { "Employee.code": "EMP-00042", "existingCodes": [] },
         "when": "create",
         "then": { "result": "success" } }
       { "scenario": "error", "description": "Code dupliqué rejeté",
         "given": { "Employee.code": "EMP-00001", "existingCodes": ["EMP-00001"] },
         "when": "create",
         "then": { "result": "error", "message": "Code déjà existant", "field": "code" } }
       ```
     - `calculation` :
       ```json
       { "scenario": "calculation", "description": "Solde avec report",
         "given": { "AbsenceBalance.entitled": 25, "AbsenceBalance.carriedForward": 3, "AbsenceBalance.taken": 12 },
         "when": "calculate_remaining",
         "then": { "AbsenceBalance.remaining": 16, "formula": "25 + 3 - 12 = 16" } }
       { "scenario": "boundary", "description": "Solde à zéro",
         "given": { "AbsenceBalance.entitled": 10, "AbsenceBalance.carriedForward": 0, "AbsenceBalance.taken": 10 },
         "when": "calculate_remaining",
         "then": { "AbsenceBalance.remaining": 0 } }
       ```
     - `workflow` :
       ```json
       { "scenario": "happy_path", "description": "Transition Draft→Submitted",
         "given": { "Absence.status": "Draft" },
         "when": "submit",
         "then": { "Absence.status": "Submitted", "result": "success" } }
       { "scenario": "error", "description": "Transition interdite Approved→Draft",
         "given": { "Absence.status": "Approved" },
         "when": "submit",
         "then": { "result": "error", "message": "Transition non autorisée" } }
       ```
     - `security` :
       ```json
       { "scenario": "happy_path", "description": "Admin accède à toutes les fiches",
         "given": { "user.role": "RH Admin", "Employee.departmentId": "any" },
         "when": "read",
         "then": { "result": "success", "scope": "all" } }
       { "scenario": "error", "description": "Employé accède uniquement à sa fiche",
         "given": { "user.role": "Employé", "target.employeeId": "other-user" },
         "when": "read",
         "then": { "result": "error", "message": "Accès limité à votre fiche" } }
       ```
     **Fallback** : `{input, expected}` accepté mais marqué WARNING "format non test-ready".
   - `conditions[]` : OBLIGATOIRE pour les règles conditionnelles (IF/THEN) — structuré `{entity, field, operator, value}`
     Ex: `[{ entity: "Absence", field: "type", operator: "==", value: "Maladie" }, { entity: "Absence", field: "duration", operator: ">", value: 3 }]`
   - `consequences[]` : OBLIGATOIRE pour les règles avec effets de bord
     Ex: `[{ type: "notification", target: "RH Manager", description: "Certificat médical requis" }]`
   - `formula` : OBLIGATOIRE pour les BR-CALC
   - `domainSpecific: true` si la règle vient de la recherche domaine (C-pre)

   **Minimums par module métier (hors Portal/config) :**
   - 100% des règles avec `examples[]` non vide (minimum 2 par règle : happy_path + error)
   - >= 4 règles avec exemples test-ready `{scenario, given, when, then}`
   - >= 2 règles avec `conditions[]` structurés
   - >= 1 règle avec `consequences[]`
   - Toute BR-CALC avec un exemple `calculation` contenant des valeurs numériques chiffrées

   **Self-check AVANT présentation client :**
   ```
   domainCount = rules.filter(r => r.domainSpecific === true).length
   IF domainCount < 2 AND module is NOT config/lookup/Portal:
     SELF-CHECK: "Only {domainCount} domain-specific rules."
     → Re-examine module-completeness-challenge.md archetypes for this module type
     → Add at least 2 domain-specific rules before presenting to client
   ```

2. **Présenter le LOT complet** (markdown PUIS AskUserQuestion) :
   ```
   ### Règles métier proposées pour {moduleName}

   | ID | Catégorie | Règle | Sévérité |
   |----|-----------|-------|----------|
   | BR-VAL-... | validation | {statement} | blocking |
   | BR-CALC-... | calculation | {statement} (formula: {formula}) | blocking |
   ```
   AskUserQuestion : "Voici les {N} règles métier pour {moduleName}. Manques/corrections ?"

3. SI le cadrage est ambigu → poser les questions ciblées :
   - Q3.7 si les validations ne sont pas claires
   - Q3.8 si les relations inter-champs sont complexes
   - Q3.9 si les données sensibles ne sont pas identifiées

4. **POST-VALIDATION : Qualité test-ready des exemples (OBLIGATOIRE)**

   Après validation client des règles métier, vérifier la qualité des exemples AVANT écriture :

   ```javascript
   // 1. Règles sans aucun exemple
   const rulesWithoutExamples = module.rules.filter(r =>
     !r.examples || r.examples.length === 0
   );
   if (rulesWithoutExamples.length > 0) {
     // Auto-générer des exemples test-ready à partir du statement et des conditions
     for (const rule of rulesWithoutExamples) {
       rule.examples = generateTestReadyExamples(rule);
       // Génère minimum: 1 happy_path + 1 error avec given/when/then
     }
     Display(`${rulesWithoutExamples.length} règle(s) complétée(s) avec exemples test-ready auto-générés.`);
   }

   // 2. Règles avec exemples en prose (ancien format {input, expected} sans given/when/then)
   const proseExamples = module.rules.filter(r =>
     r.examples?.length > 0 && !r.examples.some(e => e.given && e.when && e.then)
   );
   if (proseExamples.length > 0) {
     // Convertir les exemples prose en format test-ready
     for (const rule of proseExamples) {
       rule.examples = rule.examples.map(e => convertToTestReady(e, rule));
     }
     Display(`${proseExamples.length} règle(s) converties du format prose au format test-ready.`);
   }

   // 3. BR-CALC sans exemple chiffré
   const calcWithoutNumbers = module.rules.filter(r =>
     r.category === 'calculation' &&
     !r.examples?.some(e => e.scenario === 'calculation' || (e.then && Object.values(e.then).some(v => typeof v === 'number')))
   );
   if (calcWithoutNumbers.length > 0) {
     Display(`⚠ ${calcWithoutNumbers.length} BR-CALC sans exemple chiffré — ajouter des valeurs numériques.`);
   }

   // 4. Vérification minimale
   const testReady = module.rules.filter(r =>
     r.examples?.some(e => e.given && e.when && e.then)
   );
   if (testReady.length < 4 && module.featureType !== 'portal') {
     Display(`⚠ ${testReady.length}/4 règles au format test-ready {given, when, then}. Enrichir.`);
   }
   ```

   **Minimum requis par module métier (hors Portal/config) :**
   - 100% des règles avec `examples[]` non vide (minimum 2 par règle)
   - >= 4 règles avec exemples test-ready `{scenario, given, when, then}`
   - Toute BR-CALC avec un exemple `calculation` contenant des valeurs numériques
   - Zéro exemple en prose vague (tout doit être en format `given/when/then`)

INTERDICTION de passer à D (Use Cases) tant que le client n'a pas validé les règles.

### D. Use Cases

> **Note (v3) :** Les questions parcours/decisions/erreurs (ex-Q2.16-Q2.18) sont desormais capturees ICI
> lors de la specification des use cases, et non plus en step-01 cadrage. Cela evite la duplication
> et produit des donnees directement exploitables par module.

**D-pre. Capture du parcours utilisateur (1 question par module si pertinent)**

> Avant de construire les UCs, comprendre le parcours type pour CE module.
> SKIP si le module est un module config/lookup ou Portal (pas de parcours propre).

```
IF module is NOT config/lookup AND NOT Portal:
  AskUserQuestion (1 question):
  "{language == 'fr'
    ? 'Pour le module {moduleName} : décrivez le parcours typique. L\'utilisateur arrive, que fait-il ? Quand quelque chose ne se passe pas comme prévu, que se passe-t-il ?'
    : 'For the {moduleName} module: describe the typical journey. The user arrives, what do they do? When something doesn't go as planned, what happens?'}"

  Record answer as:
  - mainScenario steps → feed UC mainScenario
  - decision points → feed UC alternativeScenarios
  - error cases → feed UC errorScenarios + BR-WF rules
```

For each stakeholder action. **Each use case MUST specify `sectionCode`** matching a code from `anticipatedSections[]` — this links the UC to the screen/page where it happens:

> → **Load** `references/03-json-schemas.md` section "Use Cases Schema Format" for the canonical JSON format.

### E. Permissions

Define the permission matrix:

> → **Load** `references/03-json-schemas.md` section "Permissions Schema Format" for the canonical JSON format.

### E-bis. Notifications et Dashboards (OBLIGATOIRE)

> **Regle : 2 questions max par AskUserQuestion.**

**Batch E-bis-1** — Notifications :
- Q3.21 : "Quels evenements doivent declencher une notification dans {moduleName} ?"
- Q3.22 : "Qui doit etre notifie ? Par quel canal ? (in-app, email)" (conditionnel si Q3.21 = oui)

**Batch E-bis-2** — Dashboards (SI pertinent pour ce module) :
- Q3.14 : "Des tableaux de bord ou indicateurs sont-ils requis ?"
- Q3.15 : "Si oui, quels KPIs afficher ?" (conditionnel si Q3.14 = oui)

### E-ter. Client Validation — Use Cases & Permissions (OBLIGATOIRE)

**Mode : Auto-déduction + Confirmation groupée**

1. **Construire UCs et permissions** à partir des entités + règles validées :
   - CRUD UCs dérivés des entités (Create, Read, Update, Delete)
   - Export UC si cadrage mentionne export/Excel/CSV
   - Import UC si cadrage mentionne import/upload
   - Workflow UCs si entité a des états (enum status)

   **ENRICHISSEMENT OBLIGATOIRE pour chaque Use Case :**

   Pour chaque UC proposé, inclure ces champs :
   - `postconditions[]` : OBLIGATOIRE — effets après le UC
     Ex: `["Absence.status = Approved", "AbsenceBalance.taken += duration", "Notification envoyee a l'employe"]`
   - `errorScenarios[]` : OBLIGATOIRE pour les UCs workflow (>= 1 par UC workflow)
     Ex: `{ name: "Solde insuffisant", steps: ["Systeme affiche le solde restant", "Systeme bloque la soumission", "Employe peut modifier les dates"] }`
   - `mainScenario[]` : MINIMUM 5 steps pour les UCs workflow (3 suffisent pour list/read)
   - `alternativeScenarios[]` : MINIMUM 1 pour les UCs avec branchements

   **Minimums par module métier :**
   - >= 3 UCs avec `errorScenarios[]` non vides
   - >= 4 UCs avec `postconditions[]` non vides
   - Moyenne mainScenario >= 4 steps

2. **Présenter les deux lots en 1 seule interaction** (markdown PUIS AskUserQuestion) :
   ```
   ### Use Cases proposés pour {moduleName}
   | ID | Nom | Acteur | Règles liées |
   ...

   ### Permissions proposées
   | Rôle | Permissions |
   ...
   ```
   AskUserQuestion : "Voici les UCs et permissions pour {moduleName}. Corrections ?"

3. SI le cadrage est ambigu → poser les questions ciblées du lot E-bis (notifications, dashboards)

INTERDICTION de passer à F (Seed Data) / H (Write) tant que le client n'a pas validé use cases ET permissions.

### F. Seed Data pour tables de référence

> **OBLIGATOIRE** : Chaque entité de type "table de référence" (lookup, étapes de pipeline, types) DOIT avoir ses valeurs initiales documentées.

```json
{
  "entity": "OpportunityStage",
  "type": "reference-table",
  "seedData": [
    { "name": "Qualification", "sortOrder": 1, "defaultProbability": 20, "isWon": false, "isLost": false },
    { "name": "Proposition", "sortOrder": 2, "defaultProbability": 50, "isWon": false, "isLost": false },
    { "name": "Négociation", "sortOrder": 3, "defaultProbability": 75, "isWon": false, "isLost": false },
    { "name": "Gagné", "sortOrder": 4, "defaultProbability": 100, "isWon": true, "isLost": false },
    { "name": "Perdu", "sortOrder": 5, "defaultProbability": 0, "isWon": false, "isLost": true }
  ]
}
```

### F-bis. LifeCycle State Machine (MANDATORY for workflow entities)

> Les lifeCycles sont déjà dans le schema (`specification-schema.json` l.317-383).
> Ils doivent être générés pour CHAQUE entité ayant un attribut status de type enum.
> Ces données alimentent les diagrammes d'état Mermaid ET le code généré.

Pour CHAQUE entité avec attribut `type: "enum"` contenant "status" dans ses `options[]` :

  1. Extraire les états depuis `options[]` de l'attribut enum
  2. Identifier les transitions depuis les règles BR-WF-*
  3. Lier chaque transition à :
     - `permission` : depuis permissions.json
     - `guards[]` : BR-VAL-* qui doivent passer avant la transition
     - `effects[]` : BR-NOTIF-* et BR-CALC-* déclenchées par la transition

  Générer le `lifeCycle` complet :
  ```json
  {
    "entity": "Absence",
    "field": "status",
    "initialState": "Draft",
    "states": [
      { "id": "Draft", "displayName": "Brouillon", "color": "gray", "allowedTransitions": ["Submitted"], "isTerminal": false },
      { "id": "Submitted", "displayName": "Soumis", "color": "blue", "allowedTransitions": ["Approved", "Rejected"], "isTerminal": false },
      { "id": "Approved", "displayName": "Approuvé", "color": "green", "allowedTransitions": [], "isTerminal": true },
      { "id": "Rejected", "displayName": "Refusé", "color": "red", "allowedTransitions": [], "isTerminal": true }
    ],
    "transitions": [
      { "from": "Draft", "to": "Submitted", "action": "submit", "permission": "Rh.Absences.Create",
        "guards": ["BR-VAL-ABS-001", "BR-VAL-ABS-002", "BR-VAL-ABS-003"],
        "effects": [{ "type": "notification", "target": "RH Manager", "template": "absence-submitted" }] },
      { "from": "Submitted", "to": "Approved", "action": "approve", "permission": "Rh.Absences.Approve",
        "guards": [],
        "effects": [
          { "type": "field-update", "target": "AbsenceBalance.taken", "template": "+= duration" },
          { "type": "notification", "target": "employee", "template": "absence-approved" }
        ] }
    ]
  }
  ```

  Présenter le lifecycle au client dans le lot E-ter (avec UCs et permissions).

  **Écriture :** Stocker dans `usecases.json` sous la clé `lifeCycles[]` (déjà dans le schema).

### F-ter. Gherkin Auto-Generation from Enriched UCs (MANDATORY)

> **Les Gherkin doivent être DERIVES des UCs enrichis, pas inventés séparément.**
> Chaque `mainScenario` → 1 Gherkin happy path.
> Chaque `errorScenario` → 1 Gherkin error path.
> Chaque `alternativeScenario` → 1 Gherkin alternative path.
> Les `examples[]{input, expected}` des BRs alimentent les `Examples:` tables Gherkin.

**Process :**

```
For EACH UC in module:
  // 1. Happy path from mainScenario + postconditions
  Generate Gherkin:
    Feature: {UC.name}
    Scenario: {UC.name} - happy path
      Given {UC.preconditions[] joined as Given/And}
      When {UC.mainScenario[] mapped to When/And steps}
      Then {UC.postconditions[] mapped to Then/And assertions}

  // 2. Error paths from errorScenarios
  For EACH errorScenario in UC.errorScenarios:
    Scenario: {UC.name} - {errorScenario.name}
      Given {UC.preconditions[] + error setup conditions}
      When {trigger action from mainScenario step 1}
      Then {errorScenario.steps[] mapped to Then/And}

  // 3. Alternative paths from alternativeScenarios
  For EACH alt in UC.alternativeScenarios:
    Scenario: {UC.name} - {alt.name}
      Given {modified preconditions}
      When {alt trigger}
      Then {alt.steps[] mapped to Then/And}

  // 4. Data-driven examples from linked BR examples
  IF UC.linkedRules references BRs with structured examples[]:
    Scenario Outline: {UC.name} - validation rules
      Given a {entity} with <field> = <value>
      When the user submits the form
      Then the system shows <expected>
    Examples:
      | field | value | expected |
      {for each BR example: | {input} | {expected} |}
```

**Exemple concret (Absence - Soumission) :**

```gherkin
Feature: Soumettre une demande d'absence (UC-ABS-001)

  Scenario: Soumission reussie
    Given un employe avec la permission Rh.Absences.Create
    And un solde de CP >= duree demandee
    When il selectionne le type CP, dates 15/03 au 19/03
    And le systeme calcule la duree en jours ouvres (5j)
    And le systeme verifie les dates, chevauchements et solde
    And il soumet la demande
    Then Absence.status = Submitted
    And AbsenceBalance.taken reste inchange (pas encore approuve)
    And notification envoyee au RH Manager

  Scenario: Soumission echouee - solde insuffisant
    Given un employe avec 2 jours de CP restants
    When il soumet une absence de 5 jours de type CP
    Then le systeme affiche le solde restant (2 jours)
    And la soumission est bloquee

  Scenario: Soumission echouee - chevauchement
    Given un employe avec une absence approuvee du 15/03 au 19/03
    When il soumet une absence du 17/03 au 21/03
    Then le systeme affiche l'absence existante en conflit
    And la soumission est bloquee

  Scenario Outline: Validation des dates
    Given un employe connecte
    When il saisit debut=<debut> fin=<fin>
    Then le systeme affiche <resultat>
    Examples:
      | debut | fin | resultat |
      | 15/03 | 12/03 | Erreur : date de fin avant date de debut |
      | 15/03 | 15/03 | OK (1 jour) |
      | 15/03 | 19/03 | OK (5 jours ouvres) |
```

**Minimums :** >= 3 scnarios Gherkin par module mtier (1 happy + 2 error/alt minimum).

**criture :** Stocker dans `usecases.json` sous la cl `gherkinScenarios[]`.

### F-quater. Field-Level Validations Auto-Generation (MANDATORY)

> **Gnrer les validations field-level depuis les attributs d'entits + rgles mtier.**
> Ces validations alimentent directement les validators frontend (FluentValidation backend + form validation frontend).

```
For EACH entity in module:
  For EACH attribute in entity.attributes:

    validations = []

    // From attribute metadata
    IF attribute.required: validations.push({ rule: "required", errorMessageKey: "{module}.{entity}.{attr}.required" })
    IF attribute.type === "string" AND attribute.maxLength: validations.push({ rule: "maxLength", params: { max: attribute.maxLength }, errorMessageKey: "..." })
    IF attribute.type === "string" AND attribute.unique: validations.push({ rule: "unique", errorMessageKey: "..." })
    IF attribute.type === "email": validations.push({ rule: "email", errorMessageKey: "..." })
    IF attribute.type === "decimal" AND (context is amount/price): validations.push({ rule: "min", params: { min: 0 }, errorMessageKey: "..." })
    IF attribute.type === "int": validations.push({ rule: "integer", errorMessageKey: "..." })
    IF attribute.type === "date": validations.push({ rule: "date", errorMessageKey: "..." })

    // From linked business rules (BR-VAL-*)
    IF exists BR-VAL referencing this attribute:
      For EACH matching BR:
        validations.push({ rule: "custom", brRef: BR.id, description: BR.statement, errorMessageKey: "..." })

    Store: { entity: entity.name, field: attribute.name, rules: validations }
```

**Exemple concret (Absence) :**

```json
[
  { "entity": "Absence", "field": "startDate", "rules": [
    { "rule": "required", "errorMessageKey": "absences.absence.startDate.required" },
    { "rule": "date", "errorMessageKey": "absences.absence.startDate.date" },
    { "rule": "custom", "brRef": "BR-VAL-ABS-001", "description": "startDate <= endDate", "errorMessageKey": "absences.absence.startDate.beforeEnd" }
  ]},
  { "entity": "Absence", "field": "endDate", "rules": [
    { "rule": "required", "errorMessageKey": "absences.absence.endDate.required" },
    { "rule": "date", "errorMessageKey": "absences.absence.endDate.date" }
  ]},
  { "entity": "Absence", "field": "reason", "rules": [
    { "rule": "maxLength", "params": { "max": 500 }, "errorMessageKey": "absences.absence.reason.maxLength" }
  ]}
]
```

**criture :** Stocker dans `usecases.json` sous la cl `validations[]` (dj dans le schema specification-schema.json).

### G. Interface Specs — Delegated to /business-analyse-design

> **Screen specifications are NOT produced in this step.**
> Interface design (SmartTable columns, SmartForm fields, wireframes, navigation) is handled by Phase 2: `/business-analyse-design`.
> This separation allows iterating on UI design without replaying the full analysis cycle.

### G-bis. API Endpoint Schema Enrichment (MANDATORY)

> **Gnrer les request/response schemas pour chaque API endpoint depuis les entits.**
> Les apiEndpoints[] existent dj dans le schema mais manquent les DTOs.
> Ce sont ces DTOs qui permettent au develop de gnrer le bon code du premier coup.

```
For EACH apiEndpoint in module:
  // Derive requestSchema from entity attributes
  IF method in ["POST", "PUT", "PATCH"]:
    requestSchema = {}
    For EACH attribute in entity.attributes:
      IF attribute is NOT computed AND attribute is NOT auto-generated:
        requestSchema[attribute.name] = {
          type: attribute.type,
          required: attribute.required || false,
          description: attribute.description
        }

  // Derive responseSchema from entity attributes + computed fields
  responseSchema = { id: "guid" }
  For EACH attribute in entity.attributes:
    responseSchema[attribute.name] = {
      type: attribute.type,
      computed: attribute.computed || false
    }
  // Add navigation properties for relationships
  For EACH relationship in entity.relationships:
    IF relationship.type === "ManyToOne":
      responseSchema[relationship.target + "Name"] = { type: "string", computed: true }

  // Derive errorCodes from linked BRs
  errorCodes = []
  For EACH linkedRule in UC.linkedRules:
    IF rule.severity === "blocking":
      errorCodes.push({ code: 400, reason: rule.id, description: rule.statement })
  errorCodes.push({ code: 403, reason: "Forbidden", description: "Missing permission: " + endpoint.permission })
  errorCodes.push({ code: 404, reason: "NotFound", description: entity.name + " not found" })
```

**Exemple concret (POST /api/rh/absences) :**

```json
{
  "method": "POST",
  "path": "/api/rh/absences",
  "permission": "Rh.Absences.Create",
  "linkedUC": "UC-ABS-001",
  "requestSchema": {
    "employeeId": { "type": "guid", "required": true },
    "absenceTypeId": { "type": "guid", "required": true },
    "startDate": { "type": "date", "required": true },
    "endDate": { "type": "date", "required": true },
    "reason": { "type": "string", "required": false }
  },
  "responseSchema": {
    "id": { "type": "guid" },
    "code": { "type": "string" },
    "status": { "type": "enum", "computed": false },
    "duration": { "type": "decimal", "computed": true },
    "employeeName": { "type": "string", "computed": true }
  },
  "errorCodes": [
    { "code": 400, "reason": "BR-VAL-ABS-001", "description": "Date de debut apres date de fin" },
    { "code": 400, "reason": "BR-VAL-ABS-002", "description": "Chevauchement avec absence existante" },
    { "code": 400, "reason": "BR-VAL-ABS-003", "description": "Solde insuffisant" },
    { "code": 403, "reason": "Forbidden", "description": "Permission Rh.Absences.Create requise" },
    { "code": 404, "reason": "NotFound", "description": "Employee not found" }
  ]
}
```

**criture :** Stocker dans `usecases.json` sous la cl `apiEndpoints[]` (enrichi).

### H-pre. Schema Refresh (OBLIGATOIRE avant chaque ecriture)

AVANT d'ecrire les JSON pour le module courant :
→ **Relire** `references/03-json-schemas.md` section "Business Rules Schema Format"
→ Verifier que CHAQUE rule dans le buffer utilise les champs canoniques :
  - `id` (pas `code`), `name` (pas `label`), `statement` (pas `description`/`rule`),
    `category` (pas `type`), `severity` (blocking/warning/info, pas Error/error),
    `examples[]` (array d'`{input, expected}`, pas string), `entities[]`, `sectionCode`
→ SI un champ deprecated est detecte : corriger AVANT ecriture
→ Ce refresh prend ~30 secondes et previent 100% des derives de schema

**POURQUOI ce refresh :** Apres 5+ modules, le contexte est compacte et les exemples
JSON du debut de conversation sont perdus. Ce refresh garantit que chaque module
est ecrit avec le format canonique, meme si le contexte a ete compresse.

### H. Write & Advance (SEULEMENT apres validation complete)

> **GATE:** ba-writer ne peut etre lance QUE si le client a valide :
> entites (B-ter) + regles (C-bis) + use cases/permissions (E-ter).
> Si un lot n'est pas valide → RETOUR au lot concerne.

After completing all sub-steps for a module:

1. **Validate against schema BEFORE writing** (uses schemas deployed in step-00):
   ```javascript
   ba-writer.validate("entities", entitiesData, docs_dir + "/schemas/entities.json")
   ba-writer.validate("rules", rulesData, docs_dir + "/schemas/rules.json")
   ba-writer.validate("usecases", usecasesData, docs_dir + "/schemas/usecases.json")
   ba-writer.validate("permissions", permissionsData, docs_dir + "/schemas/permissions.json")
   ```
   > If validation fails: fix the data and re-validate. Do NOT write invalid JSON.

2. Write via ba-writer:
   - `entities.json`
   - `rules.json`
   - `usecases.json`
   - `permissions.json`
   - Update `index.json` (hashes, summary counts)

3. Update module status to `specified`

3. Advance to next module in topological order

### H-bis. Module Transition Summary

Afficher un resume compact du module termine :
```
✓ {moduleName} — {entityCount} entites, {ruleCount} regles, {ucCount} UCs, {permCount} permissions
```

Passer au module suivant. INTERDICTION de lancer des ba-writer en parallele pour des modules differents.

### I. Module Completion Check

After all modules are specified:
- Verify all modules have status `specified`
- Display summary: total entities, rules, UCs, permissions across all modules
- Remind user: "Run /business-analyse-design to design interfaces for these modules"
- Transition to step 04

### I-bis. Cross-Module Questions (apres tous les modules)

SI des dependances cross-module existent (identifiees en step-02) :
→ Charger questionnaire/05-cross-module.md et poser les questions pertinentes via AskUserQuestion.
→ Integrer les reponses dans les modules concernes via ba-writer.

## POST-CHECK (EXECUTABLE — BLOCKING)

> → **Load** `references/03-post-check-validation.md` and execute ALL checks.
> Any BLOCKING check failure must be resolved before proceeding to step 04.

## Transition

→ Step 04 (consolidate) — Cross-module validation
