@ryuenn3123/agentic-senior-core 3.0.10 → 3.0.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.
@@ -6,30 +6,14 @@
6
6
  *
7
7
  * Advisory-first UI design contract judge.
8
8
  *
9
+ * Repo-internal workflow audit; no user-facing runtime modes.
9
10
  * Compares changed UI diffs against docs/design-intent.json and docs/DESIGN.md.
10
- * Default mode is advisory: findings never block release. Strict mode is opt-in.
11
- *
12
- * Usage:
13
- * node scripts/ui-design-judge.mjs
14
- * node scripts/ui-design-judge.mjs --dry-run
15
- * node scripts/ui-design-judge.mjs --strict
16
- *
17
- * Environment variables:
18
- * OPENAI_API_KEY / ANTHROPIC_API_KEY / GEMINI_API_KEY
19
- * UI_DESIGN_JUDGE_MODEL Override model for this script
20
- * LLM_JUDGE_MODEL Shared fallback model override
21
- * UI_DESIGN_JUDGE_MAX_DIFF_CHARS Max diff chars to send (default: 12000)
22
- * UI_DESIGN_JUDGE_OUTPUT_PATH Machine-readable report output path
23
- * UI_DESIGN_JUDGE_EMIT_JSON false disables report file emission
24
- * UI_DESIGN_JUDGE_MOCK_RESPONSE Test-only raw LLM response body
25
- * UI_DESIGN_JUDGE_CHANGED_FILES Optional comma/newline-separated changed file override
26
- * PR_DIFF Inject diff directly
27
- * GITHUB_BASE_SHA / GITHUB_HEAD_SHA
28
- * CI_MERGE_REQUEST_DIFF_BASE_SHA / CI_COMMIT_SHA
11
+ * Runs only in advisory mode for this repository workflow.
12
+ * Emits JSON to stdout for release-gate and CI consumption.
29
13
  */
30
14
 
31
15
  import { execSync } from 'node:child_process';
32
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
16
+ import { existsSync, readFileSync } from 'node:fs';
33
17
  import { resolve, dirname, extname } from 'node:path';
34
18
  import { fileURLToPath } from 'node:url';
35
19
 
@@ -39,13 +23,7 @@ const REPOSITORY_ROOT = resolve(__dirname, '..');
39
23
 
40
24
  const DESIGN_INTENT_PATH = resolve(REPOSITORY_ROOT, 'docs', 'design-intent.json');
41
25
  const DESIGN_GUIDE_PATH = resolve(REPOSITORY_ROOT, 'docs', 'DESIGN.md');
42
- const DEFAULT_MACHINE_REPORT_PATH = resolve(REPOSITORY_ROOT, '.agent-context', 'state', 'ui-design-judge-report.json');
43
- const MACHINE_REPORT_PATH = process.env.UI_DESIGN_JUDGE_OUTPUT_PATH || DEFAULT_MACHINE_REPORT_PATH;
44
- const SHOULD_EMIT_MACHINE_REPORT = process.env.UI_DESIGN_JUDGE_EMIT_JSON !== 'false';
45
- const MAX_DIFF_CHARS = parseInt(process.env.UI_DESIGN_JUDGE_MAX_DIFF_CHARS ?? '12000', 10);
46
- const IS_DRY_RUN = process.argv.includes('--dry-run');
47
- const IS_STRICT_MODE = process.argv.includes('--strict');
48
- const IS_ADVISORY_MODE = !IS_STRICT_MODE;
26
+ const MAX_DIFF_CHARS = 12000;
49
27
  const UI_FILE_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.vue', '.css', '.scss', '.sass']);
50
28
 
