---
name: step-04-consolidation
description: Cross-module consolidation - interactions, permission coherence, E2E flows, final approval
model: sonnet
next_step: steps/step-05-handoff.md
---

# Step 4: Consolidation

## MANDATORY EXECUTION RULES
- ALWAYS load summaries of ALL completed modules
- NEVER duplicate module content - work with summaries only
- ALWAYS validate permission coherence across modules
- This step auto-validates for single-module (trivial consolidation)
- Final client approval is BLOCKING before handoff

## YOUR TASK
Consolidate all specified modules into a coherent application. Detect cross-module interactions, verify permission coherence, trace E2E flows, and obtain final client approval.

---

## EXECUTION SEQUENCE

### 1. Read Module Summaries

```
ba-reader.getCompletedModulesSummary({feature_id})
→ For each module: code, entities[], key BRs, permissions[], dependencies
→ Max 100 lines per module summary

ba-reader.readApplicationContext({feature_id})
→ cadrage.applicationRoles, modules[], dependencyGraph
```

IF status already "consolidated":
  Display: "Consolidation already completed. Loading next step."
  Load: steps/step-05-handoff.md
  STOP

### 2. Cross-Module Interactions

**2a. Foreign Key References**

For each dependency edge in dependencyGraph:
```json
{
  "from": "Orders",
  "to": "Customers",
  "type": "FK",
  "references": [
    {
      "sourceEntity": "Order",
      "sourceField": "CustomerId",
      "targetEntity": "Customer",
      "targetField": "Id",
      "cardinality": "N:1",
      "cascadeDelete": false,
      "required": true
    }
  ]
}
```

Verify each FK reference exists in the corresponding module's entities.
IF missing → WARNING + ask user to confirm or add.

**2b. Shared Entities**

Detect entities referenced by multiple modules:
```json
{
  "sharedEntities": [
    {
      "entity": "Customer",
      "definedIn": "Customers",
      "referencedBy": ["Orders", "Invoices"],
      "referenceType": "FK"
    }
  ]
}
```

**2c. Event-Based Interactions**

Identify event flows between modules:
```json
{
  "events": [
    {
      "producer": "Orders",
      "event": "OrderApproved",
      "consumers": ["Invoices"],
      "description": "When an order is approved, an invoice is auto-generated"
    }
  ]
}
```

**2d. Shared Reference Data**

Identify shared lookup/reference tables:
```json
{
  "sharedReferenceData": [
    {
      "table": "Currency",
      "usedBy": ["Orders", "Invoices"],
      "owner": "shared"
    }
  ]
}
```

### 3. Permission Coherence

> **All modules MUST use the same application-level roles.**

**3a. Role Consistency**

Verify all modules use the applicationRoles defined in cadrage:
```
FOR each module in completedModules:
  FOR each role in module.specification.permissionMatrix.roles:
    IF role NOT IN cadrage.applicationRoles → ERROR
```

**3b. Permission Path Format**

Verify all permission paths follow the pattern:
`business.{app}.{module}.{resource}.{action}`

Check for:
- Inconsistent app prefix → ERROR
- Missing module segment → ERROR
- Shortcut paths (not full format) → ERROR

**3c. Role Hierarchy Coherence**

Verify the role hierarchy is respected across all modules:
```
Admin ⊃ Manager ⊃ Contributor ⊃ Viewer

FOR each module:
  IF Admin has fewer permissions than Manager → ERROR
  IF Manager has fewer permissions than Contributor → ERROR
  IF Contributor has fewer permissions than Viewer → ERROR
```

**3d. Permission Conflicts**

Detect conflicting permission assignments:
```json
{
  "conflicts": [
    {
      "role": "Manager",
      "module1": "Orders",
      "permission1": "business.sales.orders.approve",
      "module2": "Invoices",
      "permission2": "business.sales.invoices.approve",
      "issue": "Manager can approve orders but not invoices - is this intentional?"
    }
  ]
}
```

Present conflicts to user via AskUserQuestion:
```
question: "Le Manager peut approuver les commandes mais pas les factures. Est-ce intentionnel ?"
header: "Permissions"
options:
  - label: "Oui, intentionnel"
    description: "Les factures nécessitent un niveau d'approbation supérieur"
  - label: "Non, corriger"
    description: "Le Manager devrait aussi pouvoir approuver les factures"
```

### 4. Semantic Validation (MANDATORY)

For EACH module feature.json, execute these checks:

