musubi-sdd 3.0.1 → 3.5.1
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.
- package/bin/musubi-change.js +623 -10
- package/bin/musubi-orchestrate.js +456 -0
- package/bin/musubi-trace.js +393 -0
- package/package.json +3 -2
- package/src/analyzers/impact-analyzer.js +682 -0
- package/src/integrations/cicd.js +782 -0
- package/src/integrations/documentation.js +740 -0
- package/src/integrations/examples.js +789 -0
- package/src/integrations/index.js +23 -0
- package/src/integrations/platforms.js +929 -0
- package/src/managers/delta-spec.js +484 -0
- package/src/monitoring/incident-manager.js +890 -0
- package/src/monitoring/index.js +633 -0
- package/src/monitoring/observability.js +938 -0
- package/src/monitoring/release-manager.js +622 -0
- package/src/orchestration/index.js +168 -0
- package/src/orchestration/orchestration-engine.js +409 -0
- package/src/orchestration/pattern-registry.js +319 -0
- package/src/orchestration/patterns/auto.js +386 -0
- package/src/orchestration/patterns/group-chat.js +395 -0
- package/src/orchestration/patterns/human-in-loop.js +506 -0
- package/src/orchestration/patterns/nested.js +322 -0
- package/src/orchestration/patterns/sequential.js +278 -0
- package/src/orchestration/patterns/swarm.js +395 -0
- package/src/orchestration/workflow-orchestrator.js +738 -0
- package/src/reporters/coverage-report.js +452 -0
- package/src/reporters/traceability-matrix-report.js +684 -0
- package/src/steering/advanced-validation.js +812 -0
- package/src/steering/auto-updater.js +670 -0
- package/src/steering/index.js +119 -0
- package/src/steering/quality-metrics.js +650 -0
- package/src/steering/template-constraints.js +789 -0
- package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +22 -0
- package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +21 -0
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +90 -28
- package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +32 -0
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +27 -0
- package/src/templates/agents/claude-code/skills/steering/SKILL.md +30 -0
- package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +21 -0
- package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +27 -0
- package/src/templates/agents/codex/AGENTS.md +36 -1
- package/src/templates/agents/cursor/AGENTS.md +36 -1
- package/src/templates/agents/gemini-cli/GEMINI.md +36 -1
- package/src/templates/agents/github-copilot/AGENTS.md +65 -1
- package/src/templates/agents/qwen-code/QWEN.md +36 -1
- package/src/templates/agents/windsurf/AGENTS.md +36 -1
- package/src/templates/shared/delta-spec-template.md +246 -0
- package/src/validators/delta-format.js +474 -0
- package/src/validators/traceability-validator.js +561 -0
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MUSUBI Traceability Matrix Report Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates interactive HTML reports for bidirectional traceability
|
|
5
|
+
* Implements Constitutional Article V: Complete Traceability
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs-extra');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Report format options
|
|
13
|
+
*/
|
|
14
|
+
const ReportFormat = {
|
|
15
|
+
HTML: 'html',
|
|
16
|
+
MARKDOWN: 'markdown',
|
|
17
|
+
JSON: 'json',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* TraceabilityMatrixReport - Generates visual traceability reports
|
|
22
|
+
*/
|
|
23
|
+
class TraceabilityMatrixReport {
|
|
24
|
+
constructor(workspaceRoot, options = {}) {
|
|
25
|
+
this.workspaceRoot = workspaceRoot;
|
|
26
|
+
this.options = {
|
|
27
|
+
outputDir: options.outputDir || 'traceability-reports',
|
|
28
|
+
theme: options.theme || 'light',
|
|
29
|
+
interactive: options.interactive !== false,
|
|
30
|
+
includeOrphaned: options.includeOrphaned !== false,
|
|
31
|
+
...options,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Generate traceability matrix report
|
|
37
|
+
*/
|
|
38
|
+
async generate(traceabilityData, format = ReportFormat.HTML) {
|
|
39
|
+
const reportData = this.prepareReportData(traceabilityData);
|
|
40
|
+
|
|
41
|
+
switch (format) {
|
|
42
|
+
case ReportFormat.HTML:
|
|
43
|
+
return this.generateHTML(reportData);
|
|
44
|
+
case ReportFormat.MARKDOWN:
|
|
45
|
+
return this.generateMarkdown(reportData);
|
|
46
|
+
case ReportFormat.JSON:
|
|
47
|
+
return this.generateJSON(reportData);
|
|
48
|
+
default:
|
|
49
|
+
return this.generateHTML(reportData);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Prepare report data
|
|
55
|
+
*/
|
|
56
|
+
prepareReportData(traceabilityData) {
|
|
57
|
+
const { forward, backward, orphaned, completeness } = traceabilityData;
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
timestamp: new Date().toISOString(),
|
|
61
|
+
forward: forward || [],
|
|
62
|
+
backward: backward || [],
|
|
63
|
+
orphaned: orphaned || { requirements: [], design: [], tasks: [], code: [], tests: [] },
|
|
64
|
+
completeness: completeness || {
|
|
65
|
+
forwardComplete: 0,
|
|
66
|
+
forwardTotal: 0,
|
|
67
|
+
forwardPercentage: 0,
|
|
68
|
+
backwardComplete: 0,
|
|
69
|
+
backwardTotal: 0,
|
|
70
|
+
backwardPercentage: 0,
|
|
71
|
+
},
|
|
72
|
+
summary: this.calculateSummary(traceabilityData),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Calculate summary statistics
|
|
78
|
+
*/
|
|
79
|
+
calculateSummary(data) {
|
|
80
|
+
const forward = data.forward || [];
|
|
81
|
+
const backward = data.backward || [];
|
|
82
|
+
const orphaned = data.orphaned || {};
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
totalRequirements: forward.length,
|
|
86
|
+
completeChains: forward.filter(f => f.complete).length,
|
|
87
|
+
incompleteChains: forward.filter(f => !f.complete).length,
|
|
88
|
+
totalTests: backward.length,
|
|
89
|
+
orphanedCount: {
|
|
90
|
+
requirements: (orphaned.requirements || []).length,
|
|
91
|
+
design: (orphaned.design || []).length,
|
|
92
|
+
tasks: (orphaned.tasks || []).length,
|
|
93
|
+
code: (orphaned.code || []).length,
|
|
94
|
+
tests: (orphaned.tests || []).length,
|
|
95
|
+
},
|
|
96
|
+
totalOrphaned: Object.values(orphaned).reduce((sum, arr) => sum + (arr?.length || 0), 0),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Generate HTML report
|
|
102
|
+
*/
|
|
103
|
+
generateHTML(data) {
|
|
104
|
+
const { theme } = this.options;
|
|
105
|
+
const isDark = theme === 'dark';
|
|
106
|
+
|
|
107
|
+
return `<!DOCTYPE html>
|
|
108
|
+
<html lang="en">
|
|
109
|
+
<head>
|
|
110
|
+
<meta charset="UTF-8">
|
|
111
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
112
|
+
<title>Traceability Matrix Report - MUSUBI</title>
|
|
113
|
+
<style>
|
|
114
|
+
:root {
|
|
115
|
+
--bg-primary: ${isDark ? '#1a1a2e' : '#f5f5f5'};
|
|
116
|
+
--bg-secondary: ${isDark ? '#16213e' : '#ffffff'};
|
|
117
|
+
--text-primary: ${isDark ? '#eee' : '#333'};
|
|
118
|
+
--text-secondary: ${isDark ? '#aaa' : '#666'};
|
|
119
|
+
--border-color: ${isDark ? '#333' : '#ddd'};
|
|
120
|
+
--success: #28a745;
|
|
121
|
+
--warning: #ffc107;
|
|
122
|
+
--danger: #dc3545;
|
|
123
|
+
--info: #17a2b8;
|
|
124
|
+
}
|
|
125
|
+
* { box-sizing: border-box; }
|
|
126
|
+
body {
|
|
127
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
128
|
+
background: var(--bg-primary);
|
|
129
|
+
color: var(--text-primary);
|
|
130
|
+
margin: 0;
|
|
131
|
+
padding: 20px;
|
|
132
|
+
line-height: 1.6;
|
|
133
|
+
}
|
|
134
|
+
.container { max-width: 1400px; margin: 0 auto; }
|
|
135
|
+
.header {
|
|
136
|
+
text-align: center;
|
|
137
|
+
margin-bottom: 30px;
|
|
138
|
+
padding: 20px;
|
|
139
|
+
background: var(--bg-secondary);
|
|
140
|
+
border-radius: 12px;
|
|
141
|
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
142
|
+
}
|
|
143
|
+
.header h1 { margin: 0 0 10px; font-size: 2em; }
|
|
144
|
+
.timestamp { color: var(--text-secondary); font-size: 0.9em; }
|
|
145
|
+
.stats-grid {
|
|
146
|
+
display: grid;
|
|
147
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
148
|
+
gap: 20px;
|
|
149
|
+
margin-bottom: 30px;
|
|
150
|
+
}
|
|
151
|
+
.stat-card {
|
|
152
|
+
background: var(--bg-secondary);
|
|
153
|
+
border-radius: 12px;
|
|
154
|
+
padding: 20px;
|
|
155
|
+
text-align: center;
|
|
156
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
157
|
+
}
|
|
158
|
+
.stat-card .value { font-size: 2.5em; font-weight: bold; margin: 10px 0; }
|
|
159
|
+
.stat-card .label { color: var(--text-secondary); }
|
|
160
|
+
.stat-card.success .value { color: var(--success); }
|
|
161
|
+
.stat-card.warning .value { color: var(--warning); }
|
|
162
|
+
.stat-card.danger .value { color: var(--danger); }
|
|
163
|
+
.section {
|
|
164
|
+
background: var(--bg-secondary);
|
|
165
|
+
border-radius: 12px;
|
|
166
|
+
padding: 20px;
|
|
167
|
+
margin-bottom: 20px;
|
|
168
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
169
|
+
}
|
|
170
|
+
.section h2 { margin-top: 0; border-bottom: 2px solid var(--border-color); padding-bottom: 10px; }
|
|
171
|
+
.tabs {
|
|
172
|
+
display: flex;
|
|
173
|
+
gap: 10px;
|
|
174
|
+
margin-bottom: 20px;
|
|
175
|
+
}
|
|
176
|
+
.tab {
|
|
177
|
+
padding: 10px 20px;
|
|
178
|
+
background: var(--bg-primary);
|
|
179
|
+
border: none;
|
|
180
|
+
border-radius: 8px;
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
color: var(--text-primary);
|
|
183
|
+
transition: background 0.3s;
|
|
184
|
+
}
|
|
185
|
+
.tab:hover { background: var(--border-color); }
|
|
186
|
+
.tab.active { background: var(--info); color: white; }
|
|
187
|
+
.matrix-table {
|
|
188
|
+
width: 100%;
|
|
189
|
+
border-collapse: collapse;
|
|
190
|
+
margin-top: 15px;
|
|
191
|
+
}
|
|
192
|
+
.matrix-table th, .matrix-table td {
|
|
193
|
+
padding: 12px;
|
|
194
|
+
text-align: left;
|
|
195
|
+
border-bottom: 1px solid var(--border-color);
|
|
196
|
+
}
|
|
197
|
+
.matrix-table th { background: var(--bg-primary); font-weight: 600; }
|
|
198
|
+
.matrix-table tr:hover { background: var(--bg-primary); }
|
|
199
|
+
.badge {
|
|
200
|
+
display: inline-block;
|
|
201
|
+
padding: 4px 8px;
|
|
202
|
+
border-radius: 4px;
|
|
203
|
+
font-size: 0.8em;
|
|
204
|
+
margin: 2px;
|
|
205
|
+
}
|
|
206
|
+
.badge.complete { background: #d4edda; color: #155724; }
|
|
207
|
+
.badge.incomplete { background: #f8d7da; color: #721c24; }
|
|
208
|
+
.badge.link { background: #cce5ff; color: #004085; }
|
|
209
|
+
.badge.orphan { background: #fff3cd; color: #856404; }
|
|
210
|
+
.collapsible {
|
|
211
|
+
cursor: pointer;
|
|
212
|
+
user-select: none;
|
|
213
|
+
}
|
|
214
|
+
.collapsible::before { content: '▶ '; transition: transform 0.3s; }
|
|
215
|
+
.collapsible.open::before { content: '▼ '; }
|
|
216
|
+
.collapsible-content { display: none; padding-left: 20px; }
|
|
217
|
+
.collapsible-content.open { display: block; }
|
|
218
|
+
.progress-container { display: flex; align-items: center; gap: 10px; }
|
|
219
|
+
.progress { flex: 1; background: var(--border-color); border-radius: 10px; height: 20px; overflow: hidden; }
|
|
220
|
+
.progress-bar { height: 100%; border-radius: 10px; transition: width 0.5s; }
|
|
221
|
+
.progress-bar.high { background: linear-gradient(90deg, #28a745, #5cb85c); }
|
|
222
|
+
.progress-bar.medium { background: linear-gradient(90deg, #ffc107, #ffda44); }
|
|
223
|
+
.progress-bar.low { background: linear-gradient(90deg, #dc3545, #e74c5c); }
|
|
224
|
+
.legend { display: flex; gap: 20px; flex-wrap: wrap; margin-top: 10px; }
|
|
225
|
+
.legend-item { display: flex; align-items: center; gap: 5px; }
|
|
226
|
+
.legend-color { width: 12px; height: 12px; border-radius: 3px; }
|
|
227
|
+
.chain-viz {
|
|
228
|
+
display: flex;
|
|
229
|
+
align-items: center;
|
|
230
|
+
gap: 5px;
|
|
231
|
+
flex-wrap: wrap;
|
|
232
|
+
}
|
|
233
|
+
.chain-node {
|
|
234
|
+
padding: 6px 12px;
|
|
235
|
+
border-radius: 6px;
|
|
236
|
+
font-size: 0.9em;
|
|
237
|
+
}
|
|
238
|
+
.chain-arrow { color: var(--text-secondary); }
|
|
239
|
+
.chain-node.req { background: #cce5ff; color: #004085; }
|
|
240
|
+
.chain-node.design { background: #d4edda; color: #155724; }
|
|
241
|
+
.chain-node.task { background: #fff3cd; color: #856404; }
|
|
242
|
+
.chain-node.code { background: #e2e3e5; color: #383d41; }
|
|
243
|
+
.chain-node.test { background: #f5c6cb; color: #721c24; }
|
|
244
|
+
.chain-node.missing { background: #f8d7da; color: #721c24; border: 2px dashed #dc3545; }
|
|
245
|
+
.filter-bar {
|
|
246
|
+
display: flex;
|
|
247
|
+
gap: 10px;
|
|
248
|
+
margin-bottom: 20px;
|
|
249
|
+
flex-wrap: wrap;
|
|
250
|
+
}
|
|
251
|
+
.filter-bar input {
|
|
252
|
+
padding: 10px 15px;
|
|
253
|
+
border: 1px solid var(--border-color);
|
|
254
|
+
border-radius: 8px;
|
|
255
|
+
background: var(--bg-primary);
|
|
256
|
+
color: var(--text-primary);
|
|
257
|
+
flex: 1;
|
|
258
|
+
min-width: 200px;
|
|
259
|
+
}
|
|
260
|
+
.filter-bar select {
|
|
261
|
+
padding: 10px 15px;
|
|
262
|
+
border: 1px solid var(--border-color);
|
|
263
|
+
border-radius: 8px;
|
|
264
|
+
background: var(--bg-primary);
|
|
265
|
+
color: var(--text-primary);
|
|
266
|
+
}
|
|
267
|
+
@media (max-width: 768px) {
|
|
268
|
+
.stats-grid { grid-template-columns: repeat(2, 1fr); }
|
|
269
|
+
.tabs { flex-wrap: wrap; }
|
|
270
|
+
}
|
|
271
|
+
</style>
|
|
272
|
+
</head>
|
|
273
|
+
<body>
|
|
274
|
+
<div class="container">
|
|
275
|
+
<div class="header">
|
|
276
|
+
<h1>📊 Traceability Matrix Report</h1>
|
|
277
|
+
<p class="timestamp">Generated: ${data.timestamp}</p>
|
|
278
|
+
<p>MUSUBI - Ultimate Specification Driven Development</p>
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
<!-- Statistics Overview -->
|
|
282
|
+
<div class="stats-grid">
|
|
283
|
+
<div class="stat-card ${data.completeness.forwardPercentage >= 80 ? 'success' : data.completeness.forwardPercentage >= 60 ? 'warning' : 'danger'}">
|
|
284
|
+
<div class="label">Forward Traceability</div>
|
|
285
|
+
<div class="value">${data.completeness.forwardPercentage}%</div>
|
|
286
|
+
<div class="label">${data.completeness.forwardComplete}/${data.completeness.forwardTotal} Complete</div>
|
|
287
|
+
</div>
|
|
288
|
+
<div class="stat-card ${data.completeness.backwardPercentage >= 80 ? 'success' : data.completeness.backwardPercentage >= 60 ? 'warning' : 'danger'}">
|
|
289
|
+
<div class="label">Backward Traceability</div>
|
|
290
|
+
<div class="value">${data.completeness.backwardPercentage}%</div>
|
|
291
|
+
<div class="label">${data.completeness.backwardComplete}/${data.completeness.backwardTotal} Complete</div>
|
|
292
|
+
</div>
|
|
293
|
+
<div class="stat-card ${data.summary.totalOrphaned === 0 ? 'success' : 'warning'}">
|
|
294
|
+
<div class="label">Orphaned Items</div>
|
|
295
|
+
<div class="value">${data.summary.totalOrphaned}</div>
|
|
296
|
+
<div class="label">Needs Linking</div>
|
|
297
|
+
</div>
|
|
298
|
+
<div class="stat-card">
|
|
299
|
+
<div class="label">Total Requirements</div>
|
|
300
|
+
<div class="value">${data.summary.totalRequirements}</div>
|
|
301
|
+
<div class="label">Tracked</div>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<!-- Forward Traceability Section -->
|
|
306
|
+
<div class="section">
|
|
307
|
+
<h2>🔗 Forward Traceability (Requirements → Tests)</h2>
|
|
308
|
+
<p>Tracing each requirement through design, tasks, code, and tests.</p>
|
|
309
|
+
|
|
310
|
+
<div class="progress-container">
|
|
311
|
+
<span>Progress:</span>
|
|
312
|
+
<div class="progress">
|
|
313
|
+
<div class="progress-bar ${data.completeness.forwardPercentage >= 80 ? 'high' : data.completeness.forwardPercentage >= 60 ? 'medium' : 'low'}"
|
|
314
|
+
style="width: ${data.completeness.forwardPercentage}%"></div>
|
|
315
|
+
</div>
|
|
316
|
+
<span>${data.completeness.forwardPercentage}%</span>
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
<div class="filter-bar">
|
|
320
|
+
<input type="text" id="forward-search" placeholder="Search requirements..." onkeyup="filterForward()">
|
|
321
|
+
<select id="forward-filter" onchange="filterForward()">
|
|
322
|
+
<option value="all">All</option>
|
|
323
|
+
<option value="complete">Complete Only</option>
|
|
324
|
+
<option value="incomplete">Incomplete Only</option>
|
|
325
|
+
</select>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<table class="matrix-table" id="forward-table">
|
|
329
|
+
<thead>
|
|
330
|
+
<tr>
|
|
331
|
+
<th>Requirement</th>
|
|
332
|
+
<th>Design</th>
|
|
333
|
+
<th>Tasks</th>
|
|
334
|
+
<th>Code</th>
|
|
335
|
+
<th>Tests</th>
|
|
336
|
+
<th>Status</th>
|
|
337
|
+
</tr>
|
|
338
|
+
</thead>
|
|
339
|
+
<tbody>
|
|
340
|
+
${this.generateForwardRows(data.forward)}
|
|
341
|
+
</tbody>
|
|
342
|
+
</table>
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<!-- Backward Traceability Section -->
|
|
346
|
+
<div class="section">
|
|
347
|
+
<h2>🔙 Backward Traceability (Tests → Requirements)</h2>
|
|
348
|
+
<p>Tracing each test back to its originating requirements.</p>
|
|
349
|
+
|
|
350
|
+
<div class="progress-container">
|
|
351
|
+
<span>Progress:</span>
|
|
352
|
+
<div class="progress">
|
|
353
|
+
<div class="progress-bar ${data.completeness.backwardPercentage >= 80 ? 'high' : data.completeness.backwardPercentage >= 60 ? 'medium' : 'low'}"
|
|
354
|
+
style="width: ${data.completeness.backwardPercentage}%"></div>
|
|
355
|
+
</div>
|
|
356
|
+
<span>${data.completeness.backwardPercentage}%</span>
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
<table class="matrix-table" id="backward-table">
|
|
360
|
+
<thead>
|
|
361
|
+
<tr>
|
|
362
|
+
<th>Test</th>
|
|
363
|
+
<th>Code</th>
|
|
364
|
+
<th>Tasks</th>
|
|
365
|
+
<th>Design</th>
|
|
366
|
+
<th>Requirements</th>
|
|
367
|
+
<th>Status</th>
|
|
368
|
+
</tr>
|
|
369
|
+
</thead>
|
|
370
|
+
<tbody>
|
|
371
|
+
${this.generateBackwardRows(data.backward)}
|
|
372
|
+
</tbody>
|
|
373
|
+
</table>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<!-- Orphaned Items Section -->
|
|
377
|
+
${this.options.includeOrphaned ? this.generateOrphanedSection(data.orphaned, data.summary) : ''}
|
|
378
|
+
|
|
379
|
+
<!-- Legend -->
|
|
380
|
+
<div class="section">
|
|
381
|
+
<h2>📋 Legend</h2>
|
|
382
|
+
<div class="legend">
|
|
383
|
+
<div class="legend-item">
|
|
384
|
+
<div class="legend-color" style="background: #d4edda;"></div>
|
|
385
|
+
<span>Complete Chain</span>
|
|
386
|
+
</div>
|
|
387
|
+
<div class="legend-item">
|
|
388
|
+
<div class="legend-color" style="background: #f8d7da;"></div>
|
|
389
|
+
<span>Incomplete Chain</span>
|
|
390
|
+
</div>
|
|
391
|
+
<div class="legend-item">
|
|
392
|
+
<div class="legend-color" style="background: #cce5ff;"></div>
|
|
393
|
+
<span>Linked Item</span>
|
|
394
|
+
</div>
|
|
395
|
+
<div class="legend-item">
|
|
396
|
+
<div class="legend-color" style="background: #fff3cd;"></div>
|
|
397
|
+
<span>Orphaned Item</span>
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
|
|
403
|
+
<script>
|
|
404
|
+
${this.options.interactive ? this.generateInteractiveScript() : ''}
|
|
405
|
+
</script>
|
|
406
|
+
</body>
|
|
407
|
+
</html>`;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Generate forward traceability rows
|
|
412
|
+
*/
|
|
413
|
+
generateForwardRows(forward) {
|
|
414
|
+
if (!forward || forward.length === 0) {
|
|
415
|
+
return '<tr><td colspan="6" style="text-align: center;">No requirements found</td></tr>';
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return forward.map(item => {
|
|
419
|
+
const req = item.requirement;
|
|
420
|
+
const reqId = req?.id || req?.file || 'Unknown';
|
|
421
|
+
|
|
422
|
+
return `
|
|
423
|
+
<tr data-complete="${item.complete}">
|
|
424
|
+
<td><code>${this.escapeHtml(reqId)}</code></td>
|
|
425
|
+
<td>${this.formatLinks(item.design, 'design')}</td>
|
|
426
|
+
<td>${this.formatLinks(item.tasks, 'task')}</td>
|
|
427
|
+
<td>${this.formatLinks(item.code, 'code')}</td>
|
|
428
|
+
<td>${this.formatLinks(item.tests, 'test')}</td>
|
|
429
|
+
<td>
|
|
430
|
+
<span class="badge ${item.complete ? 'complete' : 'incomplete'}">
|
|
431
|
+
${item.complete ? '✓ Complete' : '✗ Incomplete'}
|
|
432
|
+
</span>
|
|
433
|
+
</td>
|
|
434
|
+
</tr>
|
|
435
|
+
`;
|
|
436
|
+
}).join('');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Generate backward traceability rows
|
|
441
|
+
*/
|
|
442
|
+
generateBackwardRows(backward) {
|
|
443
|
+
if (!backward || backward.length === 0) {
|
|
444
|
+
return '<tr><td colspan="6" style="text-align: center;">No tests found</td></tr>';
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return backward.map(item => {
|
|
448
|
+
const test = item.test;
|
|
449
|
+
const testId = test?.file || test?.id || 'Unknown';
|
|
450
|
+
|
|
451
|
+
return `
|
|
452
|
+
<tr data-complete="${item.complete}">
|
|
453
|
+
<td><code>${this.escapeHtml(path.basename(testId))}</code></td>
|
|
454
|
+
<td>${this.formatLinks(item.code, 'code')}</td>
|
|
455
|
+
<td>${this.formatLinks(item.tasks, 'task')}</td>
|
|
456
|
+
<td>${this.formatLinks(item.design, 'design')}</td>
|
|
457
|
+
<td>${this.formatLinks(item.requirements, 'req')}</td>
|
|
458
|
+
<td>
|
|
459
|
+
<span class="badge ${item.complete ? 'complete' : 'incomplete'}">
|
|
460
|
+
${item.complete ? '✓ Traced' : '✗ Untraced'}
|
|
461
|
+
</span>
|
|
462
|
+
</td>
|
|
463
|
+
</tr>
|
|
464
|
+
`;
|
|
465
|
+
}).join('');
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Format linked items
|
|
470
|
+
*/
|
|
471
|
+
formatLinks(items, type) {
|
|
472
|
+
if (!items || items.length === 0) {
|
|
473
|
+
return `<span class="badge missing">None</span>`;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return items.map(item => {
|
|
477
|
+
const id = item?.id || item?.file || 'Unknown';
|
|
478
|
+
const displayId = path.basename(id);
|
|
479
|
+
return `<span class="badge link chain-node ${type}">${this.escapeHtml(displayId)}</span>`;
|
|
480
|
+
}).join(' ');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Generate orphaned items section
|
|
485
|
+
*/
|
|
486
|
+
generateOrphanedSection(orphaned, summary) {
|
|
487
|
+
if (summary.totalOrphaned === 0) {
|
|
488
|
+
return `
|
|
489
|
+
<div class="section">
|
|
490
|
+
<h2>🎉 Orphaned Items</h2>
|
|
491
|
+
<p style="color: var(--success);">All items are properly linked! No orphaned items found.</p>
|
|
492
|
+
</div>
|
|
493
|
+
`;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return `
|
|
497
|
+
<div class="section">
|
|
498
|
+
<h2>⚠️ Orphaned Items (${summary.totalOrphaned})</h2>
|
|
499
|
+
<p>These items are not linked to any requirements and may need attention.</p>
|
|
500
|
+
|
|
501
|
+
${this.generateOrphanedList('Requirements', orphaned.requirements)}
|
|
502
|
+
${this.generateOrphanedList('Design', orphaned.design)}
|
|
503
|
+
${this.generateOrphanedList('Tasks', orphaned.tasks)}
|
|
504
|
+
${this.generateOrphanedList('Code', orphaned.code)}
|
|
505
|
+
${this.generateOrphanedList('Tests', orphaned.tests)}
|
|
506
|
+
</div>
|
|
507
|
+
`;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Generate orphaned list for a category
|
|
512
|
+
*/
|
|
513
|
+
generateOrphanedList(category, items) {
|
|
514
|
+
if (!items || items.length === 0) return '';
|
|
515
|
+
|
|
516
|
+
return `
|
|
517
|
+
<div style="margin: 15px 0;">
|
|
518
|
+
<h3 class="collapsible" onclick="toggleCollapsible(this)">${category} (${items.length})</h3>
|
|
519
|
+
<div class="collapsible-content">
|
|
520
|
+
<ul>
|
|
521
|
+
${items.map(item => {
|
|
522
|
+
const id = item?.id || item?.file || 'Unknown';
|
|
523
|
+
return `<li><code>${this.escapeHtml(id)}</code></li>`;
|
|
524
|
+
}).join('')}
|
|
525
|
+
</ul>
|
|
526
|
+
</div>
|
|
527
|
+
</div>
|
|
528
|
+
`;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Generate interactive JavaScript
|
|
533
|
+
*/
|
|
534
|
+
generateInteractiveScript() {
|
|
535
|
+
return `
|
|
536
|
+
function filterForward() {
|
|
537
|
+
const search = document.getElementById('forward-search').value.toLowerCase();
|
|
538
|
+
const filter = document.getElementById('forward-filter').value;
|
|
539
|
+
const rows = document.querySelectorAll('#forward-table tbody tr');
|
|
540
|
+
|
|
541
|
+
rows.forEach(row => {
|
|
542
|
+
const text = row.textContent.toLowerCase();
|
|
543
|
+
const isComplete = row.getAttribute('data-complete') === 'true';
|
|
544
|
+
|
|
545
|
+
let show = text.includes(search);
|
|
546
|
+
|
|
547
|
+
if (filter === 'complete') show = show && isComplete;
|
|
548
|
+
if (filter === 'incomplete') show = show && !isComplete;
|
|
549
|
+
|
|
550
|
+
row.style.display = show ? '' : 'none';
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function toggleCollapsible(element) {
|
|
555
|
+
element.classList.toggle('open');
|
|
556
|
+
const content = element.nextElementSibling;
|
|
557
|
+
if (content) {
|
|
558
|
+
content.classList.toggle('open');
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Auto-expand orphaned sections
|
|
563
|
+
document.querySelectorAll('.collapsible').forEach(el => {
|
|
564
|
+
if (el.textContent.includes('(0)')) {
|
|
565
|
+
el.style.display = 'none';
|
|
566
|
+
el.nextElementSibling.style.display = 'none';
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
`;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Escape HTML
|
|
574
|
+
*/
|
|
575
|
+
escapeHtml(text) {
|
|
576
|
+
if (!text) return '';
|
|
577
|
+
return String(text)
|
|
578
|
+
.replace(/&/g, '&')
|
|
579
|
+
.replace(/</g, '<')
|
|
580
|
+
.replace(/>/g, '>')
|
|
581
|
+
.replace(/"/g, '"')
|
|
582
|
+
.replace(/'/g, ''');
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Generate Markdown report
|
|
587
|
+
*/
|
|
588
|
+
generateMarkdown(data) {
|
|
589
|
+
let md = `# Traceability Matrix Report
|
|
590
|
+
|
|
591
|
+
Generated: ${data.timestamp}
|
|
592
|
+
|
|
593
|
+
## Summary
|
|
594
|
+
|
|
595
|
+
| Metric | Value |
|
|
596
|
+
|--------|-------|
|
|
597
|
+
| Total Requirements | ${data.summary.totalRequirements} |
|
|
598
|
+
| Complete Chains | ${data.summary.completeChains} |
|
|
599
|
+
| Incomplete Chains | ${data.summary.incompleteChains} |
|
|
600
|
+
| Total Tests | ${data.summary.totalTests} |
|
|
601
|
+
| Orphaned Items | ${data.summary.totalOrphaned} |
|
|
602
|
+
|
|
603
|
+
## Completeness
|
|
604
|
+
|
|
605
|
+
| Direction | Complete | Total | Percentage |
|
|
606
|
+
|-----------|----------|-------|------------|
|
|
607
|
+
| Forward (Req → Test) | ${data.completeness.forwardComplete} | ${data.completeness.forwardTotal} | ${data.completeness.forwardPercentage}% |
|
|
608
|
+
| Backward (Test → Req) | ${data.completeness.backwardComplete} | ${data.completeness.backwardTotal} | ${data.completeness.backwardPercentage}% |
|
|
609
|
+
|
|
610
|
+
## Forward Traceability
|
|
611
|
+
|
|
612
|
+
| Requirement | Design | Tasks | Code | Tests | Status |
|
|
613
|
+
|-------------|--------|-------|------|-------|--------|
|
|
614
|
+
`;
|
|
615
|
+
|
|
616
|
+
for (const item of data.forward) {
|
|
617
|
+
const req = item.requirement;
|
|
618
|
+
const reqId = req?.id || req?.file || 'Unknown';
|
|
619
|
+
md += `| ${reqId} | ${item.design.length} | ${item.tasks.length} | ${item.code.length} | ${item.tests.length} | ${item.complete ? '✓' : '✗'} |\n`;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
md += `
|
|
623
|
+
## Backward Traceability
|
|
624
|
+
|
|
625
|
+
| Test | Code | Tasks | Design | Requirements | Status |
|
|
626
|
+
|------|------|-------|--------|--------------|--------|
|
|
627
|
+
`;
|
|
628
|
+
|
|
629
|
+
for (const item of data.backward) {
|
|
630
|
+
const test = item.test;
|
|
631
|
+
const testFile = test?.file ? path.basename(test.file) : 'Unknown';
|
|
632
|
+
md += `| ${testFile} | ${item.code.length} | ${item.tasks.length} | ${item.design.length} | ${item.requirements.length} | ${item.complete ? '✓' : '✗'} |\n`;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (data.summary.totalOrphaned > 0) {
|
|
636
|
+
md += `
|
|
637
|
+
## Orphaned Items
|
|
638
|
+
|
|
639
|
+
`;
|
|
640
|
+
const categories = ['requirements', 'design', 'tasks', 'code', 'tests'];
|
|
641
|
+
for (const cat of categories) {
|
|
642
|
+
const items = data.orphaned[cat] || [];
|
|
643
|
+
if (items.length > 0) {
|
|
644
|
+
md += `### ${cat.charAt(0).toUpperCase() + cat.slice(1)} (${items.length})
|
|
645
|
+
|
|
646
|
+
`;
|
|
647
|
+
for (const item of items) {
|
|
648
|
+
md += `- ${item?.id || item?.file || 'Unknown'}\n`;
|
|
649
|
+
}
|
|
650
|
+
md += '\n';
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
return md;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Generate JSON report
|
|
660
|
+
*/
|
|
661
|
+
generateJSON(data) {
|
|
662
|
+
return JSON.stringify(data, null, 2);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Save report to file
|
|
667
|
+
*/
|
|
668
|
+
async saveReport(report, filename, format = ReportFormat.HTML) {
|
|
669
|
+
const outputDir = path.join(this.workspaceRoot, this.options.outputDir);
|
|
670
|
+
await fs.ensureDir(outputDir);
|
|
671
|
+
|
|
672
|
+
const extension = format === ReportFormat.HTML ? '.html' : format === ReportFormat.MARKDOWN ? '.md' : '.json';
|
|
673
|
+
const fullPath = path.join(outputDir, filename + extension);
|
|
674
|
+
|
|
675
|
+
await fs.writeFile(fullPath, report, 'utf8');
|
|
676
|
+
|
|
677
|
+
return fullPath;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
module.exports = {
|
|
682
|
+
TraceabilityMatrixReport,
|
|
683
|
+
ReportFormat,
|
|
684
|
+
};
|