@atlashub/smartstack-cli 4.22.0 → 4.24.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.
Files changed (36) hide show
  1. package/package.json +1 -1
  2. package/templates/skills/ba-design-ui/SKILL.md +91 -0
  3. package/templates/skills/ba-design-ui/steps/step-01-screens.md +177 -0
  4. package/templates/skills/ba-design-ui/steps/step-02-wireframes.md +140 -0
  5. package/templates/skills/ba-design-ui/steps/step-03-navigation.md +134 -0
  6. package/templates/skills/ba-generate-html/SKILL.md +0 -1
  7. package/templates/skills/ba-generate-html/html/ba-interactive.html +950 -1055
  8. package/templates/skills/ba-generate-html/html/src/scripts/01-data-init.js +1 -2
  9. package/templates/skills/ba-generate-html/html/src/scripts/02-navigation.js +0 -1
  10. package/templates/skills/ba-generate-html/html/src/scripts/03-render-cadrage.js +0 -39
  11. package/templates/skills/ba-generate-html/html/src/scripts/05-render-specs.js +0 -1
  12. package/templates/skills/ba-generate-html/html/src/scripts/07-render-handoff.js +0 -1
  13. package/templates/skills/ba-generate-html/html/src/scripts/08-editing.js +133 -135
  14. package/templates/skills/ba-generate-html/html/src/scripts/10-comments.js +199 -199
  15. package/templates/skills/ba-generate-html/html/src/scripts/11-review-panel.js +165 -166
  16. package/templates/skills/ba-generate-html/html/src/styles/05-modules.css +444 -454
  17. package/templates/skills/ba-generate-html/html/src/template.html +0 -49
  18. package/templates/skills/ba-generate-html/references/data-build.md +176 -182
  19. package/templates/skills/ba-generate-html/references/data-mapping.md +295 -299
  20. package/templates/skills/ba-generate-html/steps/step-01-collect.md +4 -22
  21. package/templates/skills/ba-generate-html/steps/step-02-build-data.md +2 -11
  22. package/templates/skills/ba-review/SKILL.md +16 -1
  23. package/templates/skills/ba-review/steps/step-01-apply.md +60 -10
  24. package/templates/skills/business-analyse/SKILL.md +19 -15
  25. package/templates/skills/business-analyse/steps/step-03-specify.md +9 -38
  26. package/templates/skills/derive-prd/SKILL.md +9 -9
  27. package/templates/skills/derive-prd/references/acceptance-criteria.md +166 -116
  28. package/templates/skills/derive-prd/references/entity-domain-mapping.md +5 -5
  29. package/templates/skills/derive-prd/references/handoff-file-templates.md +12 -12
  30. package/templates/skills/derive-prd/references/handoff-mappings.md +13 -14
  31. package/templates/skills/derive-prd/references/handoff-seeddata-generation.md +1 -1
  32. package/templates/skills/derive-prd/references/readiness-scoring.md +41 -50
  33. package/templates/skills/derive-prd/schemas/handoff-schema.json +2 -2
  34. package/templates/skills/derive-prd/steps/step-00-validate.md +73 -52
  35. package/templates/skills/derive-prd/steps/step-01-transform.md +86 -43
  36. package/templates/skills/ba-generate-html/html/src/partials/cadrage-risks.html +0 -48
@@ -192,55 +192,6 @@
192
192
  </div>
193
193
  </div>
194
194
 
