← Files ForgeMindARCHIVED FILE
src/xray-evidence.mjs
2.62 KB · Oct 4, 2026 · 12:31 UTC
import { copyFile, mkdir } from 'node:fs/promises';
import path from 'node:path';
import { artifactStatePath } from './artifact-store.mjs';
import { pngDifferencePercent } from './xray-image-diff.mjs';
export async function compareVisualEvidence({ workspace, flowId, screenshot, thresholdPercent = 0, baseline = false }) {
const current = path.resolve(workspace, screenshot);
const target = artifactStatePath(workspace, 'xray', 'visual', 'baseline', `${flowId}.png`);
if (baseline) { await mkdir(path.dirname(target), { recursive: true }); await copyFile(current, target); return { evidence: [relative(workspace, target)], finding: null, gap: null, differencePercent: 0 }; }
let differencePercent;
try { differencePercent = await pngDifferencePercent(target, current); }
catch (error) {
const missing = error?.code === 'ENOENT';
return { evidence: [screenshot], finding: null, gap: { code: missing ? 'FM_XRAY_VISUAL_BASELINE_MISSING' : 'FM_XRAY_VISUAL_COMPARISON_UNAVAILABLE', message: missing ? `No approved visual baseline exists for ${flowId}.` : `Visual comparison for ${flowId} requires compatible PNG screenshots.`, nextAction: missing ? 'Run xray baseline after reviewing the local UI state.' : 'Capture non-interlaced 8-bit RGB or RGBA PNG screenshots and rerun Xray.' } };
}
const evidence = [relative(workspace, target), screenshot];
return differencePercent > thresholdPercent ? { evidence, differencePercent, gap: null, finding: { id: `finding-visual-${flowId}`, severity: 'medium', surfaces: ['web-gui'], componentIds: ['gui-usability', 'accessibility-visual'], title: `Visual regression: ${flowId}`, expected: `Change no more than ${thresholdPercent}% of pixels.`, actual: `${differencePercent.toFixed(2)}% of pixels differ from the approved baseline.`, evidence } } : { evidence, differencePercent, finding: null, gap: null };
}
export function performanceFinding(receipt, budgetMs) {
if (!Number.isFinite(receipt?.durationMs)) return budgetMs === undefined ? null : { code: 'FM_XRAY_PERFORMANCE_TIMING_UNAVAILABLE', message: `No timing evidence is available for ${receipt?.id ?? 'the check'}.` };
if (budgetMs === undefined || receipt.durationMs <= budgetMs) return null;
return { id: `finding-performance-${receipt.id}`, severity: 'medium', surfaces: receipt.surfaceIds ?? [], componentIds: receipt.componentIds ?? [], title: `Performance budget exceeded: ${receipt.id}`, expected: `Complete within ${budgetMs}ms.`, actual: `Completed in ${receipt.durationMs}ms.`, evidence: receipt.evidence ?? [] };
}
function relative(workspace, target) { return path.relative(workspace, target).replaceAll('\\', '/'); }
SHA-256: ee971593e882d324bb853bc8398731ffff48fe94ff72981b3a9138457d244aa4