| # | Check | Rule | Severity |
|---|-------|------|----------|
| 1 | Permission orpheline | Every permissionMatrix.permissions[].path is used by at least one useCases[].permission | WARNING |
| 2 | UC sans FR | Every useCases[].id is referenced by at least one functionalRequirements[].linkedUseCases[] | WARNING |
| 3 | Entity sans endpoint | Every entities[].name has at least one endpoint in apiEndpoints[] | WARNING |
| 4 | UC sans scenario | Every useCases[].mainScenario has at least 2 steps | WARNING |
| 5 | Role sans permissions | Every applicationRole has at least one entry in roleAssignments[] | WARNING |
| 6 | Navigation sans traduction | Every navigation.entries[].code has translations in 4 languages in seedDataCore.navigationTranslations[] | ERROR |
| 7 | LifeCycle terminal | Every lifeCycles[].states[] has at least one state with isTerminal: true | WARNING |
| 8 | Schema conformity | No custom fields outside defined schema (no KPIDefinitions, ChartConfigurations, etc.) | ERROR |
| 9 | Wireframe coverage | Every navigation section has a corresponding uiWireframes[].screen | ERROR |
| 10 | Doublon entity | No entity with same name in two different modules (unless shared via dependencyGraph) | ERROR |

Store results in consolidation.semanticChecks[]:
```json
{
  "check": "permission-orpheline",
  "module": "RepairManagement",
  "status": "PASS|WARNING|ERROR",
  "details": "Permission business.freebike.repairmanagement.export not referenced by any UC",
  "autoFixed": false
}
```

**Blocking rule:**
- 0 ERROR → PASS
- ≥1 ERROR → BLOCK (fix before handoff, ask user for each)
- WARNING only → PASS with user notification

### 4-bis. Structural Schema Conformity Check (MANDATORY)

> **Post-generation validation.** After semantic checks, verify that ALL module feature.json files conform to the JSON schema structure. This catches field name mismatches, missing sections, and wrong nesting.

For EACH module feature.json, run these structural checks:

**A. Required Sections Presence**

| Section | Required | Check |
|---------|----------|-------|
| `metadata` | YES | Must have: app, module, language, featureDescription, featureType |
| `discovery` | YES | Must exist (even if minimal) |
| `analysis.entities[]` | YES | Must have ≥1 entity |
| `analysis.businessRules[]` | YES | Must have ≥1 BR |
| `analysis.objectives[]` | YES | Must have ≥1 objective |
| `analysis.processFlow` | YES | Must have entryPoints, mainFlow, decisionPoints |
| `analysis.dataLifecycle` | YES | Must have retention, archival, deletion, gdprRelevant |
| `specification.useCases[]` | YES | Must have ≥1 UC |
| `specification.functionalRequirements[]` | YES | Must have ≥1 FR |
| `specification.permissionMatrix` | YES | Must be `{permissions[], roleAssignments[]}` (NOT flat array) |
| `specification.sections[]` | YES | Must have ≥1 section with resources |
| `specification.uiWireframes[]` | YES | Must have ≥1 wireframe with `layout` object |
| `specification.lifeCycles[]` | IF entities have status | States must be objects (NOT strings) |
| `specification.seedDataCore` | YES | Must have 5 arrays: navigationModules, navigationTranslations, permissions, rolePermissions, permissionConstants |
| `specification.gherkinScenarios` | YES | Must be `{feature, scenarios[]}` (NOT flat array) |
| `specification.navigation` | YES | Must have entries[] |
| `specification.i18nKeys[]` | YES | Must have ≥1 key with fr, en, nl, de |
| `validation` | YES | Must have completenessChecks, consistencyChecks, semanticChecks, decision |
| `handoff` | Written by step-05 | Checked post-handoff |

**B. Field Name Conformity**

For each section, verify NO FORBIDDEN field names are used:

| Section | FORBIDDEN fields | Correct fields |
|---------|-----------------|----------------|
| `entities[].attributes[]` | `type`, `values`, `rules` | `description`, `unique`, `validation` |
| `businessRules[]` | `rule`, `condition` (singular), `action` | `name`, `statement`, `conditions[]` |
| `useCases[]` | `actor`, `linkedBRs`, `linkedFRs` | `primaryActor`, `linkedRules` |
| `functionalRequirements[]` | `name`, `description`, `linkedUCs`, `linkedBRs` | `statement`, `linkedUseCases`, `linkedRules` |
| `permissionMatrix` | flat array with `resource`/`roles` | `{permissions[], roleAssignments[]}` |
| `lifeCycles[].states[]` | string values like `"Active"` | objects with `{id, displayName, color, isTerminal}` |
| `seedDataCore` | flat object, string values | 5 typed arrays |
| `gherkinScenarios` | flat scenario array | `{feature, scenarios[]}` nested |

**C. ID Pattern Conformity**

