# Review Data Mapping Reference (references/review-data-mapping.md)

> **Used by:** step-06-review (Apply Review Corrections)
> **Purpose:** Reverse mapping from ba-review.json (HTML export) back to feature.json format
> **Complement of:** `references/html-data-mapping.md` (forward mapping)

---

## Overview

The ba-review.json is exported from the interactive HTML with the same structure as the HTML data model. This document defines the **reverse transformation** to apply corrections back into feature.json format.

---

## Cadrage Reverse Mapping

### Scope (HTML → feature.json)

```javascript
// HTML format: { vital: [{name, description}], important: [...], optional: [...], excluded: [...] }
// feature.json format: { mustHave: [string], shouldHave: [string], couldHave: [string], outOfScope: [string] }

reverseScope(reviewCadrage) {
  return {
    mustHave: (reviewCadrage.scope?.vital || []).map(s => s.name),
    shouldHave: (reviewCadrage.scope?.important || []).map(s => s.name),
    couldHave: (reviewCadrage.scope?.optional || []).map(s => s.name),
    outOfScope: (reviewCadrage.scope?.excluded || []).map(s => s.name)
  };
}
```

### Stakeholders (HTML → feature.json)

```javascript
// HTML format: { role, function, tasks[], frequency, access, frustrations }
// feature.json format: { role, function, involvement, tasks[], frequency, painPoints[] }

const accessToInvolvement = {
  admin: "decision-maker",
  manager: "consulted",
  contributor: "end-user",
  viewer: "informed"
};

const frequencyReverse = {
  daily: "Quotidien",
  weekly: "Hebdomadaire",
  monthly: "Mensuel",
  occasional: "Occasionnel"
};

reverseStakeholder(htmlStakeholder) {
  return {
    role: htmlStakeholder.role,
    function: htmlStakeholder.function || "",
    involvement: accessToInvolvement[htmlStakeholder.access] || "end-user",
    tasks: htmlStakeholder.tasks || [],
    frequency: frequencyReverse[htmlStakeholder.frequency] || htmlStakeholder.frequency,
    painPoints: (htmlStakeholder.frustrations || "").split("\n").filter(Boolean)
  };
}
```

### Problem / Vision / Current (HTML → feature.json)

```javascript
// HTML has detailed sub-fields; feature.json has flat strings
reverseProblemVision(reviewCadrage) {
  return {
    problem: reviewCadrage.problem?.description || "",
    trigger: reviewCadrage.problem?.trigger || "",
    asIs: reviewCadrage.current?.tools || "",
    toBe: reviewCadrage.vision?.changes || ""
  };
}
```

### Risks (HTML → feature.json)

```javascript
// HTML format: { description, probability, impact, mitigation }
// feature.json format: { id, type, description, probability, impact, mitigation }

reverseRisks(reviewRisks, existingRisks) {
  return (reviewRisks || []).map((r, i) => {
    // Try to match with existing risk by description
    const existing = (existingRisks || []).find(er => er.description === r.description);
    return {
      id: existing?.id || "RISK-" + String(i + 1).padStart(3, "0"),
      type: existing?.type || "business",
      description: r.description,
      probability: r.probability,
      impact: r.impact,
      mitigation: r.mitigation || ""
    };
  });
}
```

### Acceptance Criteria (HTML → feature.json)

```javascript
// HTML format: success.definition (string, one criterion per line)
// feature.json format: acceptanceCriteria[]: { id, criterion, validated }

reverseAcceptanceCriteria(reviewSuccess, existingCriteria) {
  const lines = (reviewSuccess?.definition || "").split("\n").filter(Boolean);
  return lines.map((line, i) => {
    const existing = (existingCriteria || []).find(ac => ac.criterion === line);
    return {
      id: existing?.id || "AC-" + String(i + 1).padStart(3, "0"),
      criterion: line.trim(),
      validated: existing?.validated || false
    };
  });
}
```

---

