From f80910cc0a477557294c6120bb5d39b8f5249e5e Mon Sep 17 00:00:00 2001 From: waleedcodes <110061477+waleedcodes@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:21:07 +0500 Subject: [PATCH 1/3] feat(engine): scaffold ProofCodeEngine class and verify options interface --- src/engine/proofCodeEngine.ts | 89 +++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/engine/proofCodeEngine.ts diff --git a/src/engine/proofCodeEngine.ts b/src/engine/proofCodeEngine.ts new file mode 100644 index 0000000..5f9212f --- /dev/null +++ b/src/engine/proofCodeEngine.ts @@ -0,0 +1,89 @@ +import * as path from 'node:path'; +import { GitService } from '../git/gitService'; +import { ProjectGraph } from '../graph/projectGraph'; +import { ProjectRulesEngine } from '../rules/projectRulesEngine'; +import { RiskEngine } from '../rules/riskEngine'; +import { TestRunner } from '../test-runner/testRunner'; +import { + BreakageRiskItem, + CheckResult, + FileDiff, + ImpactAnalysis, + RiskFinding, + RuleResult, + VerificationReport, + VerificationScore, + VerificationVerdict +} from '../types'; + +export interface VerifyOptions { + staged?: boolean; + baseRef?: string; + runLiveTests?: boolean; + rulesFilePath?: string; +} + +export class ProofCodeEngine { + private workspaceRoot: string; + private gitService: GitService; + private testRunner: TestRunner; + + constructor(workspaceRoot: string) { + this.workspaceRoot = workspaceRoot; + this.gitService = new GitService({ cwd: workspaceRoot }); + this.testRunner = new TestRunner(); + } + + public getWorkspaceRoot(): string { + return this.workspaceRoot; + } + + public async verify(options: VerifyOptions = {}): Promise { + const repoRoot = await this.gitService.getRepoRoot(); + const branch = await this.gitService.getCurrentBranch(repoRoot); + + // 1. Git Diff & Change Extraction + const diffResult = await this.gitService.getDiff({ + staged: options.staged, + baseRef: options.baseRef, + repoRoot + }); + + // If workspaceRoot is a subfolder of repoRoot, scope verification to workspaceRoot + const targetWorkspace = path.resolve(this.workspaceRoot); + const isSubfolder = + targetWorkspace !== path.resolve(repoRoot) && + targetWorkspace.startsWith(path.resolve(repoRoot)); + + const filesToVerify = isSubfolder + ? diffResult.files.filter((f) => { + const full = path.resolve(repoRoot, f.newPath); + return full.startsWith(targetWorkspace); + }) + : diffResult.files; + + const activeRoot = isSubfolder ? targetWorkspace : repoRoot; + + // 2. Project Graph & Dependency Mapping + const graph = new ProjectGraph(activeRoot); + await graph.buildGraph(); + + // Map changed files to added/changed lines + const fileChangedLines = new Map(); + for (const file of filesToVerify) { + const fullPath = path.resolve(repoRoot, file.newPath); + fileChangedLines.set(fullPath, file.addedLines); + } + + // 3. Compute Change Impact + const impact = graph.computeImpact(fileChangedLines); + + // 4. Static Risk Analysis on changed files + const allRisks: RiskFinding[] = []; + for (const file of filesToVerify) { + const fullPath = path.resolve(repoRoot, file.newPath); + const risks = RiskEngine.analyzeFile(fullPath, undefined, file); + allRisks.push(...risks); + } + + // 5. Project Rules Verification From b7f92d03ebf3e1ea364475ad4ded62e7bada7435 Mon Sep 17 00:00:00 2001 From: waleedcodes <110061477+waleedcodes@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:21:07 +0500 Subject: [PATCH 2/3] feat(engine): orchestrate Git diff, AST parsing, and graph impact pipeline --- src/engine/proofCodeEngine.ts | 89 +++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/engine/proofCodeEngine.ts b/src/engine/proofCodeEngine.ts index 5f9212f..3f64e66 100644 --- a/src/engine/proofCodeEngine.ts +++ b/src/engine/proofCodeEngine.ts @@ -87,3 +87,92 @@ export class ProofCodeEngine { } // 5. Project Rules Verification + const rulesEngine = new ProjectRulesEngine(activeRoot, options.rulesFilePath); + const ruleResults = await rulesEngine.evaluateRules(filesToVerify, graph); + + // 6. Test Runner Checks + let tsCheck: CheckResult = { name: 'TypeScript', status: 'PASS', message: 'Ready' }; + let lintCheck: CheckResult = { name: 'ESLint', status: 'PASS', message: 'Ready' }; + let unitTestsCheck: CheckResult = { name: 'Unit Tests', status: 'SKIPPED', message: 'Not run' }; + let buildCheck: CheckResult = { name: 'Build', status: 'SKIPPED', message: 'Not run' }; + + if (options.runLiveTests) { + [tsCheck, lintCheck, unitTestsCheck, buildCheck] = await Promise.all([ + this.testRunner.runTypeScriptCheck(repoRoot), + this.testRunner.runLint(repoRoot), + this.testRunner.runUnitTests(repoRoot), + this.testRunner.runBuild(repoRoot) + ]); + } + + // 7. Calculate Verification Score + const score = this.calculateVerificationScore({ + filesChanged: filesToVerify, + risks: allRisks, + rules: ruleResults, + untestedCallersCount: impact.untestedCallersCount, + checks: { tsCheck, lintCheck, unitTestsCheck, buildCheck } + }); + + // 8. Determine Verdict + const verdict = this.determineVerdict(score.total, allRisks, ruleResults, { + tsCheck, + lintCheck, + unitTestsCheck + }); + + // Count test files touched + const testsAdded = filesToVerify.filter( + (f) => f.isNew && graph.isTestFile(f.newPath) + ).length; + const testsAffected = filesToVerify.filter((f) => graph.isTestFile(f.newPath)).length; + + const highRiskCount = allRisks.filter((r) => r.severity === 'HIGH').length; + const mediumRiskCount = allRisks.filter((r) => r.severity === 'MEDIUM').length; + const lowRiskCount = allRisks.filter((r) => r.severity === 'LOW').length; + + const totalLinesAdded = filesToVerify.reduce((acc, f) => acc + f.addedLines.length, 0); + const totalLinesDeleted = filesToVerify.reduce((acc, f) => acc + f.deletedLines.length, 0); + + const breakageRisks = this.computeBreakageRisks(impact, allRisks, ruleResults); + + return { + timestamp: new Date().toISOString(), + repoRoot: activeRoot, + branch, + score, + verdict, + summary: { + filesChanged: filesToVerify.length, + linesAdded: totalLinesAdded, + linesDeleted: totalLinesDeleted, + symbolsAffected: impact.changedSymbols.length, + apiRoutesAffected: impact.affectedRoutes.length, + testsAdded, + testsAffected, + highRiskCount, + mediumRiskCount, + lowRiskCount + }, + impact, + risks: allRisks, + breakageRisks, + rules: ruleResults, + checks: { + typescript: tsCheck, + eslint: lintCheck, + unitTests: unitTestsCheck, + build: buildCheck + } + }; + } + + private calculateVerificationScore(params: { + filesChanged: FileDiff[]; + risks: RiskFinding[]; + rules: { status: string }[]; + untestedCallersCount: number; + checks: { + tsCheck: CheckResult; + lintCheck: CheckResult; + unitTestsCheck: CheckResult; From ace36f5c83547f2d127fcfc6c9045721f89201ac Mon Sep 17 00:00:00 2001 From: waleedcodes <110061477+waleedcodes@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:21:07 +0500 Subject: [PATCH 3/3] feat(engine): implement transparent score deductions and verdict logic --- src/engine/proofCodeEngine.ts | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/engine/proofCodeEngine.ts b/src/engine/proofCodeEngine.ts index 3f64e66..fdd1f13 100644 --- a/src/engine/proofCodeEngine.ts +++ b/src/engine/proofCodeEngine.ts @@ -176,3 +176,48 @@ export class ProofCodeEngine { tsCheck: CheckResult; lintCheck: CheckResult; unitTestsCheck: CheckResult; + buildCheck: CheckResult; + }; + }): VerificationScore { + let currentScore = 100; + const deductions: VerificationScore['deductions'] = []; + + // Deduct for High Risks (15 pts each, max 45) + const highRisks = params.risks.filter((r) => r.severity === 'HIGH').length; + if (highRisks > 0) { + const deduction = Math.min(45, highRisks * 15); + currentScore -= deduction; + deductions.push({ + reason: `${highRisks} High Severity Risk(s) detected`, + amount: deduction + }); + } + + // Deduct for Medium Risks (7 pts each, max 28) + const medRisks = params.risks.filter((r) => r.severity === 'MEDIUM').length; + if (medRisks > 0) { + const deduction = Math.min(28, medRisks * 7); + currentScore -= deduction; + deductions.push({ + reason: `${medRisks} Medium Severity Risk(s) detected`, + amount: deduction + }); + } + + // Deduct for Low Risks (2 pts each, max 10) + const lowRisks = params.risks.filter((r) => r.severity === 'LOW').length; + if (lowRisks > 0) { + const deduction = Math.min(10, lowRisks * 2); + currentScore -= deduction; + deductions.push({ + reason: `${lowRisks} Low Severity Risk(s) detected`, + amount: deduction + }); + } + + // Deduct for Project Rule Violations (15 pts each) + const violations = params.rules.filter((r) => r.status === 'VIOLATION').length; + if (violations > 0) { + const deduction = Math.min(30, violations * 15); + currentScore -= deduction; + deductions.push({