From d2149fe05444971f9eb1967439108df3bfec41cf Mon Sep 17 00:00:00 2001 From: waleedcodes <110061477+waleedcodes@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:21:37 +0500 Subject: [PATCH 1/3] feat(formatters): initialize ReportFormatter class structure --- src/engine/reportFormatter.ts | 68 +++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/engine/reportFormatter.ts diff --git a/src/engine/reportFormatter.ts b/src/engine/reportFormatter.ts new file mode 100644 index 0000000..6896608 --- /dev/null +++ b/src/engine/reportFormatter.ts @@ -0,0 +1,68 @@ +import { VerificationReport } from '../types'; + +export class ReportFormatter { + /** + * Formats the verification report into clean GitHub Flavored Markdown + */ + public static toMarkdown(report: VerificationReport): string { + const verdictBadge = + report.verdict === 'READY_TO_SHIP' + ? '๐ŸŸข **READY TO SHIP**' + : report.verdict === 'NEEDS_REVIEW' + ? '๐ŸŸ  **NEEDS REVIEW**' + : '๐Ÿ”ด **BLOCKED**'; + + let md = `# ๐Ÿ›ก๏ธ ProofCode Verification Report\n\n`; + md += `> **AI writes code. ProofCode proves the change.**\n\n`; + md += `- **Verdict:** ${verdictBadge}\n`; + md += `- **Verification Score:** **${report.score.total}%**\n`; + md += `- **Branch:** \`${report.branch || 'HEAD'}\`\n`; + md += `- **Timestamp:** \`${report.timestamp}\`\n\n`; + + md += `## ๐Ÿ“Š Change Summary\n\n`; + md += `| Metric | Count |\n`; + md += `|---|---|\n`; + md += `| Files Changed | **${report.summary.filesChanged}** |\n`; + md += `| Lines Added | +${report.summary.linesAdded} |\n`; + md += `| Lines Deleted | -${report.summary.linesDeleted} |\n`; + md += `| Functions Affected | **${report.summary.symbolsAffected}** |\n`; + md += `| API Routes Affected | **${report.summary.apiRoutesAffected}** |\n`; + md += `| High Risks Detected | ๐Ÿ”ด **${report.summary.highRiskCount}** |\n`; + md += `| Medium Risks Detected | ๐ŸŸ  **${report.summary.mediumRiskCount}** |\n`; + md += `| Low Risks Detected | ๐ŸŸข **${report.summary.lowRiskCount}** |\n\n`; + + // Score Deductions breakdown + if (report.score.deductions.length > 0) { + md += `### Score Deductions Breakdown\n\n`; + for (const d of report.score.deductions) { + md += `- -${d.amount} pts: ${d.reason}\n`; + } + md += `\n`; + } + + // What Could This Change Break? + if (report.breakageRisks && report.breakageRisks.length > 0) { + md += `## ๐Ÿ’ฅ What Could This Change Break?\n\n`; + md += `| Area | Severity | Trigger & Impact | Dependents At Risk |\n`; + md += `|---|---|---|---|\n`; + for (const b of report.breakageRisks) { + const icon = b.severity === 'HIGH' ? '๐Ÿ”ด' : b.severity === 'MEDIUM' ? '๐ŸŸ ' : '๐ŸŸข'; + const deps = b.affectedCallersOrRoutes.length > 0 ? b.affectedCallersOrRoutes.map((c) => `\`${c}\``).join(', ') : '*None*'; + md += `| **${b.area}** | ${icon} ${b.severity} | **${b.trigger}**
${b.detail} | ${deps} |\n`; + } + md += `\n`; + } + + // Risks & Evidence + md += `## โš ๏ธ Risks & Verifiable Evidence\n\n`; + if (report.risks.length === 0) { + md += `*No risks detected in current changes. All static checks passed.*\n\n`; + } else { + for (const risk of report.risks) { + const severityIcon = + risk.severity === 'HIGH' ? '๐Ÿ”ด' : risk.severity === 'MEDIUM' ? '๐ŸŸ ' : '๐ŸŸข'; + md += `### ${severityIcon} [${risk.severity}] ${risk.title}\n\n`; + md += `**Location:** \`${risk.file}:${risk.line}\`\n\n`; + md += `**Description:** ${risk.description}\n\n`; + md += `**Recommendation:** ${risk.recommendation}\n\n`; + From 2ba9e066f44ddc6268d4a0cb708cf97f82e74bb4 Mon Sep 17 00:00:00 2001 From: waleedcodes <110061477+waleedcodes@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:21:37 +0500 Subject: [PATCH 2/3] feat(formatters): implement toMarkdown with tables, evidence traces, and breakage --- src/engine/reportFormatter.ts | 79 +++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/engine/reportFormatter.ts b/src/engine/reportFormatter.ts index 6896608..80f1f57 100644 --- a/src/engine/reportFormatter.ts +++ b/src/engine/reportFormatter.ts @@ -66,3 +66,82 @@ export class ReportFormatter { md += `**Description:** ${risk.description}\n\n`; md += `**Recommendation:** ${risk.recommendation}\n\n`; + if (risk.evidenceTrace.length > 0) { + md += `**Evidence Path:**\n\`\`\`text\n`; + for (let i = 0; i < risk.evidenceTrace.length; i++) { + const step = risk.evidenceTrace[i]; + const arrow = i === risk.evidenceTrace.length - 1 ? 'โ””โ”€โ”€' : 'โ”œโ”€โ”€'; + md += `${arrow} [Line ${step.line}] ${step.description}\n`; + } + md += `\`\`\`\n\n`; + } + + if (risk.snippet) { + md += `**Code Snippet:**\n\`\`\`typescript\n${risk.snippet}\n\`\`\`\n\n`; + } + } + } + + // Impact Analysis + md += `## โšก Change Impact Analysis\n\n`; + if (report.impact.changedSymbols.length === 0) { + md += `*No exported symbols were modified.*\n\n`; + } else { + md += `### Affected Symbols and Callers\n\n`; + for (const sym of report.impact.changedSymbols) { + md += `- **\`${sym.symbolName}\`** (${sym.kind}) in \`${sym.filePath}\`\n`; + if (sym.callers.length === 0) { + md += ` - *No external callers detected.*\n`; + } else { + for (const caller of sym.callers) { + const testBadge = caller.hasTest ? 'โœ“ tested' : 'โš ๏ธ untested'; + md += ` - Called by \`${caller.callerName}\` at \`${caller.filePath}:${caller.line}\` [${testBadge}]\n`; + } + } + } + md += `\n`; + } + + if (report.impact.affectedRoutes.length > 0) { + md += `### Affected API Routes\n\n`; + for (const route of report.impact.affectedRoutes) { + md += `- \`${route.method}\` \`${route.routePath}\` at line ${route.line}\n`; + } + md += `\n`; + } + + // Project Rules + md += `## ๐Ÿ“‹ Project Rules Status\n\n`; + if (report.rules.length === 0) { + md += `*No project rules configured.*\n\n`; + } else { + md += `| Rule | Status | Evidence |\n`; + md += `|---|---|---|\n`; + for (const r of report.rules) { + const statusEmoji = r.status === 'PASS' ? 'โœ… PASS' : r.status === 'VIOLATION' ? '๐Ÿ”ด VIOLATION' : 'โš ๏ธ WARNING'; + const evidence = r.detectedMessage || 'Compliant with policy'; + md += `| ${r.rule.title} | ${statusEmoji} | ${evidence} |\n`; + } + md += `\n`; + } + + // Checks & Tests + md += `## ๐Ÿงช Checks & Verification Tests\n\n`; + md += `| Check | Status | Details |\n`; + md += `|---|---|---|\n`; + md += `| TypeScript | ${report.checks.typescript.status} | ${report.checks.typescript.message || ''} |\n`; + md += `| ESLint | ${report.checks.eslint.status} | ${report.checks.eslint.message || ''} |\n`; + md += `| Unit Tests | ${report.checks.unitTests.status} | ${report.checks.unitTests.message || ''} |\n`; + md += `| Build Check | ${report.checks.build.status} | ${report.checks.build.message || ''} |\n\n`; + + md += `---\n*Generated by ProofCode v0.1.0 โ€” Code Change Verification*\n`; + + return md; + } + + /** + * Formats a concise summary for terminal/CLI usage + */ + public static toTerminal(report: VerificationReport): string { + const lines: string[] = []; + lines.push('===================================================='); From a314faf9f1a9bea04ff41a72ac8c81a674a60da1 Mon Sep 17 00:00:00 2001 From: waleedcodes <110061477+waleedcodes@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:21:37 +0500 Subject: [PATCH 3/3] feat(formatters): implement toTerminal and toJson serialization methods --- src/engine/reportFormatter.ts | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/engine/reportFormatter.ts b/src/engine/reportFormatter.ts index 80f1f57..2a41985 100644 --- a/src/engine/reportFormatter.ts +++ b/src/engine/reportFormatter.ts @@ -145,3 +145,52 @@ export class ReportFormatter { public static toTerminal(report: VerificationReport): string { const lines: string[] = []; lines.push('===================================================='); + lines.push(' PROOFCODE CHANGE VERIFICATION '); + lines.push('===================================================='); + lines.push(`Verdict: ${report.verdict}`); + lines.push(`Score: ${report.score.total}%`); + lines.push(`Files Changed: ${report.summary.filesChanged}`); + lines.push(`Functions: ${report.summary.symbolsAffected} affected`); + lines.push(`API Routes: ${report.summary.apiRoutesAffected} affected`); + lines.push( + `Risks: ๐Ÿ”ด ${report.summary.highRiskCount} High | ๐ŸŸ  ${report.summary.mediumRiskCount} Med | ๐ŸŸข ${report.summary.lowRiskCount} Low` + ); + lines.push('----------------------------------------------------'); + + if (report.breakageRisks && report.breakageRisks.length > 0) { + lines.push('WHAT COULD THIS CHANGE BREAK?'); + for (const b of report.breakageRisks) { + lines.push(`- [${b.severity}] ${b.area}: ${b.trigger}`); + lines.push(` Impact: ${b.detail}`); + if (b.affectedCallersOrRoutes.length > 0) { + lines.push(` Dependents: ${b.affectedCallersOrRoutes.join(', ')}`); + } + } + lines.push('----------------------------------------------------'); + } + + if (report.risks.length > 0) { + lines.push('CRITICAL FINDINGS:'); + for (const risk of report.risks) { + lines.push(`[${risk.severity}] ${risk.title} at ${risk.file}:${risk.line}`); + lines.push(` -> ${risk.description}`); + } + lines.push('----------------------------------------------------'); + } + + const violations = report.rules.filter((r) => r.status === 'VIOLATION'); + if (violations.length > 0) { + lines.push('RULE VIOLATIONS:'); + for (const v of violations) { + lines.push(`- ${v.rule.title}: ${v.detectedMessage} (${v.file}:${v.line})`); + } + lines.push('----------------------------------------------------'); + } + + return lines.join('\n'); + } + + public static toJson(report: VerificationReport): string { + return JSON.stringify(report, null, 2); + } +}