## Module Specifications Reverse Mapping

### Use Cases (HTML → feature.json)

```javascript
// HTML format: { id, name, actor, steps (newline-separated string), alternative (string) }
// feature.json format: { id, name, primaryActor, permission, preconditions[], postconditions[], mainScenario[], alternativeScenarios[], linkedRules[] }

reverseUseCase(htmlUC, existingUC) {
  return {
    id: htmlUC.id || existingUC?.id,
    name: htmlUC.name,
    primaryActor: htmlUC.actor,
    permission: existingUC?.permission || "",
    preconditions: existingUC?.preconditions || [],
    postconditions: existingUC?.postconditions || [],
    mainScenario: (htmlUC.steps || "").split("\n").filter(Boolean),
    alternativeScenarios: existingUC?.alternativeScenarios || [],
    errorScenarios: existingUC?.errorScenarios || [],
    linkedRules: existingUC?.linkedRules || []
  };
}
```

### Business Rules (HTML → feature.json)

```javascript
// HTML format: { id, name, category, statement, example (string) }
// feature.json format: { id, name, category, statement, priority, conditions[], examples[], testability }

reverseBusinessRule(htmlBR, existingBR) {
  return {
    id: htmlBR.id || existingBR?.id,
    name: htmlBR.name,
    category: htmlBR.category,
    statement: htmlBR.statement,
    priority: existingBR?.priority || "must",
    conditions: existingBR?.conditions || [],
    examples: htmlBR.example
      ? [{ input: htmlBR.example, expected: "" }]
      : (existingBR?.examples || []),
    testability: existingBR?.testability || ""
  };
}
```

### Entities (HTML → feature.json)

```javascript
// HTML format: { name, description, attributes: [{name, description}], relationships: [string] }
// feature.json format: { name, description, attributes: [{name, description, required}], relationships: [{target, type, description}] }

reverseEntity(htmlEntity, existingEntity) {
  return {
    name: htmlEntity.name,
    description: htmlEntity.description || "",
    attributes: (htmlEntity.attributes || []).map(a => {
      const existing = (existingEntity?.attributes || []).find(ea => ea.name === a.name);
      return {
        name: a.name,
        description: a.description || "",
        required: existing?.required ?? true
      };
    }),
    relationships: (htmlEntity.relationships || []).map(relStr => {
      // Parse "Target (type) - description" format back to structured
      const match = relStr.match(/^(.+?)\s*\((.+?)\)\s*-\s*(.*)$/);
      if (match) {
        return { target: match[1].trim(), type: match[2].trim(), description: match[3].trim() };
      }
      return { target: relStr, type: "reference", description: "" };
    })
  };
}
```

### Permissions (HTML → feature.json)

```javascript
// HTML format: permissions[] = ["Role|Action", "Role|Action", "Role|*"]
// feature.json format: permissionMatrix = { permissions: [{path, action, description}], roleAssignments: [{role, permissions[]}] }

const actionToPermission = {
  Consulter: "read",
  Creer: "create",
  Modifier: "update",
  Supprimer: "delete",
  Valider: "validate",
  Exporter: "export"
};

reversePermissions(htmlPermissions, moduleCode, appName) {
  const rolePerms = {};

  // Group permissions by role
  (htmlPermissions || []).forEach(p => {
    const [role, action] = p.split("|");
    if (!rolePerms[role]) rolePerms[role] = [];
    if (action === "*") {
      // Wildcard: add all standard permission paths
      Object.values(actionToPermission).forEach(permAction => {
        const path = `business.${appName}.${moduleCode}.*.${permAction}`;
        if (!rolePerms[role].includes(path)) rolePerms[role].push(path);
      });
    } else {
      const permAction = actionToPermission[action] || action.toLowerCase();
      const path = `business.${appName}.${moduleCode}.*.${permAction}`;
      if (!rolePerms[role].includes(path)) rolePerms[role].push(path);
    }
  });

  // Build unique permission paths
  const allPaths = new Set();
  Object.values(rolePerms).forEach(perms => perms.forEach(p => allPaths.add(p)));

  return {
    permissions: Array.from(allPaths).map(path => ({
      path,
      action: path.split(".").pop(),
      description: ""
    })),
    roleAssignments: Object.entries(rolePerms).map(([role, perms]) => ({
      role,
      permissions: perms
    }))
  };
}
```

