Implement verification engine to determine if AI agent work is truly complete by analyzing outputs and detecting deferred work patterns. Strategies: - FileChangeStrategy: Detect TODO/FIXME, placeholders, stubs - TestOutputStrategy: Validate pass rates, coverage (85%), skipped tests - BuildOutputStrategy: Detect TS errors, ESLint errors, build failures Deferred work detection patterns: - "follow-up", "to be added later" - "incremental improvement", "future enhancement" - "TODO: complete", "placeholder implementation" - "stub", "work in progress", "partially implemented" Features: - Confidence scoring (0-100%) - Verdict system: complete/incomplete/needs-review - Actionable suggestions for improvements - Strategy-based extensibility Integration: - Complements Quality Orchestrator (#134) - Uses Quality Gate Config (#135) Tests: 46 passing with 95.27% coverage Fixes #136 Co-Authored-By: Claude Opus 4.5 <[email protected]>
106 lines
3.3 KiB
TypeScript
106 lines
3.3 KiB
TypeScript
import { BaseVerificationStrategy } from "./base-verification.strategy";
|
|
import type { VerificationContext, StrategyResult, VerificationIssue } from "../interfaces";
|
|
|
|
export class BuildOutputStrategy extends BaseVerificationStrategy {
|
|
name = "build-output";
|
|
|
|
verify(context: VerificationContext): Promise<StrategyResult> {
|
|
const issues: VerificationIssue[] = [];
|
|
|
|
// If no build output, assume build wasn't run (neutral result)
|
|
if (!context.buildOutput) {
|
|
return Promise.resolve({
|
|
strategyName: this.name,
|
|
passed: true,
|
|
confidence: 50,
|
|
issues: [],
|
|
});
|
|
}
|
|
|
|
const { buildOutput } = context;
|
|
|
|
// Check for TypeScript errors
|
|
const tsErrorPattern = /error TS\d+:/gi;
|
|
const tsErrors = this.extractEvidence(buildOutput, tsErrorPattern);
|
|
if (tsErrors.length > 0) {
|
|
issues.push({
|
|
type: "build-error",
|
|
severity: "error",
|
|
message: `Found ${tsErrors.length.toString()} TypeScript error(s)`,
|
|
evidence: tsErrors.slice(0, 5).join("\n"), // Limit to first 5
|
|
});
|
|
}
|
|
|
|
// Check for ESLint errors
|
|
const eslintErrorPattern = /ESLint.*error/gi;
|
|
const eslintErrors = this.extractEvidence(buildOutput, eslintErrorPattern);
|
|
if (eslintErrors.length > 0) {
|
|
issues.push({
|
|
type: "build-error",
|
|
severity: "error",
|
|
message: `Found ${eslintErrors.length.toString()} ESLint error(s)`,
|
|
evidence: eslintErrors.slice(0, 5).join("\n"),
|
|
});
|
|
}
|
|
|
|
// Check for generic build errors
|
|
const buildErrorPattern = /\berror\b.*(?:build|compilation|failed)/gi;
|
|
const buildErrors = this.extractEvidence(buildOutput, buildErrorPattern);
|
|
if (buildErrors.length > 0 && tsErrors.length === 0) {
|
|
// Only add if not already counted as TS errors
|
|
issues.push({
|
|
type: "build-error",
|
|
severity: "error",
|
|
message: `Build errors detected`,
|
|
evidence: buildErrors.slice(0, 5).join("\n"),
|
|
});
|
|
}
|
|
|
|
// Check for compilation failure
|
|
const compilationFailedPattern = /compilation failed|build failed/gi;
|
|
if (compilationFailedPattern.test(buildOutput) && issues.length === 0) {
|
|
issues.push({
|
|
type: "build-error",
|
|
severity: "error",
|
|
message: "Compilation failed",
|
|
});
|
|
}
|
|
|
|
// Check for warnings
|
|
const warningPattern = /\bwarning\b/gi;
|
|
const warnings = this.extractEvidence(buildOutput, warningPattern);
|
|
if (warnings.length > 0) {
|
|
issues.push({
|
|
type: "build-error",
|
|
severity: "warning",
|
|
message: `Found ${warnings.length.toString()} warning(s)`,
|
|
evidence: warnings.slice(0, 3).join("\n"),
|
|
});
|
|
}
|
|
|
|
// Calculate confidence
|
|
let confidence = 100;
|
|
|
|
// Count total errors
|
|
const errorCount = tsErrors.length + eslintErrors.length + buildErrors.length;
|
|
if (errorCount > 0) {
|
|
// More aggressive penalty: 30 points per error (3 errors = 10% confidence)
|
|
confidence = Math.max(0, 100 - errorCount * 30);
|
|
}
|
|
|
|
// Penalty for warnings
|
|
if (warnings.length > 0) {
|
|
confidence -= Math.min(10, warnings.length * 2);
|
|
}
|
|
|
|
confidence = Math.max(0, Math.round(confidence));
|
|
|
|
return Promise.resolve({
|
|
strategyName: this.name,
|
|
passed: issues.filter((i) => i.severity === "error").length === 0,
|
|
confidence,
|
|
issues,
|
|
});
|
|
}
|
|
}
|