| ID Type | Pattern | Example |
|---------|---------|---------|
| BR | `BR-(VAL\|CALC\|WF\|SEC\|DATA)-{PREFIX}-\d{3}` | BR-VAL-RM-001 |
| UC | `UC-{PREFIX}-\d{3}` | UC-RM-001 |
| FR | `FR-{PREFIX}-\d{3}` | FR-RM-001 |
| Permissions | `business.{app}.{module}.{resource}.{action}` | business.freebike.repairs.create |

Verify all IDs use the correct module prefix (2-4 uppercase letters).

**D. Cross-Module ID Uniqueness**

Verify no ID collision across modules:
```
SET all_ids = {}
FOR each module:
  FOR each BR, UC, FR id:
    IF id IN all_ids → ERROR: "Duplicate {id} in {module1} and {module2}"
    ADD id to all_ids
```

**E. Wireframe Layout Completeness**

For each `uiWireframes[]`:
```
IF missing componentMapping[] → ERROR
IF missing layout object → ERROR
IF missing layout.regions[] → ERROR
FOR each region in layout.regions[]:
  IF missing position → ERROR
  IF missing components[] → ERROR
```

**F. SeedDataCore Translation Coverage**

```
FOR each navigationModules[].code:
  FOR each language in [fr, en, nl, de]:
    IF NOT EXISTS navigationTranslations[] with moduleCode AND language → ERROR
```

**Result aggregation:**
```json
{
  "structuralChecks": [
    { "check": "required-sections", "module": "...", "status": "PASS|ERROR", "details": "Missing: analysis.objectives" },
    { "check": "field-names", "module": "...", "status": "PASS|ERROR", "details": "businessRules[0] uses 'rule' instead of 'name'+'statement'" },
    { "check": "id-patterns", "module": "...", "status": "PASS|ERROR", "details": "BR-001 missing category segment" },
    { "check": "id-uniqueness", "module": "...", "status": "PASS|ERROR", "details": "UC-RM-003 duplicated in Vehicles and Repairs" },
    { "check": "wireframe-layout", "module": "...", "status": "PASS|ERROR", "details": "Wireframe repairs-list missing layout object" },
    { "check": "seeddata-translations", "module": "...", "status": "PASS|ERROR", "details": "Missing nl translation for NavigationModuleRepairs" }
  ]
}
```

**Blocking rule:**
- 0 ERROR → PASS (merge structuralChecks into consolidation.semanticChecks[])
- ≥1 ERROR → BLOCK. For each ERROR:
  1. Attempt AUTO-FIX (rename fields, restructure)
  2. Re-write the module feature.json with fixes
  3. Re-check
  4. If still ERROR → ask user

### 5. End-to-End Flows

Identify business processes that span multiple modules:

```json
{
  "e2eFlows": [
    {
      "name": "Order to Invoice",
      "steps": [
        { "module": "Customers", "action": "Customer exists", "permission": "customers.read" },
        { "module": "Products", "action": "Products selected", "permission": "products.read" },
        { "module": "Orders", "action": "Order created", "permission": "orders.create" },
        { "module": "Orders", "action": "Order approved", "permission": "orders.approve" },
        { "module": "Invoices", "action": "Invoice generated", "permission": "invoices.create" },
        { "module": "Invoices", "action": "Invoice sent", "permission": "invoices.update" }
      ],
      "actors": ["Contributor (create)", "Manager (approve)"],
      "data": "Customer → Order → OrderLines (Products) → Invoice → InvoiceLines"
    }
  ]
}
```

Display each E2E flow as a diagram:
```
Customer ──[read]──→ Order ──[create]──→ Order ──[approve]──→ Invoice ──[create]──→ Invoice ──[send]──→
  (Customers)         (Orders)            (Orders)              (Invoices)            (Invoices)
  Contributor         Contributor          Manager               System                Manager
```

### 6. Global Risk Assessment

Evaluate application-level risks:

| Risk | Metric | Threshold | Status |
|------|--------|-----------|--------|
| Total entities | {sum across modules} | ≤20 | PASS/WARNING |
| Total BRs | {sum across modules} | ≤50 | PASS/WARNING |
| Cross-module dependencies | {edge count} | ≤10 | PASS/WARNING |
| Shared entities | {count} | ≤5 | PASS/WARNING |
| E2E flow length | {max steps} | ≤8 | PASS/WARNING |
| Permission paths | {total} | ≤100 | PASS/WARNING |

### 7. Consolidation Summary

Display comprehensive summary:
```
═══════════════════════════════════════════════════════════
  CONSOLIDATION - {application_name}
═══════════════════════════════════════════════════════════

### Modules ({count})
| Module | Entités | BRs | UCs | Complexité |
|--------|---------|-----|-----|------------|
{for each module}

### Interactions Cross-Module
- FK References: {count}
- Shared Entities: {count}
- Events: {count}
- Shared Data: {count}

### Permissions
- Roles: {count} (coherent: ✓/✗)
- Total paths: {count}
- Conflicts resolved: {count}

### E2E Flows
{for each flow: name, modules involved, actors}

### Risques Globaux
{risk assessment table}

═══════════════════════════════════════════════════════════
```

