codeloop-mcp-server 0.1.11 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,108 +1,439 @@
1
1
  import { compareScreenshot } from "../evidence/screenshot_diff.js";
2
- import { existsSync, readFileSync, mkdirSync } from "fs";
3
- import { join, resolve } from "path";
2
+ import { existsSync, readFileSync, mkdirSync, readdirSync, writeFileSync, statSync, } from "fs";
3
+ import { join, resolve, basename, extname } from "path";
4
4
  import { getArtifactsBaseDir, getRunDir, listRuns } from "../evidence/artifacts.js";
5
+ import { loadFigmaConfig, resolveFigmaToken, syncFigmaDesigns, } from "../runners/figma_fetcher.js";
6
+ const SUPPORTED_REF_EXTS = [".png", ".jpg", ".jpeg", ".webp"];
7
+ const DEFAULT_DESIGNS_DIR = "designs";
5
8
  function screenFileName(screenName) {
6
9
  const s = screenName.trim();
7
- if (s.toLowerCase().endsWith(".png")) {
10
+ if (s.toLowerCase().endsWith(".png"))
8
11
  return s;
9
- }
10
12
  return `${s}.png`;
11
13
  }
14
+ function severityFromScore(score, threshold) {
15
+ if (score >= threshold)
16
+ return "low";
17
+ if (score >= threshold - 0.1)
18
+ return "medium";
19
+ if (score >= threshold - 0.25)
20
+ return "high";
21
+ return "critical";
22
+ }
12
23
  /**
13
- * Runs deterministic pixel-level comparison between reference design and actual
14
- * screenshot, and returns image paths for the AI agent to visually compare.
24
+ * Detects whether the project has any design references that should drive
25
+ * the design_compare loop. Used by gate_check and verify auto-trigger.
26
+ */
27
+ export function hasDesignReferences(cwd, designsDir = DEFAULT_DESIGNS_DIR) {
28
+ const dir = resolve(cwd, designsDir);
29
+ if (existsSync(dir)) {
30
+ try {
31
+ const files = readdirSync(dir);
32
+ if (files.some((f) => SUPPORTED_REF_EXTS.includes(extname(f).toLowerCase()))) {
33
+ return true;
34
+ }
35
+ }
36
+ catch {
37
+ /* fall through */
38
+ }
39
+ }
40
+ // Also scan one level deep (designs/<viewport>/<screen>.png)
41
+ if (existsSync(dir)) {
42
+ try {
43
+ const entries = readdirSync(dir, { withFileTypes: true });
44
+ for (const entry of entries) {
45
+ if (entry.isDirectory()) {
46
+ const sub = join(dir, entry.name);
47
+ const subFiles = readdirSync(sub);
48
+ if (subFiles.some((f) => SUPPORTED_REF_EXTS.includes(extname(f).toLowerCase()))) {
49
+ return true;
50
+ }
51
+ }
52
+ }
53
+ }
54
+ catch {
55
+ /* fall through */
56
+ }
57
+ }
58
+ if (loadFigmaConfig(cwd))
59
+ return true;
60
+ return false;
61
+ }
62
+ /**
63
+ * Walks `designs/` (and one level of subdirs treated as viewports) and returns
64
+ * every reference image with its derived screen_name and optional viewport.
65
+ *
66
+ * Conventions:
67
+ * - `designs/home.png` -> screen=home
68
+ * - `designs/home@2x.png` -> screen=home, scale=2
69
+ * - `designs/mobile/home.png` -> screen=home, viewport=mobile
70
+ * - `designs/desktop/home@1x.png` -> screen=home, viewport=desktop, scale=1
71
+ */
72
+ export function listReferenceFiles(cwd, designsDir = DEFAULT_DESIGNS_DIR) {
73
+ const dir = resolve(cwd, designsDir);
74
+ const out = [];
75
+ if (!existsSync(dir))
76
+ return out;
77
+ const visit = (currentDir, viewport) => {
78
+ let entries;
79
+ try {
80
+ entries = readdirSync(currentDir, { withFileTypes: true });
81
+ }
82
+ catch {
83
+ return;
84
+ }
85
+ for (const entry of entries) {
86
+ const name = entry.name;
87
+ const full = join(currentDir, name);
88
+ if (entry.isDirectory()) {
89
+ if (viewport === undefined) {
90
+ visit(full, name);
91
+ }
92
+ continue;
93
+ }
94
+ const ext = extname(name).toLowerCase();
95
+ if (!SUPPORTED_REF_EXTS.includes(ext))
96
+ continue;
97
+ const stem = basename(name, ext);
98
+ const scaleMatch = stem.match(/^(.+)@(\d+)x$/);
99
+ const screenName = scaleMatch ? scaleMatch[1] : stem;
100
+ const scale = scaleMatch ? parseInt(scaleMatch[2], 10) : undefined;
101
+ out.push({ path: full, screenName, viewport, scale });
102
+ }
103
+ };
104
+ visit(dir);
105
+ return out;
106
+ }
107
+ function findActualScreenshot(screenshotsDir, screenName, viewport) {
108
+ if (!existsSync(screenshotsDir))
109
+ return null;
110
+ const files = readdirSync(screenshotsDir).filter((f) => f.toLowerCase().endsWith(".png"));
111
+ const candidates = [];
112
+ if (viewport) {
113
+ candidates.push(`${screenName}_${viewport}.png`);
114
+ candidates.push(`${screenName}-${viewport}.png`);
115
+ candidates.push(`${viewport}_${screenName}.png`);
116
+ candidates.push(`${viewport}-${screenName}.png`);
117
+ }
118
+ candidates.push(`${screenName}.png`);
119
+ for (const candidate of candidates) {
120
+ const match = files.find((f) => f.toLowerCase() === candidate.toLowerCase());
121
+ if (match)
122
+ return join(screenshotsDir, match);
123
+ }
124
+ // Fallback: starts-with match (e.g. `home_chromium.png` for screen=home).
125
+ const prefix = files.find((f) => f.toLowerCase().startsWith(screenName.toLowerCase() + "_") ||
126
+ f.toLowerCase().startsWith(screenName.toLowerCase() + "-"));
127
+ if (prefix)
128
+ return join(screenshotsDir, prefix);
129
+ return null;
130
+ }
131
+ function bestRunDir(cwd) {
132
+ const base = getArtifactsBaseDir(cwd);
133
+ const runs = listRuns(base);
134
+ for (const runId of runs) {
135
+ const dir = getRunDir(runId, base);
136
+ const screenshotsDir = join(dir, "screenshots");
137
+ if (existsSync(screenshotsDir)) {
138
+ try {
139
+ const files = readdirSync(screenshotsDir).filter((f) => f.toLowerCase().endsWith(".png"));
140
+ if (files.length > 0)
141
+ return { runId, dir };
142
+ }
143
+ catch {
144
+ /* skip */
145
+ }
146
+ }
147
+ }
148
+ if (runs.length > 0) {
149
+ return { runId: runs[0], dir: getRunDir(runs[0], base) };
150
+ }
151
+ return null;
152
+ }
153
+ /**
154
+ * Single-screen comparison (legacy entry point preserved for the existing
155
+ * `runDesignCompare` MCP tool signature).
15
156
  */