51
29
  /**
@@ -64,7 +42,7 @@ const UI_FILE_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.vue', '.css'
64
42
  * generatedAt: string,
65
43
  * auditName: string,
66
44
  * schemaVersion: string,
67
- * mode: 'advisory' | 'strict',
45
+ * mode: 'advisory',
68
46
  * advisoryOnly: boolean,
69
47
  * passed: boolean,
70
48
  * skipped: boolean,
@@ -177,13 +155,6 @@ function collectPullRequestDiff() {
177
155
  }
178
156
 
179
157
  function collectChangedFiles() {
180
- if (process.env.UI_DESIGN_JUDGE_CHANGED_FILES) {
181
- return process.env.UI_DESIGN_JUDGE_CHANGED_FILES
182
- .split(/[\r\n,]+/u)
183
- .map((filePath) => filePath.trim())
184
- .filter(Boolean);
185
- }
186
-
187
158
  if (process.env.PR_DIFF) {
188
159
  const filePathSet = new Set();
189
160
  for (const diffHeaderMatch of process.env.PR_DIFF.matchAll(/^diff --git a\/(.+?) b\/(.+)$/gm)) {
@@ -256,7 +227,7 @@ function loadDesignGuide() {
256
227
  return readFileSync(DESIGN_GUIDE_PATH, 'utf8');
257
228
  }
258
229
 
259
- function buildSystemPrompt(modeLabel) {
230
+ function buildSystemPrompt() {
260
231
  return [
261
232
  'You are a Principal UI/UX Design Reviewer.',
262
233
  'Compare the changed UI code against the provided design contract.',
@@ -264,9 +235,10 @@ function buildSystemPrompt(modeLabel) {
264
235
  'Treat docs/DESIGN.md as explanatory context, not a generic style guide.',
265
236
  'Do not reward generic SaaS defaults or popular template patterns.',
266
237
  'Do not penalize originality when the implementation still aligns with the contract.',
238
+ '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.',
267
239
  'Only flag drift when there is a clear mismatch with the contract, accessibility non-negotiables, or cross-viewport adaptation rules.',
268
- `Current mode: ${modeLabel}. In advisory mode, findings are recommendations and should not be framed as release blockers unless blockingRecommended is clearly true.`,
269
- 'Focus on color intent, typographic hierarchy, responsive re-layout, interaction behavior, and genericity drift.',
240
+ 'This audit always runs in advisory mode for this repository workflow.',
241
+ 'Focus on color intent, typographic hierarchy, responsive re-layout, purposeful motion, component morphology across states, interaction behavior, and genericity drift.',
270
242
  'Return ONLY one JSON object on a single line prefixed with JSON_VERDICT:.',
271
243
  'Schema:',
272
244
  '{"alignmentScore": number|null, "notes": string[], "findings": [{"area": string, "severity": "high|medium|low", "problem": string, "evidence": string, "recommendation": string, "blockingRecommended": boolean}]}',
@@ -297,12 +269,12 @@ function buildUserMessage(designIntentContent, designGuideContent, diffContent,
297
269
  truncatedDiff.trim() || '(no UI diff)',
298
270
  '```',
299
271
  '',
300
- 'Judge alignment to the contract. Avoid aesthetic bias toward generic web trends.',
272
+ 'Judge alignment to the contract. Avoid aesthetic bias toward generic web trends or toward motionless/static outputs.',
301
273
  ].join('\n');
302
274
  }
303
275
 
304
276
  async function callOpenAiProvider(systemPrompt, userMessage) {
305
- const selectedModel = process.env.UI_DESIGN_JUDGE_MODEL ?? process.env.LLM_JUDGE_MODEL ?? 'gpt-4o-mini';
277
+ const selectedModel = process.env.LLM_JUDGE_MODEL ?? 'gpt-4o-mini';
306
278
  const apiResponse = await fetch('https://api.openai.com/v1/chat/completions', {
307
279
  method: 'POST',
308
280
  headers: {
@@ -330,7 +302,7 @@ async function callOpenAiProvider(systemPrompt, userMessage) {
330
302
  }
331
303
 
332
304
  async function callAnthropicProvider(systemPrompt, userMessage) {
333
- const selectedModel = process.env.UI_DESIGN_JUDGE_MODEL ?? process.env.LLM_JUDGE_MODEL ?? 'claude-3-5-haiku-latest';
305
+ const selectedModel = process.env.LLM_JUDGE_MODEL ?? 'claude-3-5-haiku-latest';
334
306
  const apiResponse = await fetch('https://api.anthropic.com/v1/messages', {
335
307
  method: 'POST',
336
308
  headers: {
@@ -356,7 +328,7 @@ async function callAnthropicProvider(systemPrompt, userMessage) {
356
328
  }
357
329
 
358
330
  async function callGeminiProvider(systemPrompt, userMessage) {
359
- const selectedModel = process.env.UI_DESIGN_JUDGE_MODEL ?? process.env.LLM_JUDGE_MODEL ?? 'gemini-2.0-flash';
331
+ const selectedModel = process.env.LLM_JUDGE_MODEL ?? 'gemini-2.0-flash';
360
332
  const apiKey = process.env.GEMINI_API_KEY ?? '';
361
333
  const endpointUrl = `https://generativelanguage.googleapis.com/v1beta/models/${selectedModel}:generateContent?key=${apiKey}`;
362
334
 
@@ -445,8 +417,8 @@ function buildReport(partialReport) {
445
417
  generatedAt: new Date().toISOString(),
446
418
  auditName: 'ui-design-judge',
447
419
  schemaVersion: '1.0',
448
- mode: IS_STRICT_MODE ? 'strict' : 'advisory',
449
- advisoryOnly: IS_ADVISORY_MODE,
420
+ mode: 'advisory',
421
+ advisoryOnly: true,
450
422
  passed: true,
451
423
  skipped: false,
452
424
  skipReason: null,
@@ -468,10 +440,6 @@ function buildReport(partialReport) {
468
440
  }
469
441
 
470
442
  function emitMachineReadableReport(machineReportPayload) {
471
- if (SHOULD_EMIT_MACHINE_REPORT) {
472
- writeFileSync(MACHINE_REPORT_PATH, `${JSON.stringify(machineReportPayload, null, 2)}\n`, 'utf8');
473
- }
474
-
475
443
  console.log(JSON.stringify(machineReportPayload, null, 2));
476
444
  }
477
445
 
@@ -508,28 +476,9 @@ async function main() {
508
476
  return;
509
477
  }
510
478
 
511
- const systemPrompt = buildSystemPrompt(IS_STRICT_MODE ? 'strict' : 'advisory');
479
+ const systemPrompt = buildSystemPrompt();
512
480
  const userMessage = buildUserMessage(designIntentContent, designGuideContent, rawDiff, changedUiFiles);
513
481
 
514
- if (IS_DRY_RUN) {
515
- emitMachineReadableReport(buildReport({
516
- provider: 'dry-run',
517
- contractPresent: true,
518
- summary: {
519
- changedUiFileCount: changedUiFiles.length,
520
- alignmentScore: null,
521
- driftCount: 0,
522
- blockingCandidateCount: 0,
523
- },
524
- notes: [
525
- 'Dry run enabled. No LLM provider call was made.',
526
- `System prompt chars: ${systemPrompt.length}`,
527
- `User message chars: ${userMessage.length}`,
528
- ],
529
- }));
530
- return;
531
- }
532
-
533
482
  const selectedProvider = selectAvailableProvider();
534
483
  if (!selectedProvider) {
535
484
  emitMachineReadableReport(buildReport({
@@ -541,7 +490,7 @@ async function main() {
541
490
  driftCount: 0,
542
491
  blockingCandidateCount: 0,
543
492
  },
544
- notes: ['No LLM provider configured. UI design judge skipped provider review and remained advisory.'],
493
+ notes: ['No LLM provider configured. UI design judge skipped provider review and stayed advisory.'],
545
494
  }));
546
495
  return;
547
496
  }
@@ -565,13 +514,8 @@ async function main() {
565
514
  blockingCandidateCount: 0,
566
515
  },
567
516
  notes: [`Provider call failed: ${providerErrorMessage}`],
568
- passed: IS_ADVISORY_MODE,
517
+ passed: true,
569
518
  }));
570
-
571
- if (IS_STRICT_MODE) {
572
- process.exit(1);
573
- }
574
-
575
519
  return;
576
520
  }
577
521
 
@@ -582,12 +526,11 @@ async function main() {
582
526
  const notes = Array.isArray(verdict?.notes)
583
527
  ? verdict.notes.map((note) => String(note))
584
528
  : [];
585
- const shouldFailInStrictMode = IS_STRICT_MODE && blockingCandidateCount > 0;
586
529
 
587
530
  const reportPayload = buildReport({
588
531
  provider: selectedProvider.providerName,
589
532
  contractPresent: true,
590
- passed: malformed ? IS_ADVISORY_MODE : !shouldFailInStrictMode,
533
+ passed: true,
591
534
  malformedVerdict: malformed,
592
535
  summary: {
593
536
  changedUiFileCount: changedUiFiles.length,
@@ -602,10 +545,6 @@ async function main() {
602
545
  });
603
546
 
604
547
  emitMachineReadableReport(reportPayload);
605
-
606
- if (IS_STRICT_MODE && (malformed || shouldFailInStrictMode)) {
607
- process.exit(1);
608
- }
609
548
  }
610
549
 
611
550
  main().catch((unexpectedError) => {
@@ -616,11 +555,7 @@ main().catch((unexpectedError) => {
616
555
  emitMachineReadableReport(buildReport({
617
556
  provider: 'none',
618
557
  providerError: true,
619
- passed: IS_ADVISORY_MODE,
558
+ passed: true,
620
559
  notes: [`Unexpected ui-design-judge failure: ${errorMessage}`],
621
560
  }));
622
-
623
- if (IS_STRICT_MODE) {
624
- process.exit(1);
625
- }
626
561
  });
@@ -285,13 +285,16 @@ const REQUIRED_UI_DESIGN_AUTOMATION_SNIPPETS = [
285
285
  'Responsive Strategy and Cross-Viewport Adaptation Matrix',
286
286
  '`colorTruth.format`',
287
287
  '`crossViewportAdaptation.mutationRules.mobile/tablet/desktop`',
288
+ '`motionSystem`',
289
+ '`componentMorphology`',
288
290
  ],
289
291
  },
290
292
  {
291
293
  path: 'scripts/ui-design-judge.mjs',
292
294
  snippets: [
293
295
  'Advisory-first UI design contract judge.',
294
- 'Default mode is advisory: findings never block release. Strict mode is opt-in.',
296
+ 'Repo-internal workflow audit; no user-facing runtime modes.',
297
+ 'Runs only in advisory mode for this repository workflow.',
295
298
  'Do not reward generic SaaS defaults or popular template patterns.',
296
299
  'UI design judge only evaluates changed UI surfaces.',
297
300
  ],
@@ -301,6 +304,8 @@ const REQUIRED_UI_DESIGN_AUTOMATION_SNIPPETS = [
301
304
  snippets: [
302
305
  'colorTruth',
303
306
  'crossViewportAdaptation',
307
+ 'motionSystem',
308
+ 'componentMorphology',
304
309
  'requireViewportMutationRules',
305
310
  'allowHexDerivatives',
306
311
  ],
@@ -1,25 +0,0 @@
1
- {
2
- "generatedAt": "2026-04-20T12:33:27.082Z",
3
- "auditName": "ui-design-judge",
4
- "schemaVersion": "1.0",
5
- "mode": "advisory",
6
- "advisoryOnly": true,
7
- "passed": true,
8
- "skipped": true,
9
- "skipReason": "Design contract is missing or unreadable. Skipping UI design judge.",
10
- "provider": "none",
11
- "ciProvider": "github",
12
- "contractPresent": false,
13
- "summary": {
14
- "changedUiFileCount": 0,
15
- "alignmentScore": null,
16
- "driftCount": 0,
17
- "blockingCandidateCount": 0
18
- },
19
- "malformedVerdict": false,
20
- "providerError": false,
21
- "findings": [],
22
- "notes": [
23
- "docs/design-intent.json is required for contract-aware UI judging."
24
- ]
25
- }