---
name: step-02-build-data
description: Build FEATURE_DATA and EMBEDDED_ARTIFACTS objects from collected JSON data
model: opus
next_step: steps/step-03-render.md
---

> **Reference files:** `references/data-mapping.md` | `references/data-build.md`

# Step 2: Build Data Objects

## YOUR TASK

Build `FEATURE_DATA` and `EMBEDDED_ARTIFACTS` objects from `{collected_data}` collected in step-01.

---

## EXECUTION SEQUENCE

### 1. Build FEATURE_DATA

Construct the complete FEATURE_DATA object following the exact mapping in `references/data-mapping.md`:

**metadata:**
```javascript
metadata: {
  applicationName: master.metadata.application,
  applicationId: master.id,
  version: master.version,
  createdAt: master.metadata.createdAt,
  lastModified: master.metadata.updatedAt,
  analysisMode: master.metadata.analysisMode || "interactive"
}
```

**cadrage.context** (lean format):
```javascript
context: {
  problem: master.cadrage.problem || "",
  trigger: master.cadrage.trigger || "",
  currentSituation: master.cadrage.asIs || "",        // asIs → currentSituation
  desiredSituation: master.cadrage.toBe || "",        // toBe → desiredSituation
  painPoints: (master.cadrage.stakeholders || []).flatMap(s => s.painPoints || []).join("\n"),
  acceptanceCriteria: (master.cadrage.acceptanceCriteria || []).map(ac => ac.criterion).join("\n")
}
```

**cadrage.stakeholders** (with frequency/access conversion):
```javascript
stakeholders: master.cadrage.stakeholders.map(s => ({
  role: s.role,
  function: s.function || "",
  tasks: s.tasks || [],
  frequency: mapFrequency(s.frequency),     // "Quotidien"→"daily", "Hebdomadaire"→"weekly", "Mensuel"→"monthly"
  access: mapAccess(s.involvement),         // "decision-maker"→"admin", "end-user"→"contributor", "observer"→"viewer"
  frustrations: (s.painPoints || []).join("\n")
}))
```

**cadrage.scope** (KEY CONVERSION — CRITICAL):
```javascript
scope: {
  vital: (master.cadrage.globalScope?.mustHave || []).map(item => ({ name: item, description: "" })),
  important: (master.cadrage.globalScope?.shouldHave || []).map(item => ({ name: item, description: "" })),
  optional: (master.cadrage.globalScope?.couldHave || []).map(item => ({ name: item, description: "" })),
  excluded: (master.cadrage.globalScope?.outOfScope || []).map(item => ({ name: item, description: "" }))
}
```

> Safety net: also check `master.cadrage.coverageMatrix` as alternative source for scope items.


**modules[]:**
```javascript
modules: master.modules.map(m => ({
  code: m.code,
  name: m.name || m.code,                   // display name (MANDATORY)
  description: m.description || "",
  featureType: m.featureType || "data-centric",
  priority: m.priority || "must",
  entities: m.entities || [],
  anticipatedSections: m.anticipatedSections || [],
  dependencies: m.dependencies || [],
  dependents: m.dependents || [],
  estimatedComplexity: m.estimatedComplexity || "medium",
  status: m.status || "handed-off"
}))
```

**moduleSpecs{} — ONE entry per module (CRITICAL):**

> **FLAT-FILE ARCHITECTURE:** Module data comes from separate JSON files collected in step-01,
> NOT from index.json fields. Use `collected_data.modules[moduleCode].entities`, `.usecases`, etc.

For EACH module, use the flat file data from `collected_data.modules[moduleCode]`:

```javascript
const mod = collected_data.modules[moduleCode];

moduleSpecs[moduleCode] = {
  useCases: (mod.usecases?.useCases || []).map(uc => ({
    name: uc.name,
    actor: uc.primaryActor,
    steps: (uc.mainScenario || []).join("\n"),
    alternative: (uc.alternativeScenarios || []).map(a => a.name + ": " + (a.steps || []).join(", ")).join("\n")
  })),
  businessRules: (mod.rules?.rules || []).map(br => ({
    name: br.name,
    category: br.category,
    statement: br.statement,
    example: (br.examples || []).map(e => e.input + " → " + e.expected).join("; ")
  })),
  // ENTITY SAFETY NET: map fields[] → attributes[] if agent deviated
  entities: (mod.entities?.entities || []).map(ent => ({
    name: ent.name,
    description: ent.description || "",
    attributes: (ent.attributes || []).length > 0
      ? ent.attributes.map(a => ({ name: a.name, type: a.type || "string", required: a.required || false, description: a.description || "" }))
      : (ent.fields || []).map(f => ({ name: f.name, type: f.type || "string", required: f.required || false, description: f.description || "" })),
    relationships: (ent.relationships || []).map(r =>
      typeof r === 'string' ? r : r.target + " (" + r.type + ") - " + (r.description || ""))
  })),
  permissions: buildPermissionKeys(mod.permissions),
  apiEndpoints: mod.usecases?.apiEndpoints || []
}
```