16
157
  export async function runDesignCompare(input, config, cwd = process.cwd()) {
158
+ if (input.mode === "all" || (!input.reference_image_path && !input.screen_name)) {
159
+ return runDesignCompareAll(input, config, cwd);
160
+ }
161
+ if (!input.reference_image_path || !input.screen_name) {
162
+ return failureResult("reference", "reference_image_path and screen_name are required in single mode.", "Pass `mode: 'all'` or provide both fields.");
163
+ }
17
164
  const refPath = resolve(cwd, input.reference_image_path);
18
165
  if (!existsSync(refPath)) {
19
- return {
20
- output: {
21
- match_score: 0,
22
- differences: [
23
- {
24
- area: "reference",
25
- description: `Reference image not found: ${input.reference_image_path}`,
26
- severity: "high",
27
- fix_hint: "Provide a valid reference_image_path.",
28
- },
29
- ],
30
- screenshots: [],
31
- recommendation: "Add the design reference image and retry.",
32
- },
33
- imagePaths: { reference: "", actual: "" },
34
- };
166
+ return failureResult("reference", `Reference image not found: ${input.reference_image_path}`, "Provide a valid reference_image_path or place the file under designs/.", refPath);
35
167
  }
36
- const base = getArtifactsBaseDir(cwd);
37
- const runs = listRuns(base);
38
- const latestRun = runs[0];
39
- const actualPath = latestRun
40
- ? join(getRunDir(latestRun, base), "screenshots", screenFileName(input.screen_name))
41
- : "";
42
- if (!actualPath || !existsSync(actualPath)) {
43
- return {
44
- output: {
45
- match_score: 0,
46
- differences: [
47
- {
48
- area: "screenshot",
49
- description: latestRun
50
- ? `No screenshot found for "${input.screen_name}" in the latest run (${latestRun}).`
51
- : "No verification runs found; cannot locate an actual screenshot.",
52
- severity: "high",
53
- fix_hint: "Run codeloop_verify to capture screenshots, or ensure the screen name matches a PNG in artifacts/runs/<run>/screenshots/.",
54
- },
55
- ],
56
- screenshots: [refPath],
57
- recommendation: "Capture the UI first, then compare.",
58
- },
59
- imagePaths: { reference: refPath, actual: "" },
60
- };
61
- }
62
- let diffPath;
63
- let pixelDiffScore = 0;
168
+ const run = bestRunDir(cwd);
169
+ if (!run) {
170
+ return failureResult("screenshot", "No verification runs found; cannot locate an actual screenshot.", "Run codeloop_verify to capture screenshots first.", refPath);
171
+ }
172
+ const screenshotsDir = join(run.dir, "screenshots");
173
+ const actualPath = findActualScreenshot(screenshotsDir, input.screen_name);
174
+ if (!actualPath) {
175
+ return failureResult("screenshot", `No screenshot found for "${input.screen_name}" in run ${run.runId}.`, "Run codeloop_capture_screenshot for this screen, or ensure the file name matches.", refPath);
176
+ }
177
+ const threshold = config.design_match_threshold ?? 0.8;
178
+ const diffsDir = join(run.dir, "diffs");
179
+ mkdirSync(diffsDir, { recursive: true });
180
+ const diffPath = join(diffsDir, `design_compare_${screenFileName(input.screen_name)}`);
181
+ let pixelMatchScore = 0;
182
+ let diffOk;
64
183
  try {
65
- const diffsDir = latestRun
66
- ? join(getRunDir(latestRun, base), "diffs")
67
- : join(cwd, ".codeloop", "diffs");
68
- mkdirSync(diffsDir, { recursive: true });
69
- diffPath = join(diffsDir, `design_compare_${screenFileName(input.screen_name)}`);
70
184
  const diffResult = compareScreenshot(actualPath, refPath, diffPath);
71
- pixelDiffScore = 1 - diffResult.diffScore;
185
+ pixelMatchScore = 1 - diffResult.diffScore;
186
+ diffOk = existsSync(diffPath) ? diffPath : undefined;
72
187
  }
73
188
  catch {
74
- pixelDiffScore = 0;
75
- diffPath = undefined;
76
- }
77
- let uxChecklist;
78
- const uxPath = input.ux_checklist_path
79
- ? resolve(cwd, input.ux_checklist_path)
80
- : undefined;
81
- if (uxPath && existsSync(uxPath)) {
189
+ pixelMatchScore = 0;
190
+ diffOk = undefined;
191
+ }
192
+ const passed = pixelMatchScore >= threshold;
193
+ const perScreen = [
194
+ {
195
+ screen_name: input.screen_name,
196
+ viewport: input.viewport_sizes?.[0],
197
+ reference_path: refPath,
198
+ actual_path: actualPath,
199
+ diff_path: diffOk,
200
+ match_score: pixelMatchScore,
201
+ passed,
202
+ severity: severityFromScore(pixelMatchScore, threshold),
203
+ reason: passed
204
+ ? `Score ${(pixelMatchScore * 100).toFixed(1)}% >= threshold ${(threshold * 100).toFixed(0)}%`
205
+ : `Score ${(pixelMatchScore * 100).toFixed(1)}% < threshold ${(threshold * 100).toFixed(0)}%`,
206
+ },
207
+ ];
208
+ const uxChecklist = readUxChecklist(input.ux_checklist_path, cwd);
209
+ const summaryPath = join(run.dir, "design_compare_summary.json");
210
+ writeFileSync(summaryPath, JSON.stringify({
211
+ threshold,
212
+ per_screen: perScreen,
213
+ min_score: pixelMatchScore,
214
+ avg_score: pixelMatchScore,
215
+ failed_screens: passed ? [] : [input.screen_name],
216
+ source: "files",
217
+ }, null, 2));
218
+ return {
219
+ output: {
220
+ match_score: pixelMatchScore,
221
+ differences: [],
222
+ screenshots: [refPath, actualPath],
223
+ recommendation: passed
224
+ ? "Pixel-level comparison suggests the implementation closely matches the reference. The AI agent will provide detailed visual assessment."
225
+ : "Pixel-level comparison detected significant differences. Fix the highlighted regions and re-run codeloop_verify before gate_check.",
226
+ per_screen: perScreen,
227
+ min_score: pixelMatchScore,
228
+ avg_score: pixelMatchScore,
229
+ failed_screens: passed ? [] : [input.screen_name],
230
+ threshold,
231
+ source: "files",
232
+ },
233
+ imagePaths: { reference: refPath, actual: actualPath, diff: diffOk },
234
+ uxChecklist,
235
+ perScreen,
236
+ source: "files",
237
+ };
238
+ }
239
+ /**
240
+ * Multi-screen / multi-viewport comparison. Auto-discovers references in
241
+ * `designs/` and from `.codeloop/figma.json`, maps each to the latest
242
+ * actual screenshot, runs pixelmatch, and writes a summary that gate_check
243
+ * consumes.
244
+ */
245
+ export async function runDesignCompareAll(input, config, cwd = process.cwd()) {
246
+ const designsDir = input.designs_dir || DEFAULT_DESIGNS_DIR;
247
+ const threshold = config.design_match_threshold ?? 0.8;
248
+ // 1. Optional Figma sync — non-fatal if it fails.
249
+ let source = "files";
250
+ const figmaErrors = [];
251
+ const figmaConfig = loadFigmaConfig(cwd);
252
+ const wantsFigma = !!input.figma_file_url || !!figmaConfig;
253
+ if (wantsFigma) {
82
254
  try {
83
- uxChecklist = readFileSync(uxPath, "utf-8");
255
+ const token = input.figma_token ||
256
+ (figmaConfig ? resolveFigmaToken(figmaConfig) : undefined) ||
257
+ process.env.FIGMA_API_TOKEN;
258
+ const fileUrl = input.figma_file_url || figmaConfig?.file_url;
259
+ if (token && (fileUrl || figmaConfig?.file_key)) {
260
+ const result = await syncFigmaDesigns(cwd, designsDir);
261
+ if (result.fetched.length > 0) {
262
+ source = source === "files" ? "figma" : "mixed";
263
+ }
264
+ figmaErrors.push(...result.errors);
265
+ }
266
+ else {
267
+ figmaErrors.push("Figma config detected but token or file URL missing — skipping Figma sync.");
268
+ }
84
269
  }
85
- catch {
86
- uxChecklist = undefined;
270
+ catch (err) {
271
+ figmaErrors.push(`Figma sync error: ${err.message}`);
87
272
  }
88
273
  }
89
- const threshold = config.design_match_threshold ?? 0.8;
90
- const recommendation = pixelDiffScore >= threshold
91
- ? "Pixel-level comparison suggests the implementation closely matches the reference. The AI agent will provide detailed visual assessment."
92
- : "Pixel-level comparison detected significant differences. The AI agent will identify specific areas to fix.";
274
+ // 2. Enumerate every reference (post-sync).
275
+ const refs = listReferenceFiles(cwd, designsDir);
276
+ if (refs.length === 0) {
277
+ return failureResult("reference", `No reference images found under ${designsDir}/ and Figma sync produced none.${figmaErrors.length ? " Errors: " + figmaErrors.join("; ") : ""}`, "Add PNG/JPEG/WebP files under designs/, or configure .codeloop/figma.json with FIGMA_API_TOKEN.");
278
+ }
279
+ if (source === "files" && figmaConfig)
280
+ source = "mixed";
281
+ // 3. Resolve actual screenshot for every reference.
282
+ const run = bestRunDir(cwd);
283
+ if (!run) {
284
+ return failureResult("screenshot", "No verification runs with screenshots found; cannot compare designs.", "Run codeloop_verify (or codeloop_capture_screenshot) first to populate artifacts/runs/<run>/screenshots/.");
285
+ }
286
+ const screenshotsDir = join(run.dir, "screenshots");
287
+ const diffsDir = join(run.dir, "diffs");
288
+ mkdirSync(diffsDir, { recursive: true });
289
+ const perScreen = [];
290
+ for (const ref of refs) {
291
+ const actualPath = findActualScreenshot(screenshotsDir, ref.screenName, ref.viewport);
292
+ if (!actualPath) {
293
+ perScreen.push({
294
+ screen_name: ref.screenName,
295
+ viewport: ref.viewport,
296
+ reference_path: ref.path,
297
+ actual_path: "",
298
+ match_score: 0,
299
+ passed: false,
300
+ severity: "high",
301
+ reason: `No screenshot found for "${ref.screenName}"${ref.viewport ? ` (${ref.viewport})` : ""}. Capture it before re-running design compare.`,
302
+ });
303
+ continue;
304
+ }
305
+ const diffName = ref.viewport
306
+ ? `design_compare_${ref.screenName}_${ref.viewport}.png`
307
+ : `design_compare_${ref.screenName}.png`;
308
+ const diffPath = join(diffsDir, diffName);
309
+ let matchScore = 0;
310
+ let diffOk;
311
+ let reason;
312
+ try {
313
+ const diffResult = compareScreenshot(actualPath, ref.path, diffPath);
314
+ matchScore = 1 - diffResult.diffScore;
315
+ diffOk = existsSync(diffPath) ? diffPath : undefined;
316
+ reason = `Pixel diff: ${(diffResult.diffScore * 100).toFixed(2)}% changed (${diffResult.diffPixels}/${diffResult.totalPixels} pixels).`;
317
+ }
318
+ catch (err) {
319
+ matchScore = 0;
320
+ diffOk = undefined;
321
+ reason = `Comparison error: ${err.message}`;
322
+ }
323
+ const passed = matchScore >= threshold;
324
+ perScreen.push({
325
+ screen_name: ref.screenName,
326
+ viewport: ref.viewport,
327
+ reference_path: ref.path,
328
+ actual_path: actualPath,
329
+ diff_path: diffOk,
330
+ match_score: matchScore,
331
+ passed,
332
+ severity: severityFromScore(matchScore, threshold),
333
+ reason,
334
+ });
335
+ }
336
+ // 4. Aggregate.
337
+ const scores = perScreen.map((p) => p.match_score);
338
+ const minScore = scores.length ? Math.min(...scores) : 0;
339
+ const avgScore = scores.length ? scores.reduce((s, v) => s + v, 0) / scores.length : 0;
340
+ const failedScreens = perScreen.filter((p) => !p.passed).map((p) => p.screen_name);
341
+ const allPassed = failedScreens.length === 0 && perScreen.length > 0;
342
+ // 5. Persist summary for gate_check.
343
+ const summaryPath = join(run.dir, "design_compare_summary.json");
344
+ writeFileSync(summaryPath, JSON.stringify({
345
+ threshold,
346
+ per_screen: perScreen,
347
+ min_score: minScore,
348
+ avg_score: avgScore,
349
+ failed_screens: failedScreens,
350
+ source,
351
+ generated_at: new Date().toISOString(),
352
+ figma_errors: figmaErrors.length ? figmaErrors : undefined,
353
+ }, null, 2));
354
+ // 6. Pick representative images for the MCP response (prefer worst failure).
355
+ const sorted = [...perScreen].sort((a, b) => a.match_score - b.match_score);
356
+ const lead = sorted[0] || perScreen[0];
357
+ const recommendation = allPassed
358
+ ? `All ${perScreen.length} screen(s) match designs at or above ${(threshold * 100).toFixed(0)}% threshold (min: ${(minScore * 100).toFixed(1)}%).`
359
+ : `${failedScreens.length}/${perScreen.length} screen(s) below threshold ${(threshold * 100).toFixed(0)}%. Worst: "${lead.screen_name}" at ${(lead.match_score * 100).toFixed(1)}%. Fix and re-run codeloop_verify before gate_check.`;
360
+ const uxChecklist = readUxChecklist(input.ux_checklist_path, cwd);
93
361
  return {
94
362
  output: {
95
- match_score: pixelDiffScore,
363
+ match_score: avgScore,
96
364
  differences: [],
97
- screenshots: [refPath, actualPath],
365
+ screenshots: perScreen
366
+ .flatMap((p) => [p.reference_path, p.actual_path])
367
+ .filter((p) => p.length > 0),
98
368
  recommendation,
369
+ per_screen: perScreen,
370
+ min_score: minScore,
371
+ avg_score: avgScore,
372
+ failed_screens: failedScreens,
373
+ threshold,
374
+ source,
99
375
  },
100
376
  imagePaths: {
101
- reference: refPath,
102
- actual: actualPath,
103
- diff: diffPath && existsSync(diffPath) ? diffPath : undefined,
377
+ reference: lead?.reference_path || "",
378
+ actual: lead?.actual_path || "",
379
+ diff: lead?.diff_path,
104
380
  },
105
381
  uxChecklist,
382
+ perScreen,
383
+ source,
384
+ };
385
+ }
386
+ function readUxChecklist(uxChecklistPath, cwd) {
387
+ if (!uxChecklistPath)
388
+ return undefined;
389
+ const uxPath = resolve(cwd, uxChecklistPath);
390
+ if (!existsSync(uxPath))
391
+ return undefined;
392
+ try {
393
+ return readFileSync(uxPath, "utf-8");
394
+ }
395
+ catch {
396
+ return undefined;
397
+ }
398
+ }
399
+ function failureResult(area, description, fixHint, refPath = "") {
400
+ return {
401
+ output: {
402
+ match_score: 0,
403
+ differences: [
404
+ {
405
+ area,
406
+ description,
407
+ severity: "high",
408
+ fix_hint: fixHint,
409
+ },
410
+ ],
411
+ screenshots: refPath ? [refPath] : [],
412
+ recommendation: fixHint,
413
+ per_screen: [],
414
+ min_score: 0,
415
+ avg_score: 0,
416
+ failed_screens: [],
417
+ threshold: 0.8,
418
+ source: "files",
419
+ },
420
+ imagePaths: { reference: refPath, actual: "" },
421
+ perScreen: [],
422
+ source: "files",
106
423
  };
107
424
  }
425
+ export function loadDesignCompareSummary(runDir) {
426
+ const path = join(runDir, "design_compare_summary.json");
427
+ if (!existsSync(path))
428
+ return null;
429
+ try {
430
+ const stat = statSync(path);
431
+ if (stat.size === 0)
432
+ return null;
433
+ return JSON.parse(readFileSync(path, "utf-8"));
434
+ }
435
+ catch {
436
+ return null;
437
+ }
438
+ }
108
439
  //# sourceMappingURL=design_compare.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"design_compare.js","sourceRoot":"","sources":["../../src/tools/design_compare.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"design_compare.js","sourceRoot":"","sources":["../../src/tools/design_compare.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"gate_check.d.ts","sourceRoot":"","sources":["../../src/tools/gate_check.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,cAAc,EAEf,MAAM,sBAAsB,CAAC;AAW9B,wBAAsB,YAAY,CAChC,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,cAAc,EACtB,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,eAAe,CAAC,CAsD1B"}
1
+ {"version":3,"file":"gate_check.d.ts","sourceRoot":"","sources":["../../src/tools/gate_check.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,cAAc,EAEf,MAAM,sBAAsB,CAAC;AAY9B,wBAAsB,YAAY,CAChC,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,cAAc,EACtB,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,eAAe,CAAC,CAsD1B"}
@@ -3,6 +3,7 @@ import { join } from "path";
3
3
  import { DEFAULT_GATES } from "@codelooptech/shared";
4
4
  import { loadRunMeta, getArtifactsBaseDir, getRunDir } from "../evidence/artifacts.js";
5
5
  import { detectPlatform } from "./verify.js";
6
+ import { hasDesignReferences, loadDesignCompareSummary } from "./design_compare.js";
6
7
  export async function runGateCheck(input, config, cwd = process.cwd()) {
7
8
  const baseDir = getArtifactsBaseDir(cwd);
8
9
  const meta = loadRunMeta(input.run_id, baseDir);
@@ -23,7 +24,7 @@ export async function runGateCheck(input, config, cwd = process.cwd()) {
23
24
  const acceptanceContent = safeReadFile(input.acceptance_path, cwd);
24
25
  const evaluations = evaluateGates(meta, config, specContent, acceptanceContent);
25
26
  // Add evidence gates for UI projects
26
- const evidenceGates = evaluateEvidenceGates(input.run_id, cwd);
27
+ const evidenceGates = evaluateEvidenceGates(input.run_id, cwd, config);
27
28
  evaluations.push(...evidenceGates);
28
29
  const passingGates = evaluations.filter((e) => e.passed).map((e) => e.gate.name);
29
30
  const failingGates = evaluations.filter((e) => !e.passed).map((e) => e.gate.name);
@@ -60,7 +61,7 @@ function isUIProject(cwd) {
60
61
  return false;
61
62
  }
62
63
  }
63
- function evaluateEvidenceGates(runId, cwd) {
64
+ function evaluateEvidenceGates(runId, cwd, config) {
64
65
  if (!isUIProject(cwd))
65
66
  return [];
66
67
  const baseDir = getArtifactsBaseDir(cwd);
@@ -127,6 +128,58 @@ function evaluateEvidenceGates(runId, cwd) {
127
128
  ? "Interaction replay frames exist"
128
129
  : "No interaction replay performed. Call codeloop_interaction_replay after stopping the recording.",
129
130
  });
131
+ // Design compare evidence gate — only when references exist (designs/ or .codeloop/figma.json).
132
+ if (hasDesignReferences(cwd)) {
133
+ const designGate = {
134
+ name: "design_compare_evidence",
135
+ description: "Design comparison must run and meet threshold for every reference",
136
+ rule: "min_score >= config.design_match_threshold and no missing screenshots",
137
+ input_artifacts: ["design_compare_summary.json"],
138
+ pass_threshold: true,
139
+ severity_if_failed: "blocker",
140
+ retry_allowance: 5,
141
+ escalation_condition: "Design references unmatched after 5 attempts",
142
+ };
143
+ const summary = loadDesignCompareSummary(runDir);
144
+ const threshold = config.design_match_threshold ?? 0.8;
145
+ if (!summary) {
146
+ results.push({
147
+ gate: designGate,
148
+ passed: false,
149
+ reason: "Design references found in designs/ (or .codeloop/figma.json) but no design_compare_summary.json in this run. " +
150
+ "Call codeloop_design_compare with mode='all' before gate_check.",
151
+ });
152
+ }
153
+ else if (summary.per_screen.length === 0) {
154
+ results.push({
155
+ gate: designGate,
156
+ passed: false,
157
+ reason: "Design compare summary contains no per-screen results.",
158
+ });
159
+ }
160
+ else {
161
+ const failed = summary.failed_screens || [];
162
+ const min = summary.min_score ?? 0;
163
+ if (failed.length === 0 && min >= threshold) {
164
+ results.push({
165
+ gate: designGate,
166
+ passed: true,
167
+ reason: `${summary.per_screen.length} screen(s) match designs (min ${(min * 100).toFixed(1)}% >= ${(threshold * 100).toFixed(0)}% threshold).`,
168
+ });
169
+ }
170
+ else {
171
+ const worst = [...summary.per_screen].sort((a, b) => a.match_score - b.match_score).slice(0, 3);
172
+ const worstSummary = worst
173
+ .map((p) => `${p.screen_name}${p.viewport ? `[${p.viewport}]` : ""}=${(p.match_score * 100).toFixed(1)}%`)
174
+ .join(", ");
175
+ results.push({
176
+ gate: designGate,
177
+ passed: false,
178
+ reason: `${failed.length}/${summary.per_screen.length} screens below ${(threshold * 100).toFixed(0)}% threshold. Worst: ${worstSummary}. Fix and re-run codeloop_verify.`,
179
+ });
180
+ }
181
+ }
182
+ }
130
183
  return results;
131
184
  }
132
185
  function evaluateGate(gate, meta, config, specContent, acceptanceContent) {