### 8. Final Client Approval (BLOCKING)

Ask via AskUserQuestion:
```
question: "La spécification complète de l'application est prête. Validez-vous l'ensemble ?"
header: "Approbation"
options:
  - label: "Approuvé"
    description: "Passer au handoff pour la phase d'implémentation"
  - label: "Réviser un module"
    description: "Retourner à la spécification d'un module spécifique"
  - label: "Modifier les interactions"
    description: "Ajuster les interactions cross-module"
```

IF "Réviser un module":
  Ask which module → set currentModuleIndex to that module → load step-03-specify.md

IF "Modifier les interactions":
  Re-enter section 2-4 of this step

### 9. Write Consolidation

> **STRUCTURE CARD: consolidation** — Must match `application-schema.json`. Follow this structure exactly.

```
ba-writer.enrichSection({
  featureId: {feature_id},
  section: "consolidation",
  data: {
    crossModuleInteractions: [
      {
        "fromModule": "Orders",
        "toModule": "Customers",
        "interactionType": "FK-reference|event-publish|event-subscribe|shared-lookup|cascade-delete|cascade-update",
        "description": "Order references customer via FK",
        "entities": ["Order→Customer"]
      }
    ],
    sharedEntities: [
      {
        "entity": "Customer",
        "ownerModule": "Customers",
        "referencedBy": ["Orders", "Invoices"],
        "sharedFields": ["code", "name"]
      }
    ],
    permissionCoherence: {
      "rolesConsistent": true,
      "pathFormatConsistent": true,
      "hierarchyRespected": true,
      "conflicts": [],
      "warnings": []
    },
    e2eFlows: [
      {
        "name": "Order to Invoice",
        "modules": ["Customers", "Orders", "Invoices"],
        "steps": [
          { "module": "Orders", "action": "Create order", "permission": "business.sales.orders.create", "dataFlow": "Customer → Order" }
        ]
      }
    ],
    globalRiskAssessment: [
      {
        "risk": "Cross-module coupling complexity",
        "category": "coupling|complexity|dependency-chain|security|scope",
        "severity": "critical|high|medium|low",
        "mitigation": "Event-driven communication instead of direct FK"
      }
    ],
    semanticChecks: [
      { "check": "permission-orpheline", "status": "PASS|WARNING|ERROR", "details": "..." },
      { "check": "uc-sans-fr", "status": "PASS|WARNING|ERROR", "details": "..." },
      { "check": "wireframe-coverage", "status": "PASS|WARNING|ERROR", "details": "..." }
    ],
    decision: {
      "approved": true,
      "reason": "All cross-module validations passed",
      "approvedBy": "Client",
      "approvedAt": "{ISO timestamp}"
    }
  }
})

ba-writer.updateStatus({feature_id}, "consolidated")
```

> **STRUCTURE DIFFERENCES FROM OLD FORMAT:**
> - `crossModuleInteractions` is a FLAT ARRAY (not `{fkReferences, sharedEntities, events}` nested object)
> - `sharedEntities` is TOP-LEVEL (not nested inside crossModuleInteractions)
> - `decision` is an OBJECT with `approved`, `reason`, `approvedBy`, `approvedAt` (not `clientApproval` string)
> - `globalRiskAssessment` is an ARRAY of risk objects (not `{totalEntities: {value, threshold}}`)

Add changelog entry:
```json
{
  "step": "step-04-consolidation",
  "timestamp": "...",
  "changes": [
    "Cross-module interactions: X FK, Y events, Z shared entities",
    "Permission coherence: validated ({count} paths, {conflicts} conflicts resolved)",
    "E2E flows: {count} identified",
    "Global risk: {level}",
    "Client approval: APPROVED"
  ]
}
```

---

## SINGLE-MODULE MODE

When only 1 module:
1. Skip cross-module interactions (no other modules)
2. Skip permission coherence check (only one module)
3. Skip E2E flows (single module)
4. Auto-approve (no cross-module concerns)
5. Write minimal consolidation section
6. Display: "Module unique - consolidation automatique ✓"
7. Proceed to step-05-handoff.md

---

## CONTEXT MANAGEMENT

| Loaded | Not Loaded |
|--------|------------|
| _shared.md | Full module feature.json files |
| Module summaries (100 lines each) | Questionnaires |
| Master feature.json | Templates |
| cadrage section | React viewer files |

---

## NEXT STEP

Load: `./step-05-handoff.md`
