@ryuenn3123/agentic-senior-core 3.0.9 → 3.0.10

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.
@@ -306,6 +306,21 @@ function shouldBootstrapDesignDocument(discoveryAnswers, initContext) {
306
306
  return false;
307
307
  }
308
308
 
309
+ const DESIGN_REQUIRED_SECTIONS = [
310
+ 'Design Intent and Product Personality',
311
+ 'Audience and Use-Context Signals',
312
+ 'Visual Direction and Distinctive Moves',
313
+ 'Color Science and Semantic Roles',
314
+ 'Typographic Engineering and Hierarchy',
315
+ 'Spacing, Layout Rhythm, and Density Strategy',
316
+ 'Responsive Strategy and Cross-Viewport Adaptation Matrix',
317
+ 'Interaction, Motion, and Feedback Rules',
318
+ 'Component Language and Shared Patterns',
319
+ 'Accessibility Non-Negotiables',
320
+ 'Anti-Patterns to Avoid',
321
+ 'Implementation Notes for Future UI Tasks',
322
+ ];
323
+
309
324
  function inferDesignKeywords(discoveryAnswers) {
310
325
  const normalizedDescription = String(discoveryAnswers.projectDescription || '').toLowerCase();
311
326
  const normalizedDomain = String(discoveryAnswers.primaryDomain || '').toLowerCase();
@@ -316,64 +331,114 @@ function inferDesignKeywords(discoveryAnswers) {
316
331
 
317
332
  if (aggregateText.includes('commerce') || aggregateText.includes('catalog') || aggregateText.includes('checkout')) {
318
333
  return {
334
+ designPhilosophy: 'Conversion clarity with premium restraint.',
319
335
  brandAdjectives: ['clear', 'desirable', 'confident'],
320
336
  antiAdjectives: ['cluttered', 'hesitant', 'coupon-noisy'],
337
+ typographyScaleRatio: '1.200',
338
+ baseGridUnit: 8,
339
+ densityMode: 'conversion-focused',
340
+ colorIntent: 'Use a restrained neutral foundation with one controlled accent reserved for buying cues and trust moments.',
321
341
  distinctiveMoves: [
322
342
  'Use product hierarchy and buying cues without turning the interface into a discount template.',
323
343
  'Keep decision-critical information prominent while secondary merchandising stays quiet.',
324
344
  'Let imagery and spacing create premium perception before decorative effects do.',
325
345
  ],
346
+ mutationRules: {
347
+ mobile: 'Convert browsing into vertically stacked product cards, move cart and filter actions into sticky or bottom-sheet patterns, and keep thumb-reach actions persistent.',
348
+ tablet: 'Preserve browsing flow with a two-column rhythm, collapse tertiary filters, and keep comparison moments visible without forcing desktop density.',
349
+ desktop: 'Expose multi-column merchandising, comparison views, and richer product context while keeping the purchase path visually dominant.',
350
+ },
326
351
  };
327
352
  }
328
353
 
329
354
  if (aggregateText.includes('dashboard') || aggregateText.includes('operations') || aggregateText.includes('report')) {
330
355
  return {
356
+ designPhilosophy: 'Operational calm under high information density.',
331
357
  brandAdjectives: ['calm', 'precise', 'trustworthy'],
332
358
  antiAdjectives: ['chaotic', 'gimmicky', 'visually exhausting'],
359
+ typographyScaleRatio: '1.125',
360
+ baseGridUnit: 4,
361
+ densityMode: 'high-density-scanning',
362
+ colorIntent: 'Use neutrals for structure and reserve accent saturation for status shifts, risky actions, and alerts.',
333
363
  distinctiveMoves: [
334
364
  'Prioritize scanning clarity and status recognition over decorative density.',
335
365
  'Use visual weight to separate signal from operational noise.',
336
366
  'Reserve strong accents for alerts, decisions, and state transitions only.',
337
367
  ],
368
+ mutationRules: {
369
+ mobile: 'Collapse dense tables into prioritized cards or row groups, move filters into drawers or sheets, and pin the most critical actions to the bottom reach zone.',
370
+ tablet: 'Keep two-column or split-pane workflows, collapse tertiary panels, and maintain fast scan paths for operators using touch or keyboard.',
371
+ desktop: 'Expose the highest-density views with visible navigation, comparison surfaces, and simultaneous context panels for power users.',
372
+ },
338
373
  };
339
374
  }
340
375
 
341
376
  if (aggregateText.includes('developer') || aggregateText.includes('api') || aggregateText.includes('platform')) {
342
377
  return {
378
+ designPhilosophy: 'Technical precision with explicit structure and honest feedback.',
343
379
  brandAdjectives: ['precise', 'technical', 'transparent'],
344
380
  antiAdjectives: ['vague', 'marketing-heavy', 'template-polished'],
381
+ typographyScaleRatio: '1.125',
382
+ baseGridUnit: 4,
383
+ densityMode: 'technical-utility',
384
+ colorIntent: 'Anchor the interface in disciplined neutrals and use accent color only where state, feedback, or code-adjacent interaction needs emphasis.',
345
385
  distinctiveMoves: [
346
386
  'Make structure and feedback feel exact without becoming sterile.',
347
387
  'Use code-adjacent rhythm and hierarchy to build trust with technical users.',
348
388
  'Keep complexity legible through spacing, grouping, and explicit interaction states.',
349
389
  ],
390
+ mutationRules: {
391
+ mobile: 'Switch multi-pane technical layouts into stacked sections, turn secondary navigation into segmented or sheet-based controls, and keep commands near the content they affect.',
392
+ tablet: 'Retain split-view comprehension where possible, compress chrome, and keep documentation or diagnostics adjacent to the active task.',
393
+ desktop: 'Expose full navigation, dense comparison surfaces, and multi-pane workflows for expert scanning and debugging.',
394
+ },
350
395
  };
351
396
  }
352
397
 
353
398
  if (aggregateText.includes('content') || aggregateText.includes('community') || aggregateText.includes('publish')) {
354
399
  return {
400
+ designPhilosophy: 'Editorial flow with warm but controlled expression.',
355
401
  brandAdjectives: ['editorial', 'warm', 'expressive'],
356
402
  antiAdjectives: ['flat', 'anonymous', 'feed-generic'],
403
+ typographyScaleRatio: '1.200',
404
+ baseGridUnit: 8,
405
+ densityMode: 'reading-first',
406
+ colorIntent: 'Let typography and surface contrast lead while chroma supports hierarchy and key participation actions.',
357
407
  distinctiveMoves: [
358
408
  'Build a strong reading rhythm so content feels curated rather than dumped into cards.',
359
409
  'Use contrast and spacing to guide attention between creation, moderation, and discovery.',
360
410
  'Give key interaction moments personality without sacrificing clarity.',
361
411
  ],
412
+ mutationRules: {
413
+ mobile: 'Prioritize reading and contribution flows in a single-column narrative stack, tuck secondary discovery tools behind sheets, and keep primary creation actions within reach.',
414
+ tablet: 'Balance narrative reading with supporting discovery modules, using two-column compositions only where hierarchy stays obvious.',
415
+ desktop: 'Use wider editorial compositions, visible secondary navigation, and modular discovery surfaces without breaking reading rhythm.',
416
+ },
362
417
  };
363
418
  }
364
419
 
365
420
  return {
421
+ designPhilosophy: 'Project-specific clarity with one authored tension.',
366
422
  brandAdjectives: ['clear', 'human', 'distinct'],
367
423
  antiAdjectives: ['generic', 'template-like', 'trend-chasing'],
424
+ typographyScaleRatio: '1.200',
425
+ baseGridUnit: 8,
426
+ densityMode: 'balanced-authored',
427
+ colorIntent: 'Use a restrained perceptual palette with one deliberate accent budget instead of interchangeable template colors.',
368
428
  distinctiveMoves: [
369
429
  'Create a visual direction with one memorable tension instead of stacking fashionable effects.',
370
430
  'Use rhythm, hierarchy, and motion intentionally so the interface feels authored.',
371
431
  'Keep the system flexible enough to evolve with product scope without losing identity.',
372
432
  ],
433
+ mutationRules: {
434
+ mobile: 'Stack primary tasks vertically, convert secondary navigation into thumb-friendly overlays or sheets, and simplify dense comparison layouts into progressive disclosure.',
435
+ tablet: 'Preserve hierarchy with fewer columns, condensed chrome, and adaptive navigation that maintains task continuity.',
436
+ desktop: 'Expose the full layout system, highest information density, and broadest navigation affordances without sacrificing clarity.',
437
+ },
373
438
  };
374
439
  }
375
440
 
376
- export function buildDesignIntentSeedFromSignals({
441
+ function buildDesignIntentContractObject({
377
442
  projectName,
378
443
  projectDescription,
379
444
  primaryDomain,
@@ -390,7 +455,7 @@ export function buildDesignIntentSeedFromSignals({
390
455
  });
391
456
  const designSignals = architectureRecommendation?.designGuidance?.normalizedSignals || null;
392
457
 
393
- return `${JSON.stringify({
458
+ return {
394
459
  mode: 'dynamic',
395
460
  status,
396
461
  project: {
@@ -400,6 +465,7 @@ export function buildDesignIntentSeedFromSignals({
400
465
  stack: toTitleCase(initContext.stackFileName),
401
466
  blueprint: toTitleCase(initContext.blueprintFileName),
402
467
  },
468
+ designPhilosophy: inferredKeywords.designPhilosophy,
403
469
  brandAdjectives: inferredKeywords.brandAdjectives,
404
470
  antiAdjectives: inferredKeywords.antiAdjectives,
405
471
  visualDirection: {
@@ -407,41 +473,162 @@ export function buildDesignIntentSeedFromSignals({
407
473
  distinctiveMoves: inferredKeywords.distinctiveMoves,
408
474
  copiedReferenceAllowed: false,
409
475
  },
476
+ mathSystems: {
477
+ typographyScaleRatio: inferredKeywords.typographyScaleRatio,
478
+ baseGridUnit: inferredKeywords.baseGridUnit,
479
+ spacingPattern: designSignals?.spacingPattern || 'balanced-grid',
480
+ densityMode: inferredKeywords.densityMode,
481
+ },
482
+ colorTruth: {
483
+ format: 'OKLCH',
484
+ allowHexDerivatives: true,
485
+ requirePerceptualLightnessCurve: true,
486
+ paletteRoles: designSignals?.paletteRoles || ['base', 'surface', 'accent'],
487
+ intent: inferredKeywords.colorIntent,
488
+ },
489
+ crossViewportAdaptation: {
490
+ adaptByRecomposition: true,
491
+ touchTargetMinPx: 44,
492
+ mutationRules: inferredKeywords.mutationRules,
493
+ },
410
494
  experiencePrinciples: [
411
495
  'Design must feel project-specific, not interchangeable with generic SaaS templates.',
412
496
  'Major interface decisions must be explainable in product and user terms.',
413
497
  'Accessibility, responsiveness, and implementation realism are non-negotiable.',
498
+ 'Cross-viewport behavior must reorganize tasks and navigation, not just scale the desktop layout down.',
414
499
  ],
415
500
  forbiddenPatterns: [
416
501
  'generic-saas-hero',
417
502
  'copycat-brand-system',
418
503
  'unjustified-default-gradients',
419
504
  'placeholder-design-language',
505
+ 'scale-only-responsive-layout',
420
506
  ],
421
- requiredDesignSections: [
422
- 'Design Intent and Product Personality',
423
- 'Audience and Use-Context Signals',
424
- 'Visual Direction and Distinctive Moves',
425
- 'Color System and Semantic Roles',
426
- 'Typography System and Hierarchy',
427
- 'Spacing, Layout Rhythm, and Density Strategy',
428
- 'Interaction, Motion, and Feedback Rules',
429
- 'Component Language and Shared Patterns',
430
- 'Accessibility Non-Negotiables',
431
- 'Responsive Strategy',
432
- 'Anti-Patterns to Avoid',
433
- 'Implementation Notes for Future UI Tasks',
434
- ],
507
+ validationHints: {
508
+ rejectArbitraryHexOnlyPalette: true,
509
+ requireViewportMutationRules: true,
510
+ requirePerceptualColorRationale: true,
511
+ allowHexDerivatives: true,
512
+ },
513
+ requiredDesignSections: DESIGN_REQUIRED_SECTIONS,
435
514
  implementation: {
436
515
  requiredDeliverables: ['docs/DESIGN.md', 'docs/design-intent.json'],
437
516
  requireDesignRationale: true,
438
517
  requireDistinctVisualDirection: true,
439
518
  requireMachineReadableContract: true,
519
+ requireViewportMutationRules: true,
440
520
  bootstrapPrompt: '.agent-context/prompts/bootstrap-design.md',
521
+ autoLoadedRuleFiles: [
522
+ '.agent-context/prompts/bootstrap-design.md',
523
+ '.agent-context/rules/frontend-architecture.md',
524
+ ],
525
+ disallowedAutoLoadedRuleFiles: [
526
+ '.agent-context/rules/database-design.md',
527
+ '.agent-context/rules/docker-runtime.md',
528
+ '.agent-context/rules/microservices.md',
529
+ ],
441
530
  },
442
531
  architectSignals: designSignals,
443
532
  ...supplementalFields,
444
- }, null, 2)}\n`;
533
+ };
534
+ }
535
+
536
+ export function validateDesignIntentContract(designIntentContract) {
537
+ const validationErrors = [];
538
+
539
+ if (!designIntentContract || typeof designIntentContract !== 'object') {
540
+ return ['Design intent contract must be an object.'];
541
+ }
542
+
543
+ if (designIntentContract.mode !== 'dynamic') {
544
+ validationErrors.push('designIntent.mode must equal "dynamic".');
545
+ }
546
+
547
+ if (!designIntentContract.project || typeof designIntentContract.project !== 'object') {
548
+ validationErrors.push('designIntent.project must exist.');
549
+ }
550
+
551
+ if (!designIntentContract.designPhilosophy || typeof designIntentContract.designPhilosophy !== 'string') {
552
+ validationErrors.push('designIntent.designPhilosophy must be a non-empty string.');
553
+ }
554
+
555
+ if (!designIntentContract.mathSystems || typeof designIntentContract.mathSystems !== 'object') {
556
+ validationErrors.push('designIntent.mathSystems must exist.');
557
+ } else {
558
+ if (!/^\d+(\.\d+)?$/.test(String(designIntentContract.mathSystems.typographyScaleRatio || '').trim())) {
559
+ validationErrors.push('designIntent.mathSystems.typographyScaleRatio must be numeric text.');
560
+ }
561
+ if (!Number.isInteger(designIntentContract.mathSystems.baseGridUnit) || designIntentContract.mathSystems.baseGridUnit <= 0) {
562
+ validationErrors.push('designIntent.mathSystems.baseGridUnit must be a positive integer.');
563
+ }
564
+ }
565
+
566
+ if (!designIntentContract.colorTruth || typeof designIntentContract.colorTruth !== 'object') {
567
+ validationErrors.push('designIntent.colorTruth must exist.');
568
+ } else {
569
+ if (designIntentContract.colorTruth.format !== 'OKLCH') {
570
+ validationErrors.push('designIntent.colorTruth.format must equal "OKLCH".');
571
+ }
572
+ if (designIntentContract.colorTruth.allowHexDerivatives !== true) {
573
+ validationErrors.push('designIntent.colorTruth.allowHexDerivatives must equal true.');
574
+ }
575
+ }
576
+
577
+ if (!designIntentContract.crossViewportAdaptation || typeof designIntentContract.crossViewportAdaptation !== 'object') {
578
+ validationErrors.push('designIntent.crossViewportAdaptation must exist.');
579
+ } else {
580
+ const mutationRules = designIntentContract.crossViewportAdaptation.mutationRules;
581
+ if (!mutationRules || typeof mutationRules !== 'object') {
582
+ validationErrors.push('designIntent.crossViewportAdaptation.mutationRules must exist.');
583
+ } else {
584
+ for (const viewportKey of ['mobile', 'tablet', 'desktop']) {
585
+ if (!String(mutationRules[viewportKey] || '').trim()) {
586
+ validationErrors.push(`designIntent.crossViewportAdaptation.mutationRules.${viewportKey} must be a non-empty string.`);
587
+ }
588
+ }
589
+ }
590
+ }
591
+
592
+ if (!Array.isArray(designIntentContract.requiredDesignSections) || designIntentContract.requiredDesignSections.length !== DESIGN_REQUIRED_SECTIONS.length) {
593
+ validationErrors.push('designIntent.requiredDesignSections must match the required design contract sections.');
594
+ } else {
595
+ for (const requiredSectionName of DESIGN_REQUIRED_SECTIONS) {
596
+ if (!designIntentContract.requiredDesignSections.includes(requiredSectionName)) {
597
+ validationErrors.push(`designIntent.requiredDesignSections is missing "${requiredSectionName}".`);
598
+ }
599
+ }
600
+ }
601
+
602
+ return validationErrors;
603
+ }
604
+
605
+ export function buildDesignIntentSeedFromSignals({
606
+ projectName,
607
+ projectDescription,
608
+ primaryDomain,
609
+ features = [],
610
+ initContext,
611
+ architectureRecommendation = null,
612
+ status = 'seed-needs-design-synthesis',
613
+ supplementalFields = {},
614
+ }) {
615
+ const designIntentContract = buildDesignIntentContractObject({
616
+ projectName,
617
+ projectDescription,
618
+ primaryDomain,
619
+ features,
620
+ initContext,
621
+ architectureRecommendation,
622
+ status,
623
+ supplementalFields,
624
+ });
625
+ const validationErrors = validateDesignIntentContract(designIntentContract);
626
+
627
+ if (validationErrors.length > 0) {
628
+ throw new Error(`Invalid design intent contract seed: ${validationErrors.join(' ')}`);
629
+ }
630
+
631
+ return `${JSON.stringify(designIntentContract, null, 2)}\n`;
445
632
  }
446
633
 
447
634
  function buildDesignIntentSeed({
@@ -572,13 +759,13 @@ function buildDesignBootstrapPrompt({
572
759
  '1. Design Vision and Product Personality',
573
760
  '2. Audience and Use-Context Signals',
574
761
  '3. Visual Direction and Distinctive Moves',
575
- '4. Color System (tokens, semantic roles, accessibility rationale)',
576
- '5. Typography System (pairing, scale, usage rules)',
762
+ '4. Color Science and Semantic Roles',
763
+ '5. Typographic Engineering and Hierarchy',
577
764
  '6. Spacing, Layout Rhythm, and Density Strategy',
578
- '7. Motion and Interaction Principles',
579
- '8. Component Language (cards, forms, nav, states)',
580
- '9. Accessibility Non-Negotiables',
581
- '10. Responsive Strategy',
765
+ '7. Responsive Strategy and Cross-Viewport Adaptation Matrix',
766
+ '8. Motion and Interaction Principles',
767
+ '9. Component Language (cards, forms, nav, states)',
768
+ '10. Accessibility Non-Negotiables',
582
769
  '11. Anti-Patterns to Avoid',
583
770
  '12. Implementation Notes for Future UI Tasks',
584
771
  '',
@@ -586,13 +773,18 @@ function buildDesignBootstrapPrompt({
586
773
  '1. mode',
587
774
  '2. status',
588
775
  '3. project',
589
- '4. brandAdjectives',
590
- '5. antiAdjectives',
591
- '6. visualDirection',
592
- '7. experiencePrinciples',
593
- '8. forbiddenPatterns',
594
- '9. requiredDesignSections',
595
- '10. implementation',
776
+ '4. designPhilosophy',
777
+ '5. brandAdjectives',
778
+ '6. antiAdjectives',
779
+ '7. visualDirection',
780
+ '8. mathSystems',
781
+ '9. colorTruth',
782
+ '10. crossViewportAdaptation',
783
+ '11. experiencePrinciples',
784
+ '12. forbiddenPatterns',
785
+ '13. validationHints',
786
+ '14. requiredDesignSections',
787
+ '15. implementation',
596
788
  '',
597
789
  '## Hard Rules',
598
790
  '1. No copy-paste from external style guides.',
@@ -601,6 +793,9 @@ function buildDesignBootstrapPrompt({
601
793
  '4. Keep tone decisive like an art director, not generic AI boilerplate.',
602
794
  '5. Do not anchor the final design language to a famous brand reference. Translate inspiration into original project-specific principles.',
603
795
  '6. Reject interchangeable hero layouts, generic SaaS gradients, and trend-chasing decoration unless the project context explicitly justifies them.',
796
+ '7. Encode color intent in perceptual terms first. Hex values may exist only as implementation derivatives.',
797
+ '8. Responsive guidance must include layout mutation rules for mobile, tablet, and desktop. Shrinking the desktop layout is not enough.',
798
+ '9. Keep UI-only requests context-isolated. Load frontend design rules first and do not eagerly load backend-only rules unless the task explicitly crosses those boundaries.',
604
799
  '',
605
800
  '## Project Inputs',
606
801
  `- Project name: ${discoveryAnswers.projectName}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuenn3123/agentic-senior-core",
3
- "version": "3.0.9",
3
+ "version": "3.0.10",
4
4
  "type": "module",
5
5
  "description": "Force your AI Agent to code like a Staff Engineer, not a Junior.",
6
6
  "bin": {
@@ -43,6 +43,7 @@
43
43
  "scripts": {
44
44
  "init": "node ./bin/agentic-senior-core.js init",
45
45
  "audit:frontend-usability": "node ./scripts/frontend-usability-audit.mjs",
46
+ "audit:ui-design-judge": "node ./scripts/ui-design-judge.mjs",
46
47
  "audit:documentation-boundary": "node ./scripts/documentation-boundary-audit.mjs",
47
48
  "audit:context-triggered": "node ./scripts/context-triggered-audit.mjs",
48
49
  "audit:rules-guardian": "node ./scripts/rules-guardian-audit.mjs",
@@ -20,6 +20,9 @@ const REQUIRED_FILES = [
20
20
  'docs/roadmap.md',
21
21
  'docs/v1.7-issue-breakdown.md',
22
22
  'docs/v1.7-execution-playbook.md',
23
+ '.instructions.md',
24
+ '.agent-context/prompts/bootstrap-design.md',
25
+ 'scripts/ui-design-judge.mjs',
23
26
  '.agent-context/rules/frontend-architecture.md',
24
27
  '.agent-context/review-checklists/pr-checklist.md',
25
28
  '.agent-context/review-checklists/architecture-review.md',
@@ -53,6 +56,28 @@ const REQUIRED_FRONTEND_RULE_SNIPPETS = [
53
56
  'UI Consistency Guardrails (Mandatory)',
54
57
  'Content language must stay consistent per screen and flow unless user requests multilingual output.',
55
58
  'Text color must remain contrast-safe against its background; no color collisions.',
59
+ 'Responsive quality requires layout mutation and task reprioritization across breakpoints. Shrinking the desktop layout is not enough.',
60
+ ];
61
+
62
+ const REQUIRED_BOOTSTRAP_DESIGN_SNIPPETS = [
63
+ 'UI Design Mode is context-isolated by default:',
64
+ 'Responsive Strategy and Cross-Viewport Adaptation Matrix',
65
+ 'colorTruth.format',
66
+ 'crossViewportAdaptation.mutationRules.mobile/tablet/desktop',
67
+ ];
68
+
69
+ const REQUIRED_UI_DESIGN_JUDGE_SNIPPETS = [
70
+ 'Advisory-first UI design contract judge.',
71
+ 'Default mode is advisory: findings never block release. Strict mode is opt-in.',
72
+ 'Do not reward generic SaaS defaults or popular template patterns.',
73
+ 'UI design judge only evaluates changed UI surfaces.',
74
+ ];
75
+
76
+ const REQUIRED_INSTRUCTIONS_SNIPPETS = [
77
+ 'UI Design Mode',
78
+ 'bootstrap-design.md',
79
+ 'frontend-architecture.md',
80
+ 'do not eagerly load unrelated backend-only rules',
56
81
  ];
57
82
 
58
83
  function assertFileExists(relativeFilePath, failures) {
@@ -79,6 +104,8 @@ function runAudit() {
79
104
 
80
105
  const roadmapPath = 'docs/roadmap.md';
81
106
  const frontendRulePath = '.agent-context/rules/frontend-architecture.md';
107
+ const bootstrapDesignPromptPath = '.agent-context/prompts/bootstrap-design.md';
108
+ const instructionsPath = '.instructions.md';
82
109
  const prChecklistPath = '.agent-context/review-checklists/pr-checklist.md';
83
110
  const architectureChecklistPath = '.agent-context/review-checklists/architecture-review.md';
84
111
 
@@ -97,6 +124,33 @@ function runAudit() {
97
124
  assertContains('Frontend rule', frontendRulePath, frontendRuleContent, REQUIRED_FRONTEND_RULE_SNIPPETS, failures);
98
125
  }
99
126
 
127
+ if (existsSync(resolve(REPOSITORY_ROOT, bootstrapDesignPromptPath))) {
128
+ const bootstrapDesignPromptContent = readFileSync(resolve(REPOSITORY_ROOT, bootstrapDesignPromptPath), 'utf8');
129
+ assertContains(
130
+ 'Bootstrap design prompt',
131
+ bootstrapDesignPromptPath,
132
+ bootstrapDesignPromptContent,
133
+ REQUIRED_BOOTSTRAP_DESIGN_SNIPPETS,
134
+ failures
135
+ );
136
+ }
137
+
138
+ if (existsSync(resolve(REPOSITORY_ROOT, 'scripts/ui-design-judge.mjs'))) {
139
+ const uiDesignJudgeContent = readFileSync(resolve(REPOSITORY_ROOT, 'scripts/ui-design-judge.mjs'), 'utf8');
140
+ assertContains(
141
+ 'UI design judge',
142
+ 'scripts/ui-design-judge.mjs',
143
+ uiDesignJudgeContent,
144
+ REQUIRED_UI_DESIGN_JUDGE_SNIPPETS,
145
+ failures
146
+ );
147
+ }
148
+
149
+ if (existsSync(resolve(REPOSITORY_ROOT, instructionsPath))) {
150
+ const instructionsContent = readFileSync(resolve(REPOSITORY_ROOT, instructionsPath), 'utf8');
151
+ assertContains('Instructions', instructionsPath, instructionsContent, REQUIRED_INSTRUCTIONS_SNIPPETS, failures);
152
+ }
153
+
100
154
  if (existsSync(resolve(REPOSITORY_ROOT, architectureChecklistPath))) {
101
155
  const excellenceRubricContent = readFileSync(resolve(REPOSITORY_ROOT, architectureChecklistPath), 'utf8');
102
156
  assertContains(
@@ -18,6 +18,7 @@ const REPOSITORY_ROOT = resolve(__dirname, '..');
18
18
 
19
19
  const VERSION_PATTERN = /^\d+\.\d+\.\d+$/;
20
20
  const FRONTEND_AUDIT_SCRIPT_PATH = 'scripts/frontend-usability-audit.mjs';
21
+ const UI_DESIGN_JUDGE_SCRIPT_PATH = 'scripts/ui-design-judge.mjs';
21
22
  const DOCUMENTATION_BOUNDARY_AUDIT_SCRIPT_PATH = 'scripts/documentation-boundary-audit.mjs';
22
23
  const CONTEXT_TRIGGERED_AUDIT_SCRIPT_PATH = 'scripts/context-triggered-audit.mjs';
23
24
  const RULES_GUARDIAN_AUDIT_SCRIPT_PATH = 'scripts/rules-guardian-audit.mjs';
@@ -716,6 +717,38 @@ function runReleaseGate() {
716
717
  pushResult(results, false, 'frontend-usability-audit', `Failed to execute frontend usability audit: ${frontendAuditErrorMessage}`);
717
718
  }
718
719
 
720
+ const uiDesignJudgeExecution = runMachineReadableScript(UI_DESIGN_JUDGE_SCRIPT_PATH);
721
+ if (!uiDesignJudgeExecution.report) {
722
+ const failureDetails = uiDesignJudgeExecution.executionErrorMessage
723
+ ? `UI design judge execution failed before producing a machine-readable report: ${uiDesignJudgeExecution.executionErrorMessage}`
724
+ : 'UI design judge did not produce machine-readable JSON output';
725
+ pushResult(results, false, 'ui-design-judge-advisory', failureDetails);
726
+ } else {
727
+ diagnostics.uiDesignJudge = uiDesignJudgeExecution.report;
728
+ pushResult(
729
+ results,
730
+ true,
731
+ 'ui-design-judge-advisory',
732
+ `ui-design-judge executed (passed=${uiDesignJudgeExecution.report.passed}, skipped=${uiDesignJudgeExecution.report.skipped}, mode=${uiDesignJudgeExecution.report.mode})`
733
+ );
734
+
735
+ if (uiDesignJudgeExecution.report.advisoryOnly === true) {
736
+ pushResult(
737
+ results,
738
+ true,
739
+ 'ui-design-judge-non-blocking-policy',
740
+ 'UI design judge remains advisory by default and does not hard-block release gate'
741
+ );
742
+ } else {
743
+ pushResult(
744
+ results,
745
+ false,
746
+ 'ui-design-judge-non-blocking-policy',
747
+ 'UI design judge unexpectedly ran in blocking mode during release gate'
748
+ );
749
+ }
750
+ }
751
+
719
752
  const benchmarkGateExecution = runMachineReadableScript(BENCHMARK_GATE_SCRIPT_PATH);
720
753
  if (!benchmarkGateExecution.report) {
721
754
  const failureDetails = benchmarkGateExecution.executionErrorMessage