---

## Modules Reverse Mapping

### Module List (HTML → feature.json)

```javascript
// HTML format: modules[] = [{ code, name, description, featureType, priority, entities[], status }]
// feature.json format: modules[] = [{ code, description, featureType, dependencies[], dependents[], status, priority, sortOrder, entities[], estimatedComplexity }]

reverseModule(htmlModule, existingModule) {
  return {
    code: htmlModule.code,
    description: htmlModule.description || "",
    featureType: htmlModule.featureType || "data-centric",
    dependencies: existingModule?.dependencies || [],
    dependents: existingModule?.dependents || [],
    status: existingModule?.status || "pending",
    priority: htmlModule.priority || "must",
    sortOrder: existingModule?.sortOrder || 0,
    entities: htmlModule.entities || [],
    estimatedComplexity: existingModule?.estimatedComplexity || "medium",
    featureJsonPath: existingModule?.featureJsonPath
  };
}
```

---

## Consolidation Reverse Mapping

### Cross-Module Interactions (HTML → feature.json)

```javascript
// HTML format: consolidation.interactions[] = [{ from, to, description }]
// feature.json format: consolidation.crossModuleInteractions[] = [{ fromModule, toModule, interactionType, description, entities[] }]

reverseInteraction(htmlInteraction, existingInteraction) {
  return {
    fromModule: htmlInteraction.from,
    toModule: htmlInteraction.to,
    interactionType: existingInteraction?.interactionType || "data-reference",
    description: htmlInteraction.description || "",
    entities: existingInteraction?.entities || []
  };
}
```

### E2E Flows (HTML → feature.json)

```javascript
// HTML format: consolidation.e2eFlows[] = [{ name, steps: [{module, action}], actors }]
// feature.json format: consolidation.e2eFlows[] = [{ name, steps: [{module, action, permission}] }]

reverseE2EFlow(htmlFlow, existingFlow) {
  return {
    name: htmlFlow.name,
    steps: (htmlFlow.steps || []).map((s, i) => ({
      module: s.module,
      action: s.action,
      permission: existingFlow?.steps?.[i]?.permission || ""
    }))
  };
}
```

---

## Review Comments Preservation

Comments from the review are stored in a new `review` section of the master feature.json:

```javascript
{
  review: {
    appliedAt: "ISO timestamp",
    sourceVersion: "1.0",
    comments: [
      // Inline structured comments from data.comments[]
      { id, sectionId, cardIndex, author, timestamp, content, status, category }
    ],
    specComments: {
      // Textarea comments keyed as "moduleCode.type.index"
      "Employees.uc.0": "Comment text",
      "Employees.br.1": "Another comment"
    },
    wireframeComments: {
      // Wireframe feedback keyed by module
      "Employees": { "EM-list": "Comment on list wireframe" }
    },
    changeSummary: {
      cadrageModified: true,
      modulesAdded: [],
      modulesRemoved: [],
      modulesModified: ["Employees", "LeaveRequests"],
      commentsCount: 5
    }
  }
}
```

---

## Matching Strategy

When applying corrections, always try to **match existing items** before creating new ones:

1. **By ID**: If the review item has an ID (UC-001, BR-VAL-001), find the same ID in feature.json
2. **By name**: If no ID match, find by exact name match
3. **New item**: If no match found, create a new item with a generated ID
4. **Removed items**: Items present in feature.json but absent from review are marked as removed (not deleted)

This preserves referential integrity (linked rules, use case references) across the update.
