@ryuenn3123/agentic-senior-core 3.0.15 → 3.0.17
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.
- package/.agent-context/prompts/bootstrap-design.md +43 -18
- package/.agent-context/rules/architecture.md +13 -0
- package/.agent-context/rules/frontend-architecture.md +26 -0
- package/.agent-context/state/memory-continuity-benchmark.json +1 -1
- package/.agent-context/state/onboarding-report.json +0 -1
- package/.cursorrules +66 -29
- package/.gemini/instructions.md +7 -1
- package/.github/copilot-instructions.md +7 -1
- package/.instructions.md +3 -0
- package/.windsurfrules +66 -29
- package/AGENTS.md +13 -1
- package/lib/cli/architect.mjs +71 -784
- package/lib/cli/commands/init.mjs +30 -100
- package/lib/cli/commands/optimize.mjs +0 -4
- package/lib/cli/commands/upgrade.mjs +2 -5
- package/lib/cli/compiler.mjs +1 -11
- package/lib/cli/constants.mjs +3 -73
- package/lib/cli/detector/design-evidence.mjs +427 -0
- package/lib/cli/detector.mjs +13 -116
- package/lib/cli/init-options.mjs +0 -118
- package/lib/cli/memory-continuity.mjs +2 -1
- package/lib/cli/project-scaffolder/constants.mjs +68 -0
- package/lib/cli/project-scaffolder/design-contract.mjs +857 -0
- package/lib/cli/project-scaffolder/discovery.mjs +315 -0
- package/lib/cli/project-scaffolder/prompt-builders.mjs +213 -0
- package/lib/cli/project-scaffolder/storage.mjs +154 -0
- package/lib/cli/project-scaffolder.mjs +32 -1210
- package/lib/cli/utils.mjs +2 -11
- package/package.json +1 -1
- package/scripts/documentation-boundary-audit.mjs +5 -2
- package/scripts/frontend-usability-audit.mjs +76 -0
- package/scripts/release-gate.mjs +22 -0
- package/scripts/sync-thin-adapters.mjs +24 -0
- package/scripts/ui-design-judge.mjs +365 -7
- package/scripts/validate/config.mjs +446 -0
- package/scripts/validate/coverage-checks.mjs +429 -0
- package/scripts/validate.mjs +44 -854
- package/lib/cli/init-architecture-flow.mjs +0 -233
- package/lib/cli/profile-packs.mjs +0 -108
|
@@ -25,6 +25,8 @@ const DESIGN_INTENT_PATH = resolve(REPOSITORY_ROOT, 'docs', 'design-intent.json'
|
|
|
25
25
|
const DESIGN_GUIDE_PATH = resolve(REPOSITORY_ROOT, 'docs', 'DESIGN.md');
|
|
26
26
|
const MAX_DIFF_CHARS = 12000;
|
|
27
27
|
const UI_FILE_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.vue', '.css', '.scss', '.sass']);
|
|
28
|
+
const DEFAULT_VISUAL_DIFF_REPORT_VERSION = 'hybrid-visual-diff-v1';
|
|
29
|
+
const DEFAULT_REQUIRED_VIEWPORTS = ['mobile', 'tablet', 'desktop'];
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* @typedef {{
|
|
@@ -55,6 +57,32 @@ const UI_FILE_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.vue', '.css'
|
|
|
55
57
|
* alignmentScore: number | null,
|
|
56
58
|
* driftCount: number,
|
|
57
59
|
* blockingCandidateCount: number,
|
|
60
|
+
* meaningfulDiffViewportCount: number,
|
|
61
|
+
* },
|
|
62
|
+
* deterministicVisual: {
|
|
63
|
+
* reportPresent: boolean,
|
|
64
|
+
* reportVersion: string | null,
|
|
65
|
+
* baselineStrategy: string | null,
|
|
66
|
+
* coverageComplete: boolean,
|
|
67
|
+
* sectionCoverageRequired: boolean,
|
|
68
|
+
* requiredViewports: string[],
|
|
69
|
+
* coveredViewports: string[],
|
|
70
|
+
* missingViewports: string[],
|
|
71
|
+
* requiredSectionTypes: string[],
|
|
72
|
+
* coveredSectionTypes: string[],
|
|
73
|
+
* missingSectionTypes: string[],
|
|
74
|
+
* meaningfulDiffViewports: string[],
|
|
75
|
+
* meaningfulDiffSectionTypes: string[],
|
|
76
|
+
* maskedViewportCount: number,
|
|
77
|
+
* sectionCaptureCount: number,
|
|
78
|
+
* tileCaptureCount: number,
|
|
79
|
+
* semanticEscalationRecommended: boolean,
|
|
80
|
+
* notes: string[],
|
|
81
|
+
* },
|
|
82
|
+
* semanticJudge: {
|
|
83
|
+
* attempted: boolean,
|
|
84
|
+
* skipped: boolean,
|
|
85
|
+
* skipReason: string | null,
|
|
58
86
|
* },
|
|
59
87
|
* malformedVerdict: boolean,
|
|
60
88
|
* providerError: boolean,
|
|
@@ -227,16 +255,259 @@ function loadDesignGuide() {
|
|
|
227
255
|
return readFileSync(DESIGN_GUIDE_PATH, 'utf8');
|
|
228
256
|
}
|
|
229
257
|
|
|
258
|
+
function toFiniteRatio(rawValue) {
|
|
259
|
+
return typeof rawValue === 'number' && Number.isFinite(rawValue)
|
|
260
|
+
? rawValue
|
|
261
|
+
: null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function normalizeStringArray(rawValue) {
|
|
265
|
+
if (!Array.isArray(rawValue)) {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return rawValue
|
|
270
|
+
.map((entryValue) => String(entryValue || '').trim())
|
|
271
|
+
.filter(Boolean);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function loadDeterministicVisualReport() {
|
|
275
|
+
if (process.env.UI_VISUAL_DIFF_REPORT_JSON) {
|
|
276
|
+
try {
|
|
277
|
+
return JSON.parse(process.env.UI_VISUAL_DIFF_REPORT_JSON);
|
|
278
|
+
} catch {
|
|
279
|
+
return {
|
|
280
|
+
malformed: true,
|
|
281
|
+
notes: ['UI_VISUAL_DIFF_REPORT_JSON could not be parsed as JSON.'],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (process.env.UI_VISUAL_DIFF_REPORT_PATH) {
|
|
287
|
+
const reportPath = resolve(REPOSITORY_ROOT, process.env.UI_VISUAL_DIFF_REPORT_PATH);
|
|
288
|
+
if (!existsSync(reportPath)) {
|
|
289
|
+
return {
|
|
290
|
+
malformed: true,
|
|
291
|
+
notes: [`UI_VISUAL_DIFF_REPORT_PATH does not exist: ${process.env.UI_VISUAL_DIFF_REPORT_PATH}`],
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
return JSON.parse(readFileSync(reportPath, 'utf8'));
|
|
297
|
+
} catch {
|
|
298
|
+
return {
|
|
299
|
+
malformed: true,
|
|
300
|
+
notes: [`UI_VISUAL_DIFF_REPORT_PATH could not be parsed as JSON: ${process.env.UI_VISUAL_DIFF_REPORT_PATH}`],
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function summarizeDeterministicVisualReport(rawVisualReport, designIntentContent) {
|
|
309
|
+
const visualQaPolicy = designIntentContent?.visualQaPolicy && typeof designIntentContent.visualQaPolicy === 'object'
|
|
310
|
+
? designIntentContent.visualQaPolicy
|
|
311
|
+
: {};
|
|
312
|
+
const capturePlan = visualQaPolicy?.capturePlan && typeof visualQaPolicy.capturePlan === 'object'
|
|
313
|
+
? visualQaPolicy.capturePlan
|
|
314
|
+
: {};
|
|
315
|
+
const requiredViewports = normalizeStringArray(visualQaPolicy.requiredViewports);
|
|
316
|
+
const normalizedRequiredViewports = requiredViewports.length > 0 ? requiredViewports : DEFAULT_REQUIRED_VIEWPORTS;
|
|
317
|
+
const requiredSectionTypes = normalizeStringArray(capturePlan.requiredSectionTypes);
|
|
318
|
+
const meaningfulDiffRatioThreshold = toFiniteRatio(visualQaPolicy?.semanticEscalation?.meaningfulDiffRatioThreshold) ?? 0.01;
|
|
319
|
+
const maxUnmaskedDiffRatio = toFiniteRatio(visualQaPolicy?.stability?.maxUnmaskedDiffRatio) ?? 0.005;
|
|
320
|
+
const maxMaskedDiffRatio = toFiniteRatio(visualQaPolicy?.stability?.maxMaskedDiffRatio) ?? 0.02;
|
|
321
|
+
|
|
322
|
+
if (!rawVisualReport) {
|
|
323
|
+
return {
|
|
324
|
+
reportPresent: false,
|
|
325
|
+
reportVersion: null,
|
|
326
|
+
baselineStrategy: visualQaPolicy.baselineStrategy || null,
|
|
327
|
+
coverageComplete: false,
|
|
328
|
+
sectionCoverageRequired: capturePlan.requireSectionCapturesForLongPages === true,
|
|
329
|
+
requiredViewports: normalizedRequiredViewports,
|
|
330
|
+
coveredViewports: [],
|
|
331
|
+
missingViewports: normalizedRequiredViewports,
|
|
332
|
+
requiredSectionTypes,
|
|
333
|
+
coveredSectionTypes: [],
|
|
334
|
+
missingSectionTypes: requiredSectionTypes,
|
|
335
|
+
meaningfulDiffViewports: [],
|
|
336
|
+
meaningfulDiffSectionTypes: [],
|
|
337
|
+
maskedViewportCount: 0,
|
|
338
|
+
sectionCaptureCount: 0,
|
|
339
|
+
tileCaptureCount: 0,
|
|
340
|
+
semanticEscalationRecommended: false,
|
|
341
|
+
notes: ['No deterministic visual diff report was supplied.'],
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (rawVisualReport.malformed === true) {
|
|
346
|
+
return {
|
|
347
|
+
reportPresent: false,
|
|
348
|
+
reportVersion: null,
|
|
349
|
+
baselineStrategy: visualQaPolicy.baselineStrategy || null,
|
|
350
|
+
coverageComplete: false,
|
|
351
|
+
sectionCoverageRequired: capturePlan.requireSectionCapturesForLongPages === true,
|
|
352
|
+
requiredViewports: normalizedRequiredViewports,
|
|
353
|
+
coveredViewports: [],
|
|
354
|
+
missingViewports: normalizedRequiredViewports,
|
|
355
|
+
requiredSectionTypes,
|
|
356
|
+
coveredSectionTypes: [],
|
|
357
|
+
missingSectionTypes: requiredSectionTypes,
|
|
358
|
+
meaningfulDiffViewports: [],
|
|
359
|
+
meaningfulDiffSectionTypes: [],
|
|
360
|
+
maskedViewportCount: 0,
|
|
361
|
+
sectionCaptureCount: 0,
|
|
362
|
+
tileCaptureCount: 0,
|
|
363
|
+
semanticEscalationRecommended: true,
|
|
364
|
+
notes: normalizeStringArray(rawVisualReport.notes).length > 0
|
|
365
|
+
? normalizeStringArray(rawVisualReport.notes)
|
|
366
|
+
: ['Deterministic visual diff report was malformed.'],
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const viewportResults = Array.isArray(rawVisualReport.viewportResults)
|
|
371
|
+
? rawVisualReport.viewportResults
|
|
372
|
+
.map((rawViewportResult) => {
|
|
373
|
+
const viewportName = String(rawViewportResult?.viewport || '').trim().toLowerCase();
|
|
374
|
+
const pixelDiffRatio = toFiniteRatio(rawViewportResult?.pixelDiffRatio);
|
|
375
|
+
const maskedPixelDiffRatio = toFiniteRatio(rawViewportResult?.maskedPixelDiffRatio);
|
|
376
|
+
const withinNoiseBudget = typeof rawViewportResult?.withinNoiseBudget === 'boolean'
|
|
377
|
+
? rawViewportResult.withinNoiseBudget
|
|
378
|
+
: (pixelDiffRatio === null || pixelDiffRatio <= maxUnmaskedDiffRatio)
|
|
379
|
+
&& (maskedPixelDiffRatio === null || maskedPixelDiffRatio <= maxMaskedDiffRatio);
|
|
380
|
+
const meaningfulDiff = typeof rawViewportResult?.meaningfulDiff === 'boolean'
|
|
381
|
+
? rawViewportResult.meaningfulDiff
|
|
382
|
+
: (pixelDiffRatio !== null && pixelDiffRatio > meaningfulDiffRatioThreshold)
|
|
383
|
+
|| (maskedPixelDiffRatio !== null && maskedPixelDiffRatio > meaningfulDiffRatioThreshold);
|
|
384
|
+
|
|
385
|
+
return {
|
|
386
|
+
viewport: viewportName,
|
|
387
|
+
pixelDiffRatio,
|
|
388
|
+
maskedPixelDiffRatio,
|
|
389
|
+
withinNoiseBudget,
|
|
390
|
+
meaningfulDiff,
|
|
391
|
+
dynamicMaskCategories: normalizeStringArray(rawViewportResult?.dynamicMaskCategories),
|
|
392
|
+
notes: normalizeStringArray(rawViewportResult?.notes),
|
|
393
|
+
};
|
|
394
|
+
})
|
|
395
|
+
.filter((viewportResult) => Boolean(viewportResult.viewport))
|
|
396
|
+
: [];
|
|
397
|
+
const sectionResults = Array.isArray(rawVisualReport.sectionResults)
|
|
398
|
+
? rawVisualReport.sectionResults
|
|
399
|
+
.map((rawSectionResult) => {
|
|
400
|
+
const sectionType = String(rawSectionResult?.sectionType || '').trim().toLowerCase();
|
|
401
|
+
const captureKind = String(rawSectionResult?.captureKind || '').trim().toLowerCase();
|
|
402
|
+
const tileIndex = Number.isInteger(rawSectionResult?.tileIndex) ? rawSectionResult.tileIndex : null;
|
|
403
|
+
const pixelDiffRatio = toFiniteRatio(rawSectionResult?.pixelDiffRatio);
|
|
404
|
+
const maskedPixelDiffRatio = toFiniteRatio(rawSectionResult?.maskedPixelDiffRatio);
|
|
405
|
+
const withinNoiseBudget = typeof rawSectionResult?.withinNoiseBudget === 'boolean'
|
|
406
|
+
? rawSectionResult.withinNoiseBudget
|
|
407
|
+
: (pixelDiffRatio === null || pixelDiffRatio <= maxUnmaskedDiffRatio)
|
|
408
|
+
&& (maskedPixelDiffRatio === null || maskedPixelDiffRatio <= maxMaskedDiffRatio);
|
|
409
|
+
const meaningfulDiff = typeof rawSectionResult?.meaningfulDiff === 'boolean'
|
|
410
|
+
? rawSectionResult.meaningfulDiff
|
|
411
|
+
: (pixelDiffRatio !== null && pixelDiffRatio > meaningfulDiffRatioThreshold)
|
|
412
|
+
|| (maskedPixelDiffRatio !== null && maskedPixelDiffRatio > meaningfulDiffRatioThreshold);
|
|
413
|
+
|
|
414
|
+
return {
|
|
415
|
+
sectionType,
|
|
416
|
+
captureKind,
|
|
417
|
+
tileIndex,
|
|
418
|
+
pixelDiffRatio,
|
|
419
|
+
maskedPixelDiffRatio,
|
|
420
|
+
withinNoiseBudget,
|
|
421
|
+
meaningfulDiff,
|
|
422
|
+
notes: normalizeStringArray(rawSectionResult?.notes),
|
|
423
|
+
};
|
|
424
|
+
})
|
|
425
|
+
.filter((sectionResult) => Boolean(sectionResult.sectionType))
|
|
426
|
+
: [];
|
|
427
|
+
|
|
428
|
+
const coveredViewports = Array.from(new Set(viewportResults.map((viewportResult) => viewportResult.viewport)));
|
|
429
|
+
const missingViewports = normalizedRequiredViewports.filter((requiredViewport) => !coveredViewports.includes(requiredViewport));
|
|
430
|
+
const sectionCoverageRequired = capturePlan.requireSectionCapturesForLongPages === true && (
|
|
431
|
+
rawVisualReport.requiresSectionCoverage === true
|
|
432
|
+
|| String(rawVisualReport.pageLengthCategory || '').trim().toLowerCase() === 'long'
|
|
433
|
+
|| sectionResults.length > 0
|
|
434
|
+
);
|
|
435
|
+
const coveredSectionTypes = Array.from(new Set(sectionResults.map((sectionResult) => sectionResult.sectionType)));
|
|
436
|
+
const missingSectionTypes = sectionCoverageRequired
|
|
437
|
+
? requiredSectionTypes.filter((requiredSectionType) => !coveredSectionTypes.includes(requiredSectionType))
|
|
438
|
+
: [];
|
|
439
|
+
const meaningfulDiffViewports = viewportResults
|
|
440
|
+
.filter((viewportResult) => viewportResult.meaningfulDiff)
|
|
441
|
+
.map((viewportResult) => viewportResult.viewport);
|
|
442
|
+
const meaningfulDiffSectionTypes = Array.from(new Set(
|
|
443
|
+
sectionResults
|
|
444
|
+
.filter((sectionResult) => sectionResult.meaningfulDiff)
|
|
445
|
+
.map((sectionResult) => sectionResult.sectionType)
|
|
446
|
+
));
|
|
447
|
+
const maskedViewportCount = viewportResults.filter((viewportResult) => viewportResult.dynamicMaskCategories.length > 0).length;
|
|
448
|
+
const tileCaptureCount = sectionResults.filter((sectionResult) => sectionResult.captureKind === 'tile').length;
|
|
449
|
+
const reportNotes = normalizeStringArray(rawVisualReport.notes);
|
|
450
|
+
|
|
451
|
+
const semanticEscalationRecommended = rawVisualReport?.summary?.semanticEscalationRecommended === true
|
|
452
|
+
|| meaningfulDiffViewports.length > 0
|
|
453
|
+
|| meaningfulDiffSectionTypes.length > 0
|
|
454
|
+
|| (
|
|
455
|
+
visualQaPolicy?.semanticEscalation?.escalateWhenViewportCoverageIncomplete === true
|
|
456
|
+
&& missingViewports.length > 0
|
|
457
|
+
)
|
|
458
|
+
|| (
|
|
459
|
+
sectionCoverageRequired
|
|
460
|
+
&& missingSectionTypes.length > 0
|
|
461
|
+
);
|
|
462
|
+
const fallbackNotes = [];
|
|
463
|
+
if (viewportResults.length === 0) {
|
|
464
|
+
fallbackNotes.push('Deterministic visual diff report did not include viewportResults.');
|
|
465
|
+
}
|
|
466
|
+
if (sectionCoverageRequired && sectionResults.length === 0) {
|
|
467
|
+
fallbackNotes.push('Long-page screenshot coverage was required, but sectionResults were not provided.');
|
|
468
|
+
}
|
|
469
|
+
if (sectionCoverageRequired && missingSectionTypes.length > 0) {
|
|
470
|
+
fallbackNotes.push(`Long-page screenshot coverage is incomplete. Missing section captures: ${missingSectionTypes.join(', ')}.`);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
reportPresent: true,
|
|
475
|
+
reportVersion: String(rawVisualReport.reportVersion || DEFAULT_VISUAL_DIFF_REPORT_VERSION),
|
|
476
|
+
baselineStrategy: String(rawVisualReport.baselineStrategy || visualQaPolicy.baselineStrategy || 'deterministic-screenshots'),
|
|
477
|
+
coverageComplete: missingViewports.length === 0 && (!sectionCoverageRequired || missingSectionTypes.length === 0),
|
|
478
|
+
sectionCoverageRequired,
|
|
479
|
+
requiredViewports: normalizedRequiredViewports,
|
|
480
|
+
coveredViewports,
|
|
481
|
+
missingViewports,
|
|
482
|
+
requiredSectionTypes,
|
|
483
|
+
coveredSectionTypes,
|
|
484
|
+
missingSectionTypes,
|
|
485
|
+
meaningfulDiffViewports,
|
|
486
|
+
meaningfulDiffSectionTypes,
|
|
487
|
+
maskedViewportCount,
|
|
488
|
+
sectionCaptureCount: sectionResults.length,
|
|
489
|
+
tileCaptureCount,
|
|
490
|
+
semanticEscalationRecommended,
|
|
491
|
+
notes: reportNotes.length > 0
|
|
492
|
+
? reportNotes
|
|
493
|
+
: fallbackNotes,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
230
497
|
function buildSystemPrompt() {
|
|
231
498
|
return [
|
|
232
499
|
'You are a Principal UI/UX Design Reviewer.',
|
|
233
500
|
'Compare the changed UI code against the provided design contract.',
|
|
234
501
|
'Treat docs/design-intent.json as the machine-readable source of truth.',
|
|
235
502
|
'Treat docs/DESIGN.md as explanatory context, not a generic style guide.',
|
|
503
|
+
'When deterministic visual diff evidence is provided, treat it as the first layer of truth for noise filtering, viewport coverage, long-page section coverage, and meaningful-drift detection.',
|
|
236
504
|
'Do not reward generic SaaS defaults or popular template patterns.',
|
|
237
505
|
'Do not penalize originality when the implementation still aligns with the contract.',
|
|
238
506
|
'Purposeful motion is allowed and can improve quality. Only flag motion when it drifts from the contract, ignores reduced-motion expectations, or adds avoidable performance/accessibility risk.',
|
|
239
507
|
'Only flag drift when there is a clear mismatch with the contract, accessibility non-negotiables, or cross-viewport adaptation rules.',
|
|
508
|
+
'Treat WCAG 2.2 AA failures as hard accessibility drift.',
|
|
509
|
+
'Treat APCA as advisory perceptual tuning only. Do not recommend blocking solely because APCA would prefer a stronger readability adjustment when WCAG hard requirements still pass.',
|
|
510
|
+
'Check focus visibility, focus appearance, target size, keyboard access, accessible authentication, and status or dynamic state access when the diff touches those surfaces.',
|
|
240
511
|
'This audit always runs in advisory mode for this repository workflow.',
|
|
241
512
|
'Focus on color intent, typographic hierarchy, responsive re-layout, purposeful motion, component morphology across states, interaction behavior, and genericity drift.',
|
|
242
513
|
'Return ONLY one JSON object on a single line prefixed with JSON_VERDICT:.',
|
|
@@ -245,7 +516,7 @@ function buildSystemPrompt() {
|
|
|
245
516
|
].join('\n');
|
|
246
517
|
}
|
|
247
518
|
|
|
248
|
-
function buildUserMessage(designIntentContent, designGuideContent, diffContent, changedUiFiles) {
|
|
519
|
+
function buildUserMessage(designIntentContent, designGuideContent, diffContent, changedUiFiles, deterministicVisualSummary) {
|
|
249
520
|
const truncatedDiff = diffContent.length > MAX_DIFF_CHARS
|
|
250
521
|
? `${diffContent.slice(0, MAX_DIFF_CHARS)}\n\n[DIFF TRUNCATED - ${diffContent.length - MAX_DIFF_CHARS} additional characters omitted]`
|
|
251
522
|
: diffContent;
|
|
@@ -264,6 +535,11 @@ function buildUserMessage(designIntentContent, designGuideContent, diffContent,
|
|
|
264
535
|
designGuideContent.trim() || '(missing DESIGN.md)',
|
|
265
536
|
'```',
|
|
266
537
|
'',
|
|
538
|
+
'## Deterministic Visual Diff Summary',
|
|
539
|
+
'```json',
|
|
540
|
+
JSON.stringify(deterministicVisualSummary, null, 2),
|
|
541
|
+
'```',
|
|
542
|
+
'',
|
|
267
543
|
'## UI Diff',
|
|
268
544
|
'```diff',
|
|
269
545
|
truncatedDiff.trim() || '(no UI diff)',
|
|
@@ -416,7 +692,7 @@ function buildReport(partialReport) {
|
|
|
416
692
|
return {
|
|
417
693
|
generatedAt: new Date().toISOString(),
|
|
418
694
|
auditName: 'ui-design-judge',
|
|
419
|
-
schemaVersion: '1.
|
|
695
|
+
schemaVersion: '1.1',
|
|
420
696
|
mode: 'advisory',
|
|
421
697
|
advisoryOnly: true,
|
|
422
698
|
passed: true,
|
|
@@ -430,6 +706,25 @@ function buildReport(partialReport) {
|
|
|
430
706
|
alignmentScore: null,
|
|
431
707
|
driftCount: 0,
|
|
432
708
|
blockingCandidateCount: 0,
|
|
709
|
+
meaningfulDiffViewportCount: 0,
|
|
710
|
+
},
|
|
711
|
+
deterministicVisual: {
|
|
712
|
+
reportPresent: false,
|
|
713
|
+
reportVersion: null,
|
|
714
|
+
baselineStrategy: null,
|
|
715
|
+
coverageComplete: false,
|
|
716
|
+
requiredViewports: [],
|
|
717
|
+
coveredViewports: [],
|
|
718
|
+
missingViewports: [],
|
|
719
|
+
meaningfulDiffViewports: [],
|
|
720
|
+
maskedViewportCount: 0,
|
|
721
|
+
semanticEscalationRecommended: false,
|
|
722
|
+
notes: [],
|
|
723
|
+
},
|
|
724
|
+
semanticJudge: {
|
|
725
|
+
attempted: false,
|
|
726
|
+
skipped: false,
|
|
727
|
+
skipReason: null,
|
|
433
728
|
},
|
|
434
729
|
malformedVerdict: false,
|
|
435
730
|
providerError: false,
|
|
@@ -470,14 +765,53 @@ async function main() {
|
|
|
470
765
|
alignmentScore: null,
|
|
471
766
|
driftCount: 0,
|
|
472
767
|
blockingCandidateCount: 0,
|
|
768
|
+
meaningfulDiffViewportCount: 0,
|
|
473
769
|
},
|
|
474
770
|
notes: ['UI design judge only evaluates changed UI surfaces.'],
|
|
475
771
|
}));
|
|
476
772
|
return;
|
|
477
773
|
}
|
|
478
774
|
|
|
775
|
+
const deterministicVisualSummary = summarizeDeterministicVisualReport(
|
|
776
|
+
loadDeterministicVisualReport(),
|
|
777
|
+
designIntentContent
|
|
778
|
+
);
|
|
779
|
+
const shouldRunSemanticJudge = !deterministicVisualSummary.reportPresent
|
|
780
|
+
|| deterministicVisualSummary.semanticEscalationRecommended;
|
|
781
|
+
|
|
782
|
+
if (!shouldRunSemanticJudge) {
|
|
783
|
+
emitMachineReadableReport(buildReport({
|
|
784
|
+
provider: 'none',
|
|
785
|
+
contractPresent: true,
|
|
786
|
+
summary: {
|
|
787
|
+
changedUiFileCount: changedUiFiles.length,
|
|
788
|
+
alignmentScore: null,
|
|
789
|
+
driftCount: 0,
|
|
790
|
+
blockingCandidateCount: 0,
|
|
791
|
+
meaningfulDiffViewportCount: deterministicVisualSummary.meaningfulDiffViewports.length,
|
|
792
|
+
},
|
|
793
|
+
deterministicVisual: deterministicVisualSummary,
|
|
794
|
+
semanticJudge: {
|
|
795
|
+
attempted: false,
|
|
796
|
+
skipped: true,
|
|
797
|
+
skipReason: 'deterministic-clean',
|
|
798
|
+
},
|
|
799
|
+
notes: [
|
|
800
|
+
'Deterministic visual diff reported no meaningful drift, so semantic review was skipped.',
|
|
801
|
+
...deterministicVisualSummary.notes,
|
|
802
|
+
],
|
|
803
|
+
}));
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
|
|
479
807
|
const systemPrompt = buildSystemPrompt();
|
|
480
|
-
const userMessage = buildUserMessage(
|
|
808
|
+
const userMessage = buildUserMessage(
|
|
809
|
+
designIntentContent,
|
|
810
|
+
designGuideContent,
|
|
811
|
+
rawDiff,
|
|
812
|
+
changedUiFiles,
|
|
813
|
+
deterministicVisualSummary
|
|
814
|
+
);
|
|
481
815
|
|
|
482
816
|
const selectedProvider = selectAvailableProvider();
|
|
483
817
|
if (!selectedProvider) {
|
|
@@ -489,8 +823,18 @@ async function main() {
|
|
|
489
823
|
alignmentScore: null,
|
|
490
824
|
driftCount: 0,
|
|
491
825
|
blockingCandidateCount: 0,
|
|
826
|
+
meaningfulDiffViewportCount: deterministicVisualSummary.meaningfulDiffViewports.length,
|
|
492
827
|
},
|
|
493
|
-
|
|
828
|
+
deterministicVisual: deterministicVisualSummary,
|
|
829
|
+
semanticJudge: {
|
|
830
|
+
attempted: false,
|
|
831
|
+
skipped: true,
|
|
832
|
+
skipReason: 'no-provider-configured',
|
|
833
|
+
},
|
|
834
|
+
notes: [
|
|
835
|
+
'No LLM provider configured. UI design judge skipped provider review and stayed advisory.',
|
|
836
|
+
...deterministicVisualSummary.notes,
|
|
837
|
+
],
|
|
494
838
|
}));
|
|
495
839
|
return;
|
|
496
840
|
}
|
|
@@ -512,8 +856,15 @@ async function main() {
|
|
|
512
856
|
alignmentScore: null,
|
|
513
857
|
driftCount: 0,
|
|
514
858
|
blockingCandidateCount: 0,
|
|
859
|
+
meaningfulDiffViewportCount: deterministicVisualSummary.meaningfulDiffViewports.length,
|
|
515
860
|
},
|
|
516
|
-
|
|
861
|
+
deterministicVisual: deterministicVisualSummary,
|
|
862
|
+
semanticJudge: {
|
|
863
|
+
attempted: true,
|
|
864
|
+
skipped: false,
|
|
865
|
+
skipReason: null,
|
|
866
|
+
},
|
|
867
|
+
notes: [`Provider call failed: ${providerErrorMessage}`, ...deterministicVisualSummary.notes],
|
|
517
868
|
passed: true,
|
|
518
869
|
}));
|
|
519
870
|
return;
|
|
@@ -537,11 +888,18 @@ async function main() {
|
|
|
537
888
|
alignmentScore,
|
|
538
889
|
driftCount: findings.length,
|
|
539
890
|
blockingCandidateCount,
|
|
891
|
+
meaningfulDiffViewportCount: deterministicVisualSummary.meaningfulDiffViewports.length,
|
|
892
|
+
},
|
|
893
|
+
deterministicVisual: deterministicVisualSummary,
|
|
894
|
+
semanticJudge: {
|
|
895
|
+
attempted: true,
|
|
896
|
+
skipped: false,
|
|
897
|
+
skipReason: null,
|
|
540
898
|
},
|
|
541
899
|
findings,
|
|
542
900
|
notes: malformed
|
|
543
|
-
? ['LLM response was malformed. Advisory mode kept the audit non-blocking.']
|
|
544
|
-
: notes,
|
|
901
|
+
? ['LLM response was malformed. Advisory mode kept the audit non-blocking.', ...deterministicVisualSummary.notes]
|
|
902
|
+
: [...notes, ...deterministicVisualSummary.notes],
|
|
545
903
|
});
|
|
546
904
|
|
|
547
905
|
emitMachineReadableReport(reportPayload);
|