> See `references/data-mapping.md` for `buildPermissionKeys()` implementation.

**consolidation:**
```javascript
consolidation: {
  interactions: (master.consolidation?.crossModuleInteractions || []).map(i => ({
    from: i.fromModule,
    to: i.toModule,
    description: i.description || ""
  })),
  sharedEntities: master.consolidation?.sharedEntities || [],
  e2eFlows: (master.consolidation?.e2eFlows || []).map(f => ({
    name: f.name,
    steps: (f.steps || []).map(s => ({ module: s.module, action: s.action })),
    actors: (f.steps || []).map(s => s.permission).join(", ")
  }))
}
```

**handoff:**
```javascript
handoff: {
  complexity: master.handoff?.complexity,
  implementationStrategy: master.handoff?.implementationStrategy,
  moduleOrder: master.handoff?.moduleOrder,
  filesToCreate: master.handoff?.filesToCreate,
  brToCodeMapping: master.handoff?.brToCodeMapping,
  apiEndpointSummary: master.handoff?.apiEndpointSummary
}
```

---

### 2. Build EMBEDDED_ARTIFACTS

> **FIELD RENAME WARNING:** Module JSON uses `mockupFormat`/`mockup`. HTML reads `format`/`content`. You MUST rename.

**wireframes{} — per moduleCode:**

> **FLAT-FILE:** Wireframes come from `screens.json` in each module directory (collected in step-01),
> NOT from `moduleFeature.specification.uiWireframes`.

```javascript
const mod = collected_data.modules[moduleCode];
const screens = mod.screens?.screens || [];

// Extract wireframes from screens that have mockup/wireframe data
wireframes: {
  [moduleCode]: screens
    .filter(s => s.wireframe || s.mockup || s.mockupFormat)
    .map(wf => ({
      screen: wf.screen || wf.name || wf.title || wf.id || "",
      section: wf.section || "",
      format: wf.mockupFormat || "ascii",                   // RENAME: mockupFormat → format
      content: wf.mockup || wf.ascii || wf.content || "",   // RENAME: mockup → content
      svgContent: null,
      description: wf.description || "",
      elements: wf.elements || [],
      actions: wf.actions || [],
      componentMapping: Array.isArray(wf.componentMapping) ? wf.componentMapping
        : typeof wf.componentMapping === 'object' && wf.componentMapping !== null
          ? Object.entries(wf.componentMapping).map(([k, v]) => ({ wireframeElement: k, reactComponent: v }))
          : [],
      layout: typeof wf.layout === 'object' ? wf.layout : null,
      permissionsRequired: wf.permissionsRequired || []
    }))
}
```

**e2eFlows[]:**
```javascript
e2eFlows: (master.consolidation?.e2eFlows || []).map(flow => ({
  name: flow.name,
  diagram: flow.steps.map(s => s.action + "(" + s.module + ")").join(" ──→ "),
  steps: flow.steps || [],
  actors: [...new Set(flow.steps.map(s => s.permission?.split(".")[0]).filter(Boolean))].join(", "),
  modules: [...new Set(flow.steps.map(s => s.module))].join(" → ")
}))
```

**dependencyGraph:**
```javascript
dependencyGraph: {
  nodes: (master.modules || []).map(m => ({
    id: m.code, label: m.code, type: m.featureType || "data-centric"
  })),
  edges: (master.dependencyGraph?.edges || []).map(e => ({
    from: e.from, to: e.to, description: e.description || ""
  }))
}
```

---

### 3. Generate SVG Wireframes (Parallel, Optional)

> This step is NEVER blocking — ASCII-only wireframes are acceptable.
> **Reference:** `business-analyse/references/wireframe-svg-style-guide.md` for full agent prompt template

1. Collect all wireframes where `content` exists and `svgContent` is null
2. Spawn parallel Task(sonnet) agents — ONE per wireframe
3. Validate results: strip markdown fences, verify SVG syntax
4. Inject valid SVGs into `EMBEDDED_ARTIFACTS.wireframes[moduleCode][index].svgContent`
5. Display: `"SVG wireframe generation: {successCount}/{total} generated, {failCount} fallback (ASCII)"`

---

## VALIDATION

- FEATURE_DATA.moduleSpecs must have ONE entry per module
- cadrage.scope uses HTML keys (vital/important/optional/excluded)
- Wireframe fields use format/content (NOT mockupFormat/mockup)

## NEXT STEP

Load: `steps/step-03-render.md`
