aiwg
Version:
Cognitive architecture for AI-augmented software development with structured memory, ensemble validation, and closed-loop correction. FAIR-aligned artifacts, 84% cost reduction via human-in-the-loop, standards adopted by 100+ organizations.
349 lines (298 loc) • 12.6 kB
text/typescript
/**
* Matrix Generator - Generate traceability matrices in various formats
* Supports: CSV, Excel (TSV), Markdown, HTML
*/
export interface TraceabilityMatrix {
requirements: string[];
code: string[];
tests: string[];
links: MatrixCell[][];
}
export interface MatrixCell {
requirementId: string;
itemPath: string;
itemType: 'code' | 'test' | 'documentation';
linked: boolean;
verified: boolean;
confidence: number;
}
export interface MatrixExportOptions {
format: 'csv' | 'excel' | 'markdown' | 'html';
includeVerification?: boolean;
includeConfidence?: boolean;
sortBy?: 'requirement' | 'coverage' | 'type';
}
/**
* MatrixGenerator - Generate and export traceability matrices
*/
export class MatrixGenerator {
/**
* Generate traceability matrix from links
*/
generateMatrix(
requirements: string[],
codeFiles: string[],
testFiles: string[],
links: Map<string, { code: string[]; tests: string[] }>
): TraceabilityMatrix {
const matrix: MatrixCell[][] = [];
// Build matrix rows (one per requirement)
for (const reqId of requirements) {
const row: MatrixCell[] = [];
const reqLinks = links.get(reqId) || { code: [], tests: [] };
// Add code file cells
for (const codeFile of codeFiles) {
row.push({
requirementId: reqId,
itemPath: codeFile,
itemType: 'code',
linked: reqLinks.code.includes(codeFile),
verified: reqLinks.code.includes(codeFile),
confidence: reqLinks.code.includes(codeFile) ? 1.0 : 0.0
});
}
// Add test file cells
for (const testFile of testFiles) {
row.push({
requirementId: reqId,
itemPath: testFile,
itemType: 'test',
linked: reqLinks.tests.includes(testFile),
verified: reqLinks.tests.includes(testFile),
confidence: reqLinks.tests.includes(testFile) ? 1.0 : 0.0
});
}
matrix.push(row);
}
return {
requirements,
code: codeFiles,
tests: testFiles,
links: matrix
};
}
/**
* Export matrix to CSV format
*/
exportToCSV(matrix: TraceabilityMatrix, options: MatrixExportOptions = { format: 'csv' }): string {
const lines: string[] = [];
// Header row
const headers = ['Requirement', 'Code Files', 'Tests', 'Documentation', 'Coverage'];
if (options.includeVerification) headers.push('Verified');
if (options.includeConfidence) headers.push('Confidence');
lines.push(headers.join(','));
// Data rows
for (let i = 0; i < matrix.requirements.length; i++) {
const reqId = matrix.requirements[i];
const row = matrix.links[i];
// Find linked items
const linkedCode = row.filter(cell => cell.itemType === 'code' && cell.linked).map(cell => cell.itemPath);
const linkedTests = row.filter(cell => cell.itemType === 'test' && cell.linked).map(cell => cell.itemPath);
const linkedDocs = row.filter(cell => cell.itemType === 'documentation' && cell.linked).map(cell => cell.itemPath);
// Calculate coverage
const hasCode = linkedCode.length > 0;
const hasTests = linkedTests.length > 0;
const hasDocs = linkedDocs.length > 0;
const coverage = ((hasCode ? 33 : 0) + (hasTests ? 33 : 0) + (hasDocs ? 34 : 0));
const csvRow = [
this.escapeCSV(reqId),
this.escapeCSV(linkedCode.join('; ') || 'NONE'),
this.escapeCSV(linkedTests.join('; ') || 'NONE'),
this.escapeCSV(linkedDocs.join('; ') || 'NONE'),
`${coverage}%`
];
if (options.includeVerification) {
const verified = row.every(cell => !cell.linked || cell.verified);
csvRow.push(verified ? 'YES' : 'NO');
}
if (options.includeConfidence) {
const avgConfidence = row.length > 0
? row.reduce((sum, cell) => sum + cell.confidence, 0) / row.length
: 0;
csvRow.push(`${(avgConfidence * 100).toFixed(1)}%`);
}
lines.push(csvRow.join(','));
}
return lines.join('\n');
}
/**
* Export matrix to Excel (TSV) format
*/
exportToExcel(matrix: TraceabilityMatrix, options: MatrixExportOptions = { format: 'excel' }): string {
// Excel export is TSV (tab-separated values)
const csv = this.exportToCSV(matrix, options);
return csv.replace(/,/g, '\t');
}
/**
* Export matrix to Markdown format
*/
exportToMarkdown(matrix: TraceabilityMatrix, options: MatrixExportOptions = { format: 'markdown' }): string {
const lines: string[] = [];
// Title
lines.push('# Traceability Matrix\n');
lines.push(`Generated: ${new Date().toISOString()}\n`);
lines.push(`Total Requirements: ${matrix.requirements.length}\n`);
// Table header
const headers = ['Requirement', 'Code Files', 'Tests', 'Documentation', 'Coverage'];
if (options.includeVerification) headers.push('Verified');
if (options.includeConfidence) headers.push('Confidence');
lines.push('| ' + headers.join(' | ') + ' |');
lines.push('|' + headers.map(() => '---').join('|') + '|');
// Data rows
for (let i = 0; i < matrix.requirements.length; i++) {
const reqId = matrix.requirements[i];
const row = matrix.links[i];
// Find linked items
const linkedCode = row.filter(cell => cell.itemType === 'code' && cell.linked).map(cell => this.formatPath(cell.itemPath));
const linkedTests = row.filter(cell => cell.itemType === 'test' && cell.linked).map(cell => this.formatPath(cell.itemPath));
const linkedDocs = row.filter(cell => cell.itemType === 'documentation' && cell.linked).map(cell => this.formatPath(cell.itemPath));
// Calculate coverage
const hasCode = linkedCode.length > 0;
const hasTests = linkedTests.length > 0;
const hasDocs = linkedDocs.length > 0;
const coverage = ((hasCode ? 33 : 0) + (hasTests ? 33 : 0) + (hasDocs ? 34 : 0));
const mdRow = [
`**${reqId}**`,
linkedCode.length > 0 ? linkedCode.join('<br>') : '*NONE*',
linkedTests.length > 0 ? linkedTests.join('<br>') : '*NONE*',
linkedDocs.length > 0 ? linkedDocs.join('<br>') : '*NONE*',
this.getCoverageEmoji(coverage) + ` ${coverage}%`
];
if (options.includeVerification) {
const verified = row.every(cell => !cell.linked || cell.verified);
mdRow.push(verified ? '✅' : '❌');
}
if (options.includeConfidence) {
const avgConfidence = row.length > 0
? row.reduce((sum, cell) => sum + cell.confidence, 0) / row.length
: 0;
mdRow.push(`${(avgConfidence * 100).toFixed(1)}%`);
}
lines.push('| ' + mdRow.join(' | ') + ' |');
}
return lines.join('\n');
}
/**
* Export matrix to HTML format
*/
exportToHTML(matrix: TraceabilityMatrix, options: MatrixExportOptions = { format: 'html' }): string {
const lines: string[] = [];
// HTML header
lines.push('<!DOCTYPE html>');
lines.push('<html lang="en">');
lines.push('<head>');
lines.push(' <meta charset="UTF-8">');
lines.push(' <meta name="viewport" content="width=device-width, initial-scale=1.0">');
lines.push(' <title>Traceability Matrix</title>');
lines.push(' <style>');
lines.push(' body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }');
lines.push(' h1 { color: #333; }');
lines.push(' table { width: 100%; border-collapse: collapse; background: white; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }');
lines.push(' th, td { padding: 12px; text-align: left; border: 1px solid #ddd; }');
lines.push(' th { background: #4CAF50; color: white; font-weight: bold; }');
lines.push(' tr:nth-child(even) { background: #f9f9f9; }');
lines.push(' tr:hover { background: #f1f1f1; }');
lines.push(' .coverage-high { color: #4CAF50; font-weight: bold; }');
lines.push(' .coverage-medium { color: #FF9800; font-weight: bold; }');
lines.push(' .coverage-low { color: #f44336; font-weight: bold; }');
lines.push(' .verified { color: #4CAF50; }');
lines.push(' .not-verified { color: #f44336; }');
lines.push(' .none { color: #999; font-style: italic; }');
lines.push(' code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; }');
lines.push(' </style>');
lines.push('</head>');
lines.push('<body>');
lines.push(' <h1>Traceability Matrix</h1>');
lines.push(` <p>Generated: ${new Date().toISOString()}</p>`);
lines.push(` <p>Total Requirements: ${matrix.requirements.length}</p>`);
lines.push(' <table>');
// Table header
lines.push(' <thead>');
lines.push(' <tr>');
lines.push(' <th>Requirement</th>');
lines.push(' <th>Code Files</th>');
lines.push(' <th>Tests</th>');
lines.push(' <th>Documentation</th>');
lines.push(' <th>Coverage</th>');
if (options.includeVerification) lines.push(' <th>Verified</th>');
if (options.includeConfidence) lines.push(' <th>Confidence</th>');
lines.push(' </tr>');
lines.push(' </thead>');
// Table body
lines.push(' <tbody>');
for (let i = 0; i < matrix.requirements.length; i++) {
const reqId = matrix.requirements[i];
const row = matrix.links[i];
// Find linked items
const linkedCode = row.filter(cell => cell.itemType === 'code' && cell.linked).map(cell => cell.itemPath);
const linkedTests = row.filter(cell => cell.itemType === 'test' && cell.linked).map(cell => cell.itemPath);
const linkedDocs = row.filter(cell => cell.itemType === 'documentation' && cell.linked).map(cell => cell.itemPath);
// Calculate coverage
const hasCode = linkedCode.length > 0;
const hasTests = linkedTests.length > 0;
const hasDocs = linkedDocs.length > 0;
const coverage = ((hasCode ? 33 : 0) + (hasTests ? 33 : 0) + (hasDocs ? 34 : 0));
const coverageClass = coverage >= 75 ? 'coverage-high' : coverage >= 50 ? 'coverage-medium' : 'coverage-low';
lines.push(' <tr>');
lines.push(` <td><strong>${this.escapeHTML(reqId)}</strong></td>`);
lines.push(` <td>${linkedCode.length > 0 ? linkedCode.map(f => `<code>${this.escapeHTML(f)}</code>`).join('<br>') : '<span class="none">NONE</span>'}</td>`);
lines.push(` <td>${linkedTests.length > 0 ? linkedTests.map(f => `<code>${this.escapeHTML(f)}</code>`).join('<br>') : '<span class="none">NONE</span>'}</td>`);
lines.push(` <td>${linkedDocs.length > 0 ? linkedDocs.map(f => `<code>${this.escapeHTML(f)}</code>`).join('<br>') : '<span class="none">NONE</span>'}</td>`);
lines.push(` <td class="${coverageClass}">${coverage}%</td>`);
if (options.includeVerification) {
const verified = row.every(cell => !cell.linked || cell.verified);
lines.push(` <td class="${verified ? 'verified' : 'not-verified'}">${verified ? '✅ YES' : '❌ NO'}</td>`);
}
if (options.includeConfidence) {
const avgConfidence = row.length > 0
? row.reduce((sum, cell) => sum + cell.confidence, 0) / row.length
: 0;
lines.push(` <td>${(avgConfidence * 100).toFixed(1)}%</td>`);
}
lines.push(' </tr>');
}
lines.push(' </tbody>');
lines.push(' </table>');
lines.push('</body>');
lines.push('</html>');
return lines.join('\n');
}
/**
* Escape CSV values
*/
private escapeCSV(value: string): string {
if (value.includes(',') || value.includes('"') || value.includes('\n')) {
return `"${value.replace(/"/g, '""')}"`;
}
return value;
}
/**
* Escape HTML entities
*/
private escapeHTML(value: string): string {
return value
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
/**
* Format file path for display (shorten if too long)
*/
private formatPath(filePath: string): string {
if (filePath.length <= 40) return filePath;
const parts = filePath.split('/');
if (parts.length <= 2) return filePath;
// Show first and last parts
return `${parts[0]}/.../${parts[parts.length - 1]}`;
}
/**
* Get coverage emoji
*/
private getCoverageEmoji(coverage: number): string {
if (coverage >= 75) return '✅';
if (coverage >= 50) return '⚠️';
return '❌';
}
}