195
- <!-- SECTION: Risques et hypotheses -->
196
- <div class="section" id="cadrage-risks" style="display:none;" data-vibe-hide>
197
- <h2 class="section-title">Risques et hypotheses</h2>
198
- <p class="section-subtitle">Ce qui pourrait mal tourner et les certitudes non verifiees.</p>
199
-
200
- <h3 style="color: var(--text-bright); font-size: 1rem; margin-bottom: 0.75rem;">Risques identifies</h3>
201
- <div id="risksList"></div>
202
- <button class="add-btn" onclick="toggleForm('addRiskForm')">+ Ajouter un risque</button>
203
-
204
- <div class="inline-form" id="addRiskForm">
205
- <div class="inline-form-title">Nouveau risque</div>
206
- <div class="form-group">
207
- <label class="form-label">Description du risque</label>
208
- <input type="text" class="form-input" id="risk-desc" placeholder="Qu'est-ce qui pourrait mal tourner ?">
209
- </div>
210
- <div class="form-row">
211
- <div class="form-group">
212
- <label class="form-label">Probabilite</label>
213
- <select class="form-select" id="risk-probability">
214
- <option value="high">Forte</option>
215
- <option value="medium">Moyenne</option>
216
- <option value="low">Faible</option>
217
- </select>
218
- </div>
219
- <div class="form-group">
220
- <label class="form-label">Impact</label>
221
- <select class="form-select" id="risk-impact">
222
- <option value="high">Grave</option>
223
- <option value="medium">Moyen</option>
224
- <option value="low">Faible</option>
225
- </select>
226
- </div>
227
- </div>
228
- <div class="form-group">
229
- <label class="form-label">Mesure de prevention ou de reduction</label>
230
- <textarea class="form-textarea" id="risk-mitigation" placeholder="Comment prevenir ou reduire ce risque ?"></textarea>
231
- </div>
232
- <div class="form-actions">
233
- <button class="btn" onclick="toggleForm('addRiskForm')">Annuler</button>
234
- <button class="btn btn-primary" onclick="addRisk()">Ajouter ce risque</button>
235
- </div>
236
- </div>
237
-
238
- <h3 style="color: var(--text-bright); font-size: 1rem; margin: 2rem 0 0.75rem;">Hypotheses a verifier</h3>
239
- <div class="card">
240
- <div class="editable" contenteditable="true" data-field="risks.assumptions" data-placeholder="Quelles hypotheses faites-vous sur ce projet sans les avoir verifiees ? (une par ligne)"></div>
241
- </div>
242
- </div>
243
-
244
195
  <!-- SECTION: Criteres de reussite -->
245
196
  <div class="section" id="cadrage-success" style="display:none;">
246
197
  <h2 class="section-title">Criteres de reussite</h2>
@@ -1,182 +1,176 @@
1
- # FEATURE_DATA & EMBEDDED_ARTIFACTS Build Reference
2
-
3
- > Reference for `/ba-generate-html` skill (step-02-build-data) — complete mapping specification for the interactive HTML.
4
- > **Canonical field mapping reference:** See also `data-mapping.md` for the authoritative field name mapping between module JSON files and HTML data objects.
5
-
6
- ## FEATURE_DATA Object
7
-
8
- ```javascript
9
- const FEATURE_DATA = {
10
- metadata: {
11
- applicationName: master.application_name,
12
- applicationId: master.feature_id,
13
- version: master.version,
14
- createdAt: ISO_TIMESTAMP,
15
- analysisMode: master.metadata.analysisMode || "interactive" // always "interactive" — analysis is always interactive
16
- },
17
- cadrage: {
18
- // CONTEXT SECTION — merged from problem/asIs/toBe (lean cadrage)
19
- context: {
20
- problem: master.cadrage.problem || "", // flat string from index.json
21
- trigger: master.cadrage.trigger || "", // flat string from index.json
22
- currentSituation: master.cadrage.asIs || "", // flat string from index.json
23
- desiredSituation: master.cadrage.toBe || "", // flat string from index.json
24
- painPoints: (master.cadrage.stakeholders || []).flatMap(s => s.painPoints || []),
25
- acceptanceCriteria: (master.cadrage.acceptanceCriteria || []).map(c => c.criterion)
26
- },
27
- scope: {
28
- // CONVERT index.json keys to HTML keys:
29
- // mustHave → vital, shouldHave → important, couldHave → optional, outOfScope → excluded
30
- // CRITICAL: Preserve per-item `module` assignment from coverageMatrix (NOT modules[0])
31
- vital: (master.cadrage.globalScope?.mustHave || master.cadrage.coverageMatrix?.filter(i => i.category === "mustHave") || [])
32
- .map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null }),
33
- important: (master.cadrage.globalScope?.shouldHave || master.cadrage.coverageMatrix?.filter(i => i.category === "shouldHave") || [])
34
- .map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null }),
35
- optional: (master.cadrage.globalScope?.couldHave || master.cadrage.coverageMatrix?.filter(i => i.category === "couldHave") || [])
36
- .map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null }),
37
- excluded: (master.cadrage.globalScope?.outOfScope || master.cadrage.coverageMatrix?.filter(i => i.category === "outOfScope") || [])
38
- .map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null })
39
- },
40
- risks: (master.cadrage.risks || []).map(r => ({
41
- description: r.description,
42
- probability: r.probability,
43
- impact: r.impact,
44
- mitigation: r.mitigation
45
- })),
46
- stakeholders: (master.cadrage.stakeholders || []).map(s => ({
47
- role: s.role,
48
- function: s.function,
49
- tasks: s.tasks || [],
50
- frequency: s.frequency || "",
51
- access: s.involvement || "",
52
- frustrations: (s.painPoints || []).join("\n")
53
- }))
54
- },
55
- modules: [
56
- // FOR EACH module in master.modules[]:
57
- {
58
- code: module.code, // PascalCase key (e.g., "GestionTemps")
59
- name: module.name || module.code, // Display name (e.g., "Gestion du temps") — MANDATORY
60
- description: module.description || "",
61
- featureType: module.featureType || "data-centric",
62
- priority: module.priority || "must",
63
- estimatedComplexity: module.estimatedComplexity || "medium",
64
- entities: module.entities || [],
65
- anticipatedSections: module.anticipatedSections || [],
66
- dependencies: module.dependencies || [],
67
- dependents: module.dependents || []
68
- }
69
- ],
70
- moduleSpecs: {
71
- // CRITICAL: Must have ONE entry per module with ALL module data
72
- "{moduleCode}": {
73
- useCases: module.specification.useCases || [],
74
- businessRules: module.analysis.businessRules || [],
75
- // ENTITY SAFETY NET: map fields[] attributes[] if agent deviated from canonical format
76
- entities: (module.analysis.entities || []).map(ent => ({
77
- name: ent.name,
78
- description: ent.description || "",
79
- attributes: (ent.attributes || []).length > 0
80
- ? ent.attributes.map(a => ({ name: a.name, description: a.description || "" }))
81
- : (ent.fields || []).map(f => ({ name: f.name, description: f.description || f.type || "" })),
82
- relationships: (ent.relationships || []).length > 0
83
- ? ent.relationships.map(r =>
84
- typeof r === 'string' ? r : `${r.target} (${r.type}) - ${r.description || ""}`)
85
- : (ent.fields || []).filter(f => f.foreignKey)
86
- .map(f => `${f.foreignKey.table} (N:1) - ${f.description || f.name}`)
87
- })),
88
- permissions: module.specification.permissionMatrix?.permissions || [],
89
- apiEndpoints: module.specification.apiEndpoints || []
90
- }
91
- },
92
- consolidation: {
93
- integrations: master.consolidation.crossModuleInteractions || [],
94
- sharedEntities: master.consolidation.sharedEntities || [],
95
- sequenceDiagrams: master.consolidation.e2eFlows || []
96
- },
97
- handoff: {
98
- complexity: master.handoff.complexity,
99
- implementationStrategy: master.handoff.implementationStrategy,
100
- moduleOrder: master.handoff.moduleOrder,
101
- filesToCreate: master.handoff.filesToCreate,
102
- brToCodeMapping: master.handoff.brToCodeMapping,
103
- apiEndpointSummary: master.handoff.apiEndpointSummary
104
- }
105
- };
106
- ```
107
-
108
- ### Build Process
109
-
110
- 1. Extract metadata from master index.json
111
- 2. Extract cadrage from master index.json (CONVERT scope keys)
112
- 3. Extract stakeholders from master.stakeholders
113
- 4. Iterate ALL modules and populate moduleSpecs (THIS IS CRITICAL — empty moduleSpecs = BUG)
114
- 5. For EACH module, extract: useCases, businessRules, entities, permissions, apiEndpoints
115
- 6. Extract consolidation data (integrations, shared entities, E2E flows)
116
- 7. Extract handoff section (complexity, strategy, module order, file counts)
117
-
118
- ## EMBEDDED_ARTIFACTS Object
119
-
120
- > **CRITICAL:** The wireframes object is keyed by moduleCode (NOT a flat array).
121
- > Field names MUST be renamed from module JSON files: `mockupFormat` → `format`, `mockup` → `content`.
122
- > The HTML renderer reads `wf.format` and `wf.content` using module JSON file names will produce EMPTY mockups.
123
-
124
- ```javascript
125
- const EMBEDDED_ARTIFACTS = {
126
- wireframes: {
127
- // PER-MODULE keyed object (NOT a flat array)
128
- // FOR EACH module: extract from specification.uiWireframes[] OR specification.wireframes[] (SAFETY NET)
129
- // IMPORTANT: The agent may write wireframes under either key name always check BOTH
130
- // Read from module JSON files
131
- [moduleCode]: (moduleFeature.specification.uiWireframes || moduleFeature.specification.wireframes || []).map(wf => ({
132
- screen: wf.screen || wf.name || wf.title || wf.id || "", // SAFETY NET: fallback name/title/id → screen
133
- section: wf.section || "", // e.g. "list"
134
- format: wf.mockupFormat || "ascii", // RENAME: mockupFormat format
135
- content: wf.mockup || wf.ascii || wf.content || "", // SAFETY NET: mockup/ascii/content → content
136
- svgContent: null, // Populated by SVG generation step (see wireframe-svg-style-guide.md)
137
- description: wf.description || "",
138
- elements: wf.elements || [], // [{ id, type, label }] or ["DataGrid", ...]
139
- actions: wf.actions || [], // [{ trigger, action }] or ["filter", ...]
140
- // SAFETY NET: convert object → array format if agent used { "Header": "PageHeader" } instead of [{ wireframeElement, reactComponent }]
141
- componentMapping: Array.isArray(wf.componentMapping) ? wf.componentMapping
142
- : typeof wf.componentMapping === 'object' && wf.componentMapping !== null
143
- ? Object.entries(wf.componentMapping).map(([k, v]) => ({ wireframeElement: k, reactComponent: v }))
144
- : [],
145
- layout: typeof wf.layout === 'object' ? wf.layout : null, // SAFETY NET: ignore string layout ("grid")
146
- permissionsRequired: wf.permissionsRequired || []
147
- }))
148
- },
149
- e2eFlows: (master.consolidation?.e2eFlows || []).map(flow => ({
150
- name: flow.name,
151
- diagram: flow.steps.map(s => `${s.action}(${s.module})`).join(" ──→ "),
152
- steps: flow.steps || [],
153
- actors: [...new Set(flow.steps.map(s => s.permission?.split(".")[0]).filter(Boolean))].join(", "),
154
- modules: [...new Set(flow.steps.map(s => s.module))].join(" → ")
155
- })),
156
- dependencyGraph: {
157
- nodes: (master.modules || []).map(m => ({
158
- id: m.code, label: m.code, type: m.featureType || "data-centric"
159
- })),
160
- edges: (master.dependencyGraph?.edges || []).map(e => ({
161
- from: e.from, to: e.to, description: e.description || ""
162
- }))
163
- }
164
- };
165
- ```
166
-
167
- ### Artifact Gathering
168
-
169
- 1. For EACH module: read `specification.uiWireframes[]` OR `specification.wireframes[]` (check BOTH keys — agent may use either), **rename fields** (`mockupFormat`→`format`, `mockup`/`ascii`/`content`→`content`, `screen`/`name`/`title`→`screen`), store under `wireframes[moduleCode]`
170
- 2. Read master's `consolidation.e2eFlows[]` and build e2eFlows array with diagram generation
171
- 3. Read master's `dependencyGraph` and build nodes/edges
172
- 4. Serialize as JSON with 2-space indentation
173
- 5. **SVG Wireframe Enrichment** (after wireframe extraction): For EACH wireframe with `content` (ASCII) and `svgContent === null`, generate SVG using parallel Task(sonnet) agents. See [wireframe-svg-style-guide.md](wireframe-svg-style-guide.md) for the complete prompt template and orchestration process. If generation fails for any wireframe → leave `svgContent` as null (graceful fallback to ASCII).
174
-
175
- ## Placeholder Replacement
176
-
177
- 1. Serialize FEATURE_DATA as JSON (2-space indent)
178
- 2. Serialize EMBEDDED_ARTIFACTS as JSON (2-space indent)
179
- 3. Replace `{{FEATURE_DATA}}` with serialized FEATURE_DATA
180
- 4. Replace `{{EMBEDDED_ARTIFACTS}}` with serialized EMBEDDED_ARTIFACTS
181
- 5. Replace `{{APPLICATION_NAME}}`, `{{APPLICATION_ID}}`, `{{VERSION}}`, `{{CREATED_AT}}`
182
- 6. Verify: grep for `{{` to confirm all placeholders replaced
1
+ # FEATURE_DATA & EMBEDDED_ARTIFACTS Build Reference
2
+
3
+ > Reference for `/ba-generate-html` skill (step-02-build-data) — complete mapping specification for the interactive HTML.
4
+ > **Canonical field mapping reference:** See also `data-mapping.md` for the authoritative field name mapping between module JSON files and HTML data objects.
5
+
6
+ ## FEATURE_DATA Object
7
+
8
+ ```javascript
9
+ const FEATURE_DATA = {
10
+ metadata: {
11
+ applicationName: master.application_name,
12
+ applicationId: master.feature_id,
13
+ version: master.version,
14
+ createdAt: ISO_TIMESTAMP,
15
+ analysisMode: master.metadata.analysisMode || "interactive" // always "interactive" — analysis is always interactive
16
+ },
17
+ cadrage: {
18
+ // CONTEXT SECTION — merged from problem/asIs/toBe (lean cadrage)
19
+ context: {
20
+ problem: master.cadrage.problem || "", // flat string from index.json
21
+ trigger: master.cadrage.trigger || "", // flat string from index.json
22
+ currentSituation: master.cadrage.asIs || "", // flat string from index.json
23
+ desiredSituation: master.cadrage.toBe || "", // flat string from index.json
24
+ painPoints: (master.cadrage.stakeholders || []).flatMap(s => s.painPoints || []),
25
+ acceptanceCriteria: (master.cadrage.acceptanceCriteria || []).map(c => c.criterion)
26
+ },
27
+ scope: {
28
+ // CONVERT index.json keys to HTML keys:
29
+ // mustHave → vital, shouldHave → important, couldHave → optional, outOfScope → excluded
30
+ // CRITICAL: Preserve per-item `module` assignment from coverageMatrix (NOT modules[0])
31
+ vital: (master.cadrage.globalScope?.mustHave || master.cadrage.coverageMatrix?.filter(i => i.category === "mustHave") || [])
32
+ .map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null }),
33
+ important: (master.cadrage.globalScope?.shouldHave || master.cadrage.coverageMatrix?.filter(i => i.category === "shouldHave") || [])
34
+ .map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null }),
35
+ optional: (master.cadrage.globalScope?.couldHave || master.cadrage.coverageMatrix?.filter(i => i.category === "couldHave") || [])
36
+ .map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null }),
37
+ excluded: (master.cadrage.globalScope?.outOfScope || master.cadrage.coverageMatrix?.filter(i => i.category === "outOfScope") || [])
38
+ .map(item => typeof item === 'string' ? { name: item, description: "", module: null } : { name: item.item || item, description: item.notes || "", module: item.module || null })
39
+ },
40
+ stakeholders: (master.cadrage.stakeholders || []).map(s => ({
41
+ role: s.role,
42
+ function: s.function,
43
+ tasks: s.tasks || [],
44
+ frequency: s.frequency || "",
45
+ access: s.involvement || "",
46
+ frustrations: (s.painPoints || []).join("\n")
47
+ }))
48
+ },
49
+ modules: [
50
+ // FOR EACH module in master.modules[]:
51
+ {
52
+ code: module.code, // PascalCase key (e.g., "GestionTemps")
53
+ name: module.name || module.code, // Display name (e.g., "Gestion du temps") — MANDATORY
54
+ description: module.description || "",
55
+ featureType: module.featureType || "data-centric",
56
+ priority: module.priority || "must",
57
+ estimatedComplexity: module.estimatedComplexity || "medium",
58
+ entities: module.entities || [],
59
+ anticipatedSections: module.anticipatedSections || [],
60
+ dependencies: module.dependencies || [],
61
+ dependents: module.dependents || []
62
+ }
63
+ ],
64
+ moduleSpecs: {
65
+ // CRITICAL: Must have ONE entry per module with ALL module data
66
+ "{moduleCode}": {
67
+ useCases: module.specification.useCases || [],
68
+ businessRules: module.analysis.businessRules || [],
69
+ // ENTITY SAFETY NET: map fields[] → attributes[] if agent deviated from canonical format
70
+ entities: (module.analysis.entities || []).map(ent => ({
71
+ name: ent.name,
72
+ description: ent.description || "",
73
+ attributes: (ent.attributes || []).length > 0
74
+ ? ent.attributes.map(a => ({ name: a.name, type: a.type || "string", required: a.required || false, description: a.description || "" }))
75
+ : (ent.fields || []).map(f => ({ name: f.name, type: f.type || "string", required: f.required || false, description: f.description || "" })),
76
+ relationships: (ent.relationships || []).length > 0
77
+ ? ent.relationships.map(r =>
78
+ typeof r === 'string' ? r : `${r.target} (${r.type}) - ${r.description || ""}`)
79
+ : (ent.fields || []).filter(f => f.foreignKey)
80
+ .map(f => `${f.foreignKey.table} (N:1) - ${f.description || f.name}`)
81
+ })),
82
+ permissions: module.specification.permissionMatrix?.permissions || [],
83
+ apiEndpoints: module.specification.apiEndpoints || []
84
+ }
85
+ },
86
+ consolidation: {
87
+ integrations: master.consolidation.crossModuleInteractions || [],
88
+ sharedEntities: master.consolidation.sharedEntities || [],
89
+ sequenceDiagrams: master.consolidation.e2eFlows || []
90
+ },
91
+ handoff: {
92
+ complexity: master.handoff.complexity,
93
+ implementationStrategy: master.handoff.implementationStrategy,
94
+ moduleOrder: master.handoff.moduleOrder,
95
+ filesToCreate: master.handoff.filesToCreate,
96
+ brToCodeMapping: master.handoff.brToCodeMapping,
97
+ apiEndpointSummary: master.handoff.apiEndpointSummary
98
+ }
99
+ };
100
+ ```
101
+
102
+ ### Build Process
103
+
104
+ 1. Extract metadata from master index.json
105
+ 2. Extract cadrage from master index.json (CONVERT scope keys)
106
+ 3. Extract stakeholders from master.stakeholders
107
+ 4. Iterate ALL modules and populate moduleSpecs (THIS IS CRITICAL — empty moduleSpecs = BUG)
108
+ 5. For EACH module, extract: useCases, businessRules, entities, permissions, apiEndpoints
109
+ 6. Extract consolidation data (integrations, shared entities, E2E flows)
110
+ 7. Extract handoff section (complexity, strategy, module order, file counts)
111
+
112
+ ## EMBEDDED_ARTIFACTS Object
113
+
114
+ > **CRITICAL:** The wireframes object is keyed by moduleCode (NOT a flat array).
115
+ > Field names MUST be renamed from module JSON files: `mockupFormat` → `format`, `mockup` → `content`.
116
+ > The HTML renderer reads `wf.format` and `wf.content` using module JSON file names will produce EMPTY mockups.
117
+
118
+ ```javascript
119
+ const EMBEDDED_ARTIFACTS = {
120
+ wireframes: {
121
+ // PER-MODULE keyed object (NOT a flat array)
122
+ // FOR EACH module: extract from specification.uiWireframes[] OR specification.wireframes[] (SAFETY NET)
123
+ // IMPORTANT: The agent may write wireframes under either key name — always check BOTH
124
+ // Read from module JSON files
125
+ [moduleCode]: (moduleFeature.specification.uiWireframes || moduleFeature.specification.wireframes || []).map(wf => ({
126
+ screen: wf.screen || wf.name || wf.title || wf.id || "", // SAFETY NET: fallback name/title/id → screen
127
+ section: wf.section || "", // e.g. "list"
128
+ format: wf.mockupFormat || "ascii", // RENAME: mockupFormat format
129
+ content: wf.mockup || wf.ascii || wf.content || "", // SAFETY NET: mockup/ascii/content content
130
+ svgContent: null, // Populated by SVG generation step (see wireframe-svg-style-guide.md)
131
+ description: wf.description || "",
132
+ elements: wf.elements || [], // [{ id, type, label }] or ["DataGrid", ...]
133
+ actions: wf.actions || [], // [{ trigger, action }] or ["filter", ...]
134
+ // SAFETY NET: convert object → array format if agent used { "Header": "PageHeader" } instead of [{ wireframeElement, reactComponent }]
135
+ componentMapping: Array.isArray(wf.componentMapping) ? wf.componentMapping
136
+ : typeof wf.componentMapping === 'object' && wf.componentMapping !== null
137
+ ? Object.entries(wf.componentMapping).map(([k, v]) => ({ wireframeElement: k, reactComponent: v }))
138
+ : [],
139
+ layout: typeof wf.layout === 'object' ? wf.layout : null, // SAFETY NET: ignore string layout ("grid")
140
+ permissionsRequired: wf.permissionsRequired || []
141
+ }))
142
+ },
143
+ e2eFlows: (master.consolidation?.e2eFlows || []).map(flow => ({
144
+ name: flow.name,
145
+ diagram: flow.steps.map(s => `${s.action}(${s.module})`).join(" ──→ "),
146
+ steps: flow.steps || [],
147
+ actors: [...new Set(flow.steps.map(s => s.permission?.split(".")[0]).filter(Boolean))].join(", "),
148
+ modules: [...new Set(flow.steps.map(s => s.module))].join(" → ")
149
+ })),
150
+ dependencyGraph: {
151
+ nodes: (master.modules || []).map(m => ({
152
+ id: m.code, label: m.code, type: m.featureType || "data-centric"
153
+ })),
154
+ edges: (master.dependencyGraph?.edges || []).map(e => ({
155
+ from: e.from, to: e.to, description: e.description || ""
156
+ }))
157
+ }
158
+ };
159
+ ```
160
+
161
+ ### Artifact Gathering
162
+
163
+ 1. For EACH module: read `specification.uiWireframes[]` OR `specification.wireframes[]` (check BOTH keys — agent may use either), **rename fields** (`mockupFormat`→`format`, `mockup`/`ascii`/`content`→`content`, `screen`/`name`/`title`→`screen`), store under `wireframes[moduleCode]`
164
+ 2. Read master's `consolidation.e2eFlows[]` and build e2eFlows array with diagram generation
165
+ 3. Read master's `dependencyGraph` and build nodes/edges
166
+ 4. Serialize as JSON with 2-space indentation
167
+ 5. **SVG Wireframe Enrichment** (after wireframe extraction): For EACH wireframe with `content` (ASCII) and `svgContent === null`, generate SVG using parallel Task(sonnet) agents. See [wireframe-svg-style-guide.md](wireframe-svg-style-guide.md) for the complete prompt template and orchestration process. If generation fails for any wireframe → leave `svgContent` as null (graceful fallback to ASCII).
168
+
169
+ ## Placeholder Replacement
170
+
171
+ 1. Serialize FEATURE_DATA as JSON (2-space indent)
172
+ 2. Serialize EMBEDDED_ARTIFACTS as JSON (2-space indent)
173
+ 3. Replace `{{FEATURE_DATA}}` with serialized FEATURE_DATA
174
+ 4. Replace `{{EMBEDDED_ARTIFACTS}}` with serialized EMBEDDED_ARTIFACTS
175
+ 5. Replace `{{APPLICATION_NAME}}`, `{{APPLICATION_ID}}`, `{{VERSION}}`, `{{CREATED_AT}}`
176
+ 6. Verify: grep for `{{` to confirm all placeholders replaced