agentic-qe 2.0.0 → 2.1.0

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.
Files changed (116) hide show
  1. package/.claude/agents/qx-partner.md +17 -4
  2. package/.claude/skills/accessibility-testing/SKILL.md +144 -692
  3. package/.claude/skills/agentic-quality-engineering/SKILL.md +176 -529
  4. package/.claude/skills/api-testing-patterns/SKILL.md +180 -560
  5. package/.claude/skills/brutal-honesty-review/SKILL.md +113 -603
  6. package/.claude/skills/bug-reporting-excellence/SKILL.md +116 -517
  7. package/.claude/skills/chaos-engineering-resilience/SKILL.md +127 -72
  8. package/.claude/skills/cicd-pipeline-qe-orchestrator/SKILL.md +209 -404
  9. package/.claude/skills/code-review-quality/SKILL.md +158 -608
  10. package/.claude/skills/compatibility-testing/SKILL.md +148 -38
  11. package/.claude/skills/compliance-testing/SKILL.md +132 -63
  12. package/.claude/skills/consultancy-practices/SKILL.md +114 -446
  13. package/.claude/skills/context-driven-testing/SKILL.md +117 -381
  14. package/.claude/skills/contract-testing/SKILL.md +176 -141
  15. package/.claude/skills/database-testing/SKILL.md +137 -130
  16. package/.claude/skills/exploratory-testing-advanced/SKILL.md +160 -629
  17. package/.claude/skills/holistic-testing-pact/SKILL.md +140 -188
  18. package/.claude/skills/localization-testing/SKILL.md +145 -33
  19. package/.claude/skills/mobile-testing/SKILL.md +132 -448
  20. package/.claude/skills/mutation-testing/SKILL.md +147 -41
  21. package/.claude/skills/performance-testing/SKILL.md +200 -546
  22. package/.claude/skills/quality-metrics/SKILL.md +164 -519
  23. package/.claude/skills/refactoring-patterns/SKILL.md +132 -699
  24. package/.claude/skills/regression-testing/SKILL.md +120 -926
  25. package/.claude/skills/risk-based-testing/SKILL.md +157 -660
  26. package/.claude/skills/security-testing/SKILL.md +199 -538
  27. package/.claude/skills/sherlock-review/SKILL.md +163 -699
  28. package/.claude/skills/shift-left-testing/SKILL.md +161 -465
  29. package/.claude/skills/shift-right-testing/SKILL.md +161 -519
  30. package/.claude/skills/six-thinking-hats/SKILL.md +175 -1110
  31. package/.claude/skills/skills-manifest.json +71 -20
  32. package/.claude/skills/tdd-london-chicago/SKILL.md +131 -448
  33. package/.claude/skills/technical-writing/SKILL.md +103 -154
  34. package/.claude/skills/test-automation-strategy/SKILL.md +166 -772
  35. package/.claude/skills/test-data-management/SKILL.md +126 -910
  36. package/.claude/skills/test-design-techniques/SKILL.md +179 -89
  37. package/.claude/skills/test-environment-management/SKILL.md +136 -91
  38. package/.claude/skills/test-reporting-analytics/SKILL.md +169 -92
  39. package/.claude/skills/testability-scoring/SKILL.md +172 -538
  40. package/.claude/skills/testability-scoring/scripts/generate-html-report.js +0 -0
  41. package/.claude/skills/visual-testing-advanced/SKILL.md +155 -78
  42. package/.claude/skills/xp-practices/SKILL.md +151 -587
  43. package/CHANGELOG.md +48 -0
  44. package/README.md +23 -16
  45. package/dist/agents/QXPartnerAgent.d.ts +8 -1
  46. package/dist/agents/QXPartnerAgent.d.ts.map +1 -1
  47. package/dist/agents/QXPartnerAgent.js +1174 -112
  48. package/dist/agents/QXPartnerAgent.js.map +1 -1
  49. package/dist/agents/lifecycle/AgentLifecycleManager.d.ts.map +1 -1
  50. package/dist/agents/lifecycle/AgentLifecycleManager.js +34 -31
  51. package/dist/agents/lifecycle/AgentLifecycleManager.js.map +1 -1
  52. package/dist/cli/commands/init-claude-md-template.d.ts.map +1 -1
  53. package/dist/cli/commands/init-claude-md-template.js +14 -0
  54. package/dist/cli/commands/init-claude-md-template.js.map +1 -1
  55. package/dist/core/SwarmCoordinator.d.ts +180 -0
  56. package/dist/core/SwarmCoordinator.d.ts.map +1 -0
  57. package/dist/core/SwarmCoordinator.js +473 -0
  58. package/dist/core/SwarmCoordinator.js.map +1 -0
  59. package/dist/core/metrics/MetricsAggregator.d.ts +228 -0
  60. package/dist/core/metrics/MetricsAggregator.d.ts.map +1 -0
  61. package/dist/core/metrics/MetricsAggregator.js +482 -0
  62. package/dist/core/metrics/MetricsAggregator.js.map +1 -0
  63. package/dist/core/metrics/index.d.ts +5 -0
  64. package/dist/core/metrics/index.d.ts.map +1 -0
  65. package/dist/core/metrics/index.js +11 -0
  66. package/dist/core/metrics/index.js.map +1 -0
  67. package/dist/core/optimization/SwarmOptimizer.d.ts +5 -0
  68. package/dist/core/optimization/SwarmOptimizer.d.ts.map +1 -1
  69. package/dist/core/optimization/SwarmOptimizer.js +17 -0
  70. package/dist/core/optimization/SwarmOptimizer.js.map +1 -1
  71. package/dist/core/orchestration/AdaptiveScheduler.d.ts +190 -0
  72. package/dist/core/orchestration/AdaptiveScheduler.d.ts.map +1 -0
  73. package/dist/core/orchestration/AdaptiveScheduler.js +460 -0
  74. package/dist/core/orchestration/AdaptiveScheduler.js.map +1 -0
  75. package/dist/core/orchestration/WorkflowOrchestrator.d.ts +13 -0
  76. package/dist/core/orchestration/WorkflowOrchestrator.d.ts.map +1 -1
  77. package/dist/core/orchestration/WorkflowOrchestrator.js +32 -0
  78. package/dist/core/orchestration/WorkflowOrchestrator.js.map +1 -1
  79. package/dist/core/recovery/CircuitBreaker.d.ts +176 -0
  80. package/dist/core/recovery/CircuitBreaker.d.ts.map +1 -0
  81. package/dist/core/recovery/CircuitBreaker.js +382 -0
  82. package/dist/core/recovery/CircuitBreaker.js.map +1 -0
  83. package/dist/core/recovery/RecoveryOrchestrator.d.ts +186 -0
  84. package/dist/core/recovery/RecoveryOrchestrator.d.ts.map +1 -0
  85. package/dist/core/recovery/RecoveryOrchestrator.js +476 -0
  86. package/dist/core/recovery/RecoveryOrchestrator.js.map +1 -0
  87. package/dist/core/recovery/RetryStrategy.d.ts +127 -0
  88. package/dist/core/recovery/RetryStrategy.d.ts.map +1 -0
  89. package/dist/core/recovery/RetryStrategy.js +314 -0
  90. package/dist/core/recovery/RetryStrategy.js.map +1 -0
  91. package/dist/core/recovery/index.d.ts +8 -0
  92. package/dist/core/recovery/index.d.ts.map +1 -0
  93. package/dist/core/recovery/index.js +27 -0
  94. package/dist/core/recovery/index.js.map +1 -0
  95. package/dist/core/skills/DependencyResolver.d.ts +99 -0
  96. package/dist/core/skills/DependencyResolver.d.ts.map +1 -0
  97. package/dist/core/skills/DependencyResolver.js +260 -0
  98. package/dist/core/skills/DependencyResolver.js.map +1 -0
  99. package/dist/core/skills/ManifestGenerator.d.ts +114 -0
  100. package/dist/core/skills/ManifestGenerator.d.ts.map +1 -0
  101. package/dist/core/skills/ManifestGenerator.js +449 -0
  102. package/dist/core/skills/ManifestGenerator.js.map +1 -0
  103. package/dist/core/skills/index.d.ts +9 -0
  104. package/dist/core/skills/index.d.ts.map +1 -0
  105. package/dist/core/skills/index.js +24 -0
  106. package/dist/core/skills/index.js.map +1 -0
  107. package/dist/mcp/server.d.ts +9 -9
  108. package/dist/mcp/server.d.ts.map +1 -1
  109. package/dist/mcp/server.js +1 -2
  110. package/dist/mcp/server.js.map +1 -1
  111. package/dist/types/qx.d.ts +39 -7
  112. package/dist/types/qx.d.ts.map +1 -1
  113. package/dist/types/qx.js.map +1 -1
  114. package/dist/visualization/api/RestEndpoints.js +1 -1
  115. package/dist/visualization/api/RestEndpoints.js.map +1 -1
  116. package/package.json +13 -55
@@ -20,6 +20,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
20
20
  exports.QXPartnerAgent = void 0;
21
21
  const BaseAgent_1 = require("./BaseAgent");
22
22
  const types_1 = require("../types");
23
+ const playwright_1 = require("playwright");
23
24
  const qx_1 = require("../types/qx");
24
25
  class ConsoleLogger {
25
26
  info(message, ...args) {
@@ -47,6 +48,8 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
47
48
  };
48
49
  super(baseConfig);
49
50
  this.logger = new ConsoleLogger();
51
+ this.browser = null;
52
+ this.page = null;
50
53
  this.config = {
51
54
  analysisMode: config.analysisMode || 'full',
52
55
  heuristics: config.heuristics || {
@@ -192,6 +195,15 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
192
195
  async cleanup() {
193
196
  try {
194
197
  this.logger.info(`QXPartnerAgent ${this.agentId.id} cleaning up resources`);
198
+ // Close browser if open
199
+ if (this.page) {
200
+ await this.page.close();
201
+ this.page = null;
202
+ }
203
+ if (this.browser) {
204
+ await this.browser.close();
205
+ this.browser = null;
206
+ }
195
207
  // Save current QX analysis state
196
208
  await this.saveQXState();
197
209
  // Store learned patterns
@@ -291,63 +303,577 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
291
303
  return analysis;
292
304
  }
293
305
  /**
294
- * Collect QX context from target
306
+ * Collect QX context from target using Playwright
295
307
  */
296
308
  async collectQXContext(target, additionalContext) {
297
309
  this.logger.debug(`Collecting QX context for: ${target}`);
298
- // In a real implementation, this would use Playwright or similar to analyze the target
299
- // For now, we'll create a structure that can be filled by external tools
300
- const context = {
301
- url: target,
302
- custom: additionalContext || {}
303
- };
304
- // Store context for later retrieval
305
- await this.storeMemory(`qx-context:${target}`, context);
306
- return context;
310
+ try {
311
+ // Launch browser if not already running
312
+ if (!this.browser) {
313
+ this.logger.debug('Launching browser...');
314
+ this.browser = await playwright_1.chromium.launch({
315
+ headless: true,
316
+ timeout: 30000, // 30 second timeout for launch
317
+ args: [
318
+ '--no-sandbox',
319
+ '--disable-setuid-sandbox',
320
+ '--disable-dev-shm-usage',
321
+ '--disable-accelerated-2d-canvas',
322
+ '--no-first-run',
323
+ '--no-zygote',
324
+ '--single-process', // Important for containers
325
+ '--disable-gpu'
326
+ ]
327
+ });
328
+ }
329
+ // Create new page
330
+ this.page = await this.browser.newPage();
331
+ this.logger.debug(`Navigating to ${target}...`);
332
+ // Navigate to target with timeout - try quick load first
333
+ try {
334
+ await this.page.goto(target, { waitUntil: 'commit', timeout: 15000 });
335
+ }
336
+ catch (navError) {
337
+ this.logger.warn(`Quick navigation failed, trying basic load: ${navError}`);
338
+ // Fallback: just navigate without waiting
339
+ await this.page.goto(target, { waitUntil: 'commit', timeout: 10000 });
340
+ }
341
+ // Wait a bit for some content to load
342
+ await this.page.waitForTimeout(1000);
343
+ this.logger.debug('Extracting page context...');
344
+ // Extract page context WITH ACTUAL CONTENT for contextual analysis
345
+ const pageContext = await this.page.evaluate(() => {
346
+ const countElements = (selector) => document.querySelectorAll(selector).length;
347
+ const getText = (selector, limit = 5) => Array.from(document.querySelectorAll(selector)).slice(0, limit).map(el => el.textContent?.trim() || '').filter(t => t.length > 0);
348
+ // Extract navigation items for context understanding
349
+ const navItems = getText('nav a, nav button, [role="navigation"] a');
350
+ const headings = {
351
+ h1: getText('h1', 3),
352
+ h2: getText('h2', 5),
353
+ h3: getText('h3', 5)
354
+ };
355
+ // Extract form purposes from labels/placeholders
356
+ const formPurposes = Array.from(document.querySelectorAll('form')).map(form => {
357
+ const labels = Array.from(form.querySelectorAll('label, input[placeholder]')).slice(0, 3);
358
+ return labels.map(el => el.placeholder || el.textContent?.trim() || '').filter(t => t.length > 0).join(', ');
359
+ });
360
+ // Extract button purposes
361
+ const buttonPurposes = getText('button, [role="button"], input[type="submit"]', 10);
362
+ // Extract link context (first 20 meaningful links)
363
+ const linkTexts = getText('a[href]:not([href="#"]):not([href=""])', 20);
364
+ // Extract main content snippets for purpose understanding
365
+ const mainContent = document.querySelector('main, article, [role="main"]');
366
+ const contentSnippet = mainContent?.textContent?.trim().substring(0, 300) || '';
367
+ return {
368
+ title: document.title,
369
+ url: window.location.href,
370
+ viewport: {
371
+ width: window.innerWidth,
372
+ height: window.innerHeight
373
+ },
374
+ content: {
375
+ headings,
376
+ navigationItems: navItems,
377
+ buttonPurposes,
378
+ formPurposes,
379
+ linkTexts,
380
+ mainContentSnippet: contentSnippet
381
+ },
382
+ elements: {
383
+ total: document.querySelectorAll('*').length,
384
+ buttons: countElements('button, [role="button"], input[type="button"], input[type="submit"]'),
385
+ forms: countElements('form'),
386
+ inputs: countElements('input, textarea, select'),
387
+ links: countElements('a'),
388
+ headings: {
389
+ h1: countElements('h1'),
390
+ h2: countElements('h2'),
391
+ h3: countElements('h3')
392
+ },
393
+ images: countElements('img'),
394
+ videos: countElements('video'),
395
+ iframes: countElements('iframe')
396
+ },
397
+ semantic: {
398
+ hasNav: countElements('nav') > 0,
399
+ hasHeader: countElements('header') > 0,
400
+ hasFooter: countElements('footer') > 0,
401
+ hasMain: countElements('main') > 0,
402
+ hasAside: countElements('aside') > 0,
403
+ hasArticle: countElements('article') > 0,
404
+ hasSection: countElements('section') > 0
405
+ },
406
+ accessibility: {
407
+ ariaLabels: countElements('[aria-label]'),
408
+ ariaDescriptions: countElements('[aria-describedby]'),
409
+ altTexts: Array.from(document.querySelectorAll('img')).filter(img => img.hasAttribute('alt')).length,
410
+ totalImages: countElements('img'),
411
+ landmarkRoles: countElements('[role="banner"], [role="navigation"], [role="main"], [role="complementary"], [role="contentinfo"]'),
412
+ focusableElements: countElements('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])')
413
+ },
414
+ errors: {
415
+ consoleErrors: window.__errors || [],
416
+ hasErrorMessages: countElements('.error, [role="alert"], .alert-danger, .text-danger') > 0
417
+ },
418
+ meta: {
419
+ description: document.querySelector('meta[name="description"]')?.content || '',
420
+ keywords: document.querySelector('meta[name="keywords"]')?.content || '',
421
+ viewport: document.querySelector('meta[name="viewport"]')?.content || ''
422
+ }
423
+ };
424
+ });
425
+ // Capture performance metrics
426
+ const performanceMetrics = await this.page.evaluate(() => {
427
+ const perf = performance.getEntriesByType('navigation')[0];
428
+ return {
429
+ loadTime: perf?.loadEventEnd - perf?.fetchStart || 0,
430
+ domReady: perf?.domContentLoadedEventEnd - perf?.fetchStart || 0,
431
+ firstPaint: performance.getEntriesByType('paint').find(e => e.name === 'first-paint')?.startTime || 0
432
+ };
433
+ });
434
+ const context = {
435
+ url: target,
436
+ title: pageContext.title,
437
+ domMetrics: {
438
+ totalElements: pageContext.elements.total,
439
+ interactiveElements: pageContext.elements.buttons + pageContext.elements.inputs + pageContext.elements.links,
440
+ forms: pageContext.elements.forms,
441
+ inputs: pageContext.elements.inputs,
442
+ buttons: pageContext.elements.buttons,
443
+ semanticStructure: pageContext.semantic
444
+ },
445
+ accessibility: {
446
+ ariaLabelsCount: pageContext.accessibility.ariaLabels,
447
+ altTextsCoverage: pageContext.accessibility.totalImages > 0
448
+ ? (pageContext.accessibility.altTexts / pageContext.accessibility.totalImages) * 100
449
+ : 100,
450
+ focusableElementsCount: pageContext.accessibility.focusableElements,
451
+ landmarkRoles: pageContext.accessibility.landmarkRoles
452
+ },
453
+ performance: performanceMetrics,
454
+ errorIndicators: pageContext.errors,
455
+ metadata: pageContext.meta,
456
+ custom: additionalContext || {}
457
+ };
458
+ // Close page but keep browser for potential reuse
459
+ await this.page.close();
460
+ this.page = null;
461
+ // Store context for later retrieval
462
+ await this.storeMemory(`qx-context:${target}`, context);
463
+ this.logger.debug('Context collection completed successfully');
464
+ return context;
465
+ }
466
+ catch (error) {
467
+ this.logger.error(`Failed to collect QX context: ${error}`);
468
+ // Clean up on error
469
+ if (this.page) {
470
+ try {
471
+ await this.page.close();
472
+ }
473
+ catch (e) {
474
+ // Ignore close errors
475
+ }
476
+ this.page = null;
477
+ }
478
+ // Return minimal context on error
479
+ return {
480
+ url: target,
481
+ custom: additionalContext || {},
482
+ error: error instanceof Error ? error.message : String(error)
483
+ };
484
+ }
485
+ }
486
+ /**
487
+ * Detect domain-specific failure modes based on site type
488
+ */
489
+ detectDomainSpecificFailures(context, title, description, complexity) {
490
+ const failures = [];
491
+ const titleLower = title.toLowerCase();
492
+ const descLower = description.toLowerCase();
493
+ const forms = context.domMetrics?.forms || 0;
494
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
495
+ // E-commerce / Travel Booking sites
496
+ if (titleLower.includes('hotel') || titleLower.includes('booking') || titleLower.includes('travel') ||
497
+ titleLower.includes('shop') || titleLower.includes('store') || titleLower.includes('buy') ||
498
+ descLower.includes('book') || descLower.includes('reservation') || descLower.includes('hotel')) {
499
+ failures.push({
500
+ description: 'Search and filter complexity may overwhelm users with too many options',
501
+ severity: 'medium',
502
+ likelihood: 'likely'
503
+ });
504
+ failures.push({
505
+ description: 'Booking/checkout flow friction points may cause cart abandonment',
506
+ severity: 'high',
507
+ likelihood: 'likely'
508
+ });
509
+ failures.push({
510
+ description: 'Price transparency issues or hidden fees may erode user trust',
511
+ severity: 'high',
512
+ likelihood: 'possible'
513
+ });
514
+ if (complexity === 'complex') {
515
+ failures.push({
516
+ description: 'Multi-step booking process may lose users if progress is not clearly indicated',
517
+ severity: 'medium',
518
+ likelihood: 'likely'
519
+ });
520
+ }
521
+ }
522
+ // Content/Blog/Magazine sites
523
+ else if (titleLower.includes('blog') || titleLower.includes('article') || titleLower.includes('news') ||
524
+ titleLower.includes('magazine') || titleLower.includes('testers') || titleLower.includes('testing')) {
525
+ failures.push({
526
+ description: 'Content discoverability - users may struggle to find relevant articles without robust search',
527
+ severity: 'medium',
528
+ likelihood: 'likely'
529
+ });
530
+ failures.push({
531
+ description: 'Reading experience on mobile devices may not be optimized for long-form content',
532
+ severity: 'medium',
533
+ likelihood: 'possible'
534
+ });
535
+ failures.push({
536
+ description: 'Archive navigation complexity may overwhelm readers looking for specific topics',
537
+ severity: 'low',
538
+ likelihood: 'possible'
539
+ });
540
+ }
541
+ // SaaS / Web Applications
542
+ else if (titleLower.includes('dashboard') || titleLower.includes('app') || titleLower.includes('platform') ||
543
+ interactiveElements > 50) {
544
+ failures.push({
545
+ description: 'Complex workflows may confuse new users without proper onboarding',
546
+ severity: 'medium',
547
+ likelihood: 'likely'
548
+ });
549
+ failures.push({
550
+ description: 'Data visualization and information density may cause cognitive overload',
551
+ severity: 'medium',
552
+ likelihood: 'possible'
553
+ });
554
+ failures.push({
555
+ description: 'Error messages may not provide actionable recovery steps',
556
+ severity: 'medium',
557
+ likelihood: 'likely'
558
+ });
559
+ }
560
+ // Form-heavy sites
561
+ else if (forms > 0) {
562
+ failures.push({
563
+ description: 'Form validation errors may not be clearly communicated to users',
564
+ severity: 'medium',
565
+ likelihood: 'likely'
566
+ });
567
+ failures.push({
568
+ description: 'Required field indicators may not be consistently applied',
569
+ severity: 'low',
570
+ likelihood: 'possible'
571
+ });
572
+ failures.push({
573
+ description: 'Form submission failure recovery path may not be clear',
574
+ severity: 'medium',
575
+ likelihood: 'possible'
576
+ });
577
+ }
578
+ // Return only the most relevant failures (max 5)
579
+ return failures.slice(0, 5);
307
580
  }
308
581
  /**
309
582
  * Analyze problem using Rule of Three and complexity assessment
310
583
  */
311
- async analyzeProblem(_context) {
584
+ async analyzeProblem(context) {
312
585
  this.logger.debug('Analyzing problem');
313
- // In real implementation, this would use NLP and pattern matching
314
- // For now, return a structure that can be populated by external analysis
315
- const analysis = {
316
- problemStatement: 'Problem analysis pending',
317
- complexity: 'moderate',
318
- breakdown: [],
319
- potentialFailures: [],
320
- clarityScore: 50
586
+ const title = context.title || 'Untitled page';
587
+ const description = context.metadata?.description || '';
588
+ const hasError = context.errorIndicators?.hasErrorMessages || false;
589
+ let problemStatement = `Evaluate quality experience of "${title}"`;
590
+ if (description) {
591
+ problemStatement += ` - ${description.substring(0, 100)}`;
592
+ }
593
+ const totalElements = context.domMetrics?.totalElements || 0;
594
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
595
+ const forms = context.domMetrics?.forms || 0;
596
+ let complexity;
597
+ if (totalElements > 500 || interactiveElements > 50 || forms > 3) {
598
+ complexity = 'complex';
599
+ }
600
+ else if (totalElements > 200 || interactiveElements > 20 || forms > 1) {
601
+ complexity = 'moderate';
602
+ }
603
+ else {
604
+ complexity = 'simple';
605
+ }
606
+ const breakdown = [];
607
+ if (context.domMetrics?.semanticStructure?.hasNav)
608
+ breakdown.push('Navigation structure');
609
+ if (forms > 0)
610
+ breakdown.push(`Form interactions (${forms} forms)`);
611
+ if (interactiveElements > 0)
612
+ breakdown.push(`User interactions (${interactiveElements} elements)`);
613
+ if (context.accessibility)
614
+ breakdown.push('Accessibility compliance');
615
+ if (context.performance)
616
+ breakdown.push('Performance metrics');
617
+ const potentialFailures = [];
618
+ if (!context.domMetrics?.semanticStructure?.hasMain) {
619
+ potentialFailures.push({
620
+ description: 'Missing main content landmark - users may struggle to find primary content',
621
+ severity: 'medium',
622
+ likelihood: 'likely'
623
+ });
624
+ }
625
+ if (context.accessibility && (context.accessibility.altTextsCoverage || 0) < 80) {
626
+ potentialFailures.push({
627
+ description: 'Poor image alt text coverage - screen reader users affected',
628
+ severity: 'high',
629
+ likelihood: 'very-likely'
630
+ });
631
+ }
632
+ if (hasError) {
633
+ potentialFailures.push({
634
+ description: 'Visible error messages detected - potential usability issues',
635
+ severity: 'medium',
636
+ likelihood: 'likely'
637
+ });
638
+ }
639
+ if (context.performance && (context.performance.loadTime || 0) > 3000) {
640
+ potentialFailures.push({
641
+ description: 'Slow load time - user frustration and abandonment risk',
642
+ severity: 'high',
643
+ likelihood: 'very-likely'
644
+ });
645
+ }
646
+ if (!context.metadata?.viewport) {
647
+ potentialFailures.push({
648
+ description: 'Missing viewport meta tag - mobile responsiveness issues',
649
+ severity: 'medium',
650
+ likelihood: 'possible'
651
+ });
652
+ }
653
+ // ENHANCED: Add domain-specific failure modes based on site type and context
654
+ const domainFailures = this.detectDomainSpecificFailures(context, title, description, complexity);
655
+ potentialFailures.push(...domainFailures);
656
+ // Rule of Three: Ensure at least 3 failure modes are identified
657
+ if (potentialFailures.length < 3) {
658
+ // Add generic contextual failures for complex sites
659
+ if (complexity === 'complex') {
660
+ if (potentialFailures.length < 3) {
661
+ potentialFailures.push({
662
+ description: 'Complex interaction flows may confuse first-time users',
663
+ severity: 'medium',
664
+ likelihood: 'possible'
665
+ });
666
+ }
667
+ if (potentialFailures.length < 3) {
668
+ potentialFailures.push({
669
+ description: 'Multiple interactive elements increase cognitive load',
670
+ severity: 'low',
671
+ likelihood: 'possible'
672
+ });
673
+ }
674
+ if (potentialFailures.length < 3) {
675
+ potentialFailures.push({
676
+ description: 'Error recovery paths may not be clear in complex workflows',
677
+ severity: 'medium',
678
+ likelihood: 'possible'
679
+ });
680
+ }
681
+ }
682
+ }
683
+ let clarityScore = 50;
684
+ if (title && title !== 'Untitled page')
685
+ clarityScore += 15;
686
+ if (description)
687
+ clarityScore += 15;
688
+ if (breakdown.length >= 3)
689
+ clarityScore += 10;
690
+ if (context.domMetrics?.semanticStructure?.hasMain)
691
+ clarityScore += 10;
692
+ clarityScore = Math.min(100, clarityScore);
693
+ return {
694
+ problemStatement,
695
+ complexity,
696
+ breakdown,
697
+ potentialFailures,
698
+ clarityScore
321
699
  };
322
- return analysis;
323
700
  }
324
701
  /**
325
702
  * Analyze user needs
326
703
  */
327
- async analyzeUserNeeds(_context, _problemAnalysis) {
704
+ async analyzeUserNeeds(context, problemAnalysis) {
328
705
  this.logger.debug('Analyzing user needs');
329
- const analysis = {
330
- needs: [],
331
- suitability: 'adequate',
332
- challenges: [],
333
- alignmentScore: 70
706
+ const needs = [];
707
+ const challenges = [];
708
+ const semantic = context.domMetrics?.semanticStructure;
709
+ const accessibility = context.accessibility;
710
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
711
+ const forms = context.domMetrics?.forms || 0;
712
+ // Must-have features (critical for basic functionality)
713
+ if (semantic?.hasNav) {
714
+ needs.push({ description: 'Clear navigation to find content', priority: 'must-have', addressed: true });
715
+ }
716
+ else {
717
+ challenges.push('Missing navigation structure - users cannot easily explore site');
718
+ needs.push({ description: 'Clear navigation to find content', priority: 'must-have', addressed: false });
719
+ }
720
+ if (interactiveElements > 0) {
721
+ needs.push({ description: 'Interactive elements for engagement', priority: 'must-have', addressed: true });
722
+ }
723
+ if (accessibility && (accessibility.focusableElementsCount || 0) > 0) {
724
+ needs.push({ description: 'Keyboard navigation support', priority: 'must-have', addressed: true });
725
+ }
726
+ else {
727
+ challenges.push('Limited keyboard navigation - inaccessible to some users');
728
+ needs.push({ description: 'Keyboard navigation support', priority: 'must-have', addressed: false });
729
+ }
730
+ // Should-have features (important for good UX)
731
+ if (semantic?.hasHeader) {
732
+ needs.push({ description: 'Consistent page header for orientation', priority: 'should-have', addressed: true });
733
+ }
734
+ if (semantic?.hasFooter) {
735
+ needs.push({ description: 'Footer with supporting information', priority: 'should-have', addressed: true });
736
+ }
737
+ if (accessibility && (accessibility.altTextsCoverage || 0) > 50) {
738
+ needs.push({ description: 'Image descriptions for screen readers', priority: 'should-have', addressed: true });
739
+ }
740
+ else if (accessibility && (accessibility.altTextsCoverage || 0) < 50) {
741
+ challenges.push('Poor alt text coverage - images not accessible');
742
+ needs.push({ description: 'Image descriptions for screen readers', priority: 'should-have', addressed: false });
743
+ }
744
+ if (context.performance && (context.performance.loadTime || 0) < 3000) {
745
+ needs.push({ description: 'Fast page load time', priority: 'should-have', addressed: true });
746
+ }
747
+ else if (context.performance && (context.performance.loadTime || 0) >= 3000) {
748
+ challenges.push('Slow load time - user frustration risk');
749
+ needs.push({ description: 'Fast page load time', priority: 'should-have', addressed: false });
750
+ }
751
+ // Nice-to-have features (enhancements)
752
+ if (semantic?.hasAside) {
753
+ needs.push({ description: 'Supplementary content sections', priority: 'nice-to-have', addressed: true });
754
+ }
755
+ if (accessibility && (accessibility.landmarkRoles || 0) > 3) {
756
+ needs.push({ description: 'Rich ARIA landmarks for navigation', priority: 'nice-to-have', addressed: true });
757
+ }
758
+ if (forms > 0) {
759
+ needs.push({ description: 'Form interactions for user input', priority: 'nice-to-have', addressed: true });
760
+ }
761
+ // Determine suitability
762
+ const addressedMustHaves = needs.filter(n => n.priority === 'must-have' && n.addressed).length;
763
+ const totalMustHaves = needs.filter(n => n.priority === 'must-have').length;
764
+ let suitability;
765
+ if (challenges.length === 0 && addressedMustHaves >= 3) {
766
+ suitability = 'excellent';
767
+ }
768
+ else if (challenges.length <= 1 && addressedMustHaves >= 2) {
769
+ suitability = 'good';
770
+ }
771
+ else if (challenges.length <= 2 && addressedMustHaves >= 2) {
772
+ suitability = 'adequate';
773
+ }
774
+ else {
775
+ suitability = 'poor';
776
+ }
777
+ // Calculate alignment score
778
+ let alignmentScore = 40;
779
+ alignmentScore += addressedMustHaves * 10;
780
+ alignmentScore += needs.filter(n => n.priority === 'should-have' && n.addressed).length * 5;
781
+ alignmentScore += needs.filter(n => n.priority === 'nice-to-have' && n.addressed).length * 2;
782
+ alignmentScore -= challenges.length * 8;
783
+ alignmentScore = Math.max(0, Math.min(100, alignmentScore));
784
+ return {
785
+ needs,
786
+ suitability,
787
+ challenges,
788
+ alignmentScore
334
789
  };
335
- return analysis;
336
790
  }
337
791
  /**
338
792
  * Analyze business needs
339
793
  */
340
- async analyzeBusinessNeeds(_context, _problemAnalysis) {
794
+ async analyzeBusinessNeeds(context, problemAnalysis) {
341
795
  this.logger.debug('Analyzing business needs');
342
- const analysis = {
343
- primaryGoal: 'balanced',
344
- kpisAffected: [],
345
- crossTeamImpact: [],
346
- compromisesUX: false,
347
- impactsKPIs: false,
348
- alignmentScore: 70
796
+ const forms = context.domMetrics?.forms || 0;
797
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
798
+ const performance = context.performance;
799
+ const hasErrors = context.errorIndicators?.hasErrorMessages || false;
800
+ let primaryGoal;
801
+ let kpisAffected = [];
802
+ if (forms > 2) {
803
+ primaryGoal = 'business-ease'; // Conversion focus leans business
804
+ kpisAffected = ['Form completion rate', 'Lead generation', 'User sign-ups'];
805
+ }
806
+ else if (interactiveElements > 30) {
807
+ primaryGoal = 'user-experience'; // Engagement focus leans UX
808
+ kpisAffected = ['Time on site', 'Click-through rate', 'User engagement'];
809
+ }
810
+ else {
811
+ primaryGoal = 'balanced';
812
+ kpisAffected = ['Content consumption', 'Bounce rate', 'Page views'];
813
+ }
814
+ const crossTeamImpact = [];
815
+ if (forms > 0) {
816
+ crossTeamImpact.push({
817
+ team: 'Marketing',
818
+ impactType: 'positive',
819
+ description: 'Form conversion optimization needed'
820
+ });
821
+ crossTeamImpact.push({
822
+ team: 'Development',
823
+ impactType: 'neutral',
824
+ description: 'Form validation and submission handling'
825
+ });
826
+ }
827
+ if (context.accessibility && (context.accessibility.altTextsCoverage || 0) < 100) {
828
+ crossTeamImpact.push({
829
+ team: 'Content',
830
+ impactType: 'negative',
831
+ description: 'Image alt text creation required'
832
+ });
833
+ }
834
+ if (performance && (performance.loadTime || 0) > 2000) {
835
+ crossTeamImpact.push({
836
+ team: 'Engineering',
837
+ impactType: 'negative',
838
+ description: 'Performance optimization needed'
839
+ });
840
+ }
841
+ if (problemAnalysis.complexity === 'complex') {
842
+ crossTeamImpact.push({
843
+ team: 'QA',
844
+ impactType: 'neutral',
845
+ description: 'Comprehensive testing strategy required'
846
+ });
847
+ }
848
+ let compromisesUX = false;
849
+ if (hasErrors) {
850
+ compromisesUX = true;
851
+ }
852
+ if (context.accessibility && (context.accessibility.altTextsCoverage || 0) < 50) {
853
+ compromisesUX = true;
854
+ }
855
+ if (performance && (performance.loadTime || 0) > 4000) {
856
+ compromisesUX = true;
857
+ }
858
+ const impactsKPIs = kpisAffected.length > 0;
859
+ let alignmentScore = 50;
860
+ if (kpisAffected.length > 0)
861
+ alignmentScore += 15;
862
+ if (crossTeamImpact.length > 0)
863
+ alignmentScore += 10;
864
+ if (!compromisesUX)
865
+ alignmentScore += 20;
866
+ if (impactsKPIs)
867
+ alignmentScore += 5;
868
+ alignmentScore = Math.min(100, alignmentScore);
869
+ return {
870
+ primaryGoal,
871
+ kpisAffected,
872
+ crossTeamImpact,
873
+ compromisesUX,
874
+ impactsKPIs,
875
+ alignmentScore
349
876
  };
350
- return analysis;
351
877
  }
352
878
  /**
353
879
  * Detect oracle problems from analysis context
@@ -381,79 +907,148 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
381
907
  */
382
908
  async generateRecommendations(problemAnalysis, userNeeds, businessNeeds, oracleProblems, impactAnalysis, heuristics, _testabilityIntegration) {
383
909
  const recommendations = [];
384
- // Generate recommendations from problem analysis
385
- if (problemAnalysis.clarityScore < (this.config.thresholds?.minProblemClarity || 60)) {
910
+ let priorityCounter = 1;
911
+ // Oracle problems (highest priority - match manual report structure)
912
+ for (const problem of oracleProblems) {
913
+ const impactScore = problem.severity === 'critical' ? 90 : problem.severity === 'high' ? 80 : problem.severity === 'medium' ? 60 : 40;
914
+ const impactPct = Math.round((impactScore / 100) * 30); // Up to 30% impact
386
915
  recommendations.push({
387
- principle: 'Problem Clarity',
388
- recommendation: 'Improve problem statement clarity and breakdown',
389
- severity: 'high',
390
- impact: 100 - problemAnalysis.clarityScore,
391
- effort: 'medium',
392
- priority: 1,
393
- category: 'qx'
916
+ principle: 'Oracle Problem',
917
+ recommendation: `Resolve: ${problem.description}`,
918
+ severity: problem.severity,
919
+ impact: impactScore,
920
+ effort: problem.severity === 'critical' || problem.severity === 'high' ? 'high' : 'medium',
921
+ priority: priorityCounter++,
922
+ category: 'qa',
923
+ impactPercentage: impactPct,
924
+ estimatedEffort: problem.severity === 'critical' ? 'High - Critical issue' : problem.severity === 'high' ? 'High' : 'Medium'
394
925
  });
395
926
  }
396
- // Generate recommendations from user needs
397
- if (userNeeds.alignmentScore < (this.config.thresholds?.minUserNeedsAlignment || 70)) {
927
+ // Problem clarity
928
+ if (problemAnalysis.clarityScore < (this.config.thresholds?.minProblemClarity || 70)) {
929
+ const gap = 70 - problemAnalysis.clarityScore;
398
930
  recommendations.push({
399
- principle: 'User Needs Alignment',
400
- recommendation: 'Better align solution with user needs',
401
- severity: 'high',
402
- impact: 100 - userNeeds.alignmentScore,
403
- effort: 'high',
404
- priority: 2,
405
- category: 'ux'
931
+ principle: 'Problem Understanding',
932
+ recommendation: 'Improve problem statement clarity with detailed breakdown of failure modes and user scenarios',
933
+ severity: gap > 25 ? 'high' : 'medium',
934
+ impact: Math.round(gap * 1.2),
935
+ effort: 'medium',
936
+ priority: priorityCounter++,
937
+ category: 'qx',
938
+ impactPercentage: Math.round((gap / 70) * 20),
939
+ estimatedEffort: 'Medium - Requires stakeholder workshops'
406
940
  });
407
941
  }
408
- // Generate recommendations from business needs
409
- if (businessNeeds.alignmentScore < (this.config.thresholds?.minBusinessAlignment || 70)) {
942
+ // User needs alignment (match manual report priority)
943
+ if (userNeeds.alignmentScore < (this.config.thresholds?.minUserNeedsAlignment || 75)) {
944
+ const gap = 75 - userNeeds.alignmentScore;
945
+ const impactPct = Math.min(35, Math.round((gap / 75) * 100));
410
946
  recommendations.push({
411
- principle: 'Business Alignment',
412
- recommendation: 'Improve alignment with business objectives',
413
- severity: 'medium',
414
- impact: 100 - businessNeeds.alignmentScore,
415
- effort: 'medium',
416
- priority: 3,
417
- category: 'qx'
947
+ principle: 'User Needs Alignment',
948
+ recommendation: `Improve user needs coverage from ${userNeeds.alignmentScore}/100 to at least 75/100`,
949
+ severity: gap > 20 ? 'high' : 'medium',
950
+ impact: Math.round(gap * 0.9),
951
+ effort: gap > 25 ? 'high' : 'medium',
952
+ priority: priorityCounter++,
953
+ category: 'ux',
954
+ impactPercentage: impactPct,
955
+ estimatedEffort: gap > 25 ? 'High - Major UX redesign' : 'Medium - UX improvements'
418
956
  });
419
957
  }
420
- // Generate recommendations from oracle problems
421
- for (const problem of oracleProblems) {
422
- if (problem.severity === 'high' || problem.severity === 'critical') {
958
+ // Heuristic-specific high-impact recommendations
959
+ const lowScoringHeuristics = heuristics
960
+ .filter(h => h.score < 70)
961
+ .sort((a, b) => a.score - b.score)
962
+ .slice(0, 5); // Top 5 worst
963
+ lowScoringHeuristics.forEach(heuristic => {
964
+ const impactScore = 75 - heuristic.score;
965
+ const impactPct = Math.round((impactScore / 75) * 25);
966
+ if (heuristic.recommendations.length > 0 && heuristic.heuristicType) {
423
967
  recommendations.push({
424
- principle: 'Oracle Problem',
425
- recommendation: `Resolve: ${problem.description}`,
426
- severity: problem.severity,
427
- impact: 80,
428
- effort: 'high',
429
- priority: problem.severity === 'critical' ? 1 : 2,
430
- category: 'qa'
968
+ principle: this.formatHeuristicName(heuristic.heuristicType),
969
+ recommendation: heuristic.recommendations[0],
970
+ severity: heuristic.score < 50 ? 'high' : 'medium',
971
+ impact: impactScore,
972
+ effort: heuristic.score < 40 ? 'high' : 'medium',
973
+ priority: priorityCounter++,
974
+ category: heuristic.category,
975
+ impactPercentage: impactPct,
976
+ estimatedEffort: heuristic.score < 40 ? 'High - Significant work required' : 'Medium'
431
977
  });
432
978
  }
979
+ });
980
+ // High-impact issues from heuristics
981
+ heuristics.forEach(heuristic => {
982
+ if (!heuristic.heuristicType)
983
+ return;
984
+ heuristic.issues
985
+ .filter(issue => issue.severity === 'critical' || issue.severity === 'high')
986
+ .slice(0, 1) // One per heuristic
987
+ .forEach(issue => {
988
+ const impactScore = Math.min(85, 100 - heuristic.score);
989
+ const impactPct = Math.round((impactScore / 100) * 22);
990
+ recommendations.push({
991
+ principle: this.formatHeuristicName(heuristic.heuristicType),
992
+ recommendation: issue.description,
993
+ severity: issue.severity,
994
+ impact: impactScore,
995
+ effort: issue.severity === 'critical' ? 'high' : 'medium',
996
+ priority: priorityCounter++,
997
+ category: heuristic.category,
998
+ impactPercentage: impactPct,
999
+ estimatedEffort: issue.severity === 'critical' ? 'High - Critical fix' : 'Medium'
1000
+ });
1001
+ });
1002
+ });
1003
+ // Business-user balance
1004
+ const balanceDiff = Math.abs(userNeeds.alignmentScore - businessNeeds.alignmentScore);
1005
+ if (balanceDiff > 15) {
1006
+ const impactPct = Math.round((balanceDiff / 100) * 20);
1007
+ const favorsUser = userNeeds.alignmentScore > businessNeeds.alignmentScore;
1008
+ recommendations.push({
1009
+ principle: 'User-Business Balance',
1010
+ recommendation: favorsUser
1011
+ ? 'Strengthen business value metrics while maintaining user experience quality'
1012
+ : 'Enhance user experience focus to balance business-centric approach',
1013
+ severity: balanceDiff > 30 ? 'high' : 'medium',
1014
+ impact: Math.round(balanceDiff * 0.75),
1015
+ effort: 'medium',
1016
+ priority: priorityCounter++,
1017
+ category: 'qx',
1018
+ impactPercentage: impactPct,
1019
+ estimatedEffort: 'Medium - Requires stakeholder alignment'
1020
+ });
433
1021
  }
434
- // Generate recommendations from heuristics
435
- for (const heuristic of heuristics) {
436
- for (const issue of heuristic.issues) {
437
- if (issue.severity === 'high' || issue.severity === 'critical') {
438
- recommendations.push({
439
- principle: heuristic.name,
440
- recommendation: issue.description,
441
- severity: issue.severity,
442
- impact: 100 - heuristic.score,
443
- effort: 'medium',
444
- priority: 4,
445
- category: 'design'
446
- });
447
- }
448
- }
1022
+ // Business needs (if misaligned)
1023
+ if (businessNeeds.alignmentScore < (this.config.thresholds?.minBusinessAlignment || 70)) {
1024
+ const gap = 70 - businessNeeds.alignmentScore;
1025
+ recommendations.push({
1026
+ principle: 'Business Alignment',
1027
+ recommendation: 'Improve alignment with business KPIs and objectives',
1028
+ severity: gap > 25 ? 'high' : 'medium',
1029
+ impact: Math.round(gap * 0.7),
1030
+ effort: 'medium',
1031
+ priority: priorityCounter++,
1032
+ category: 'qx',
1033
+ impactPercentage: Math.round((gap / 70) * 18),
1034
+ estimatedEffort: 'Medium - Business stakeholder review'
1035
+ });
449
1036
  }
450
- // Sort by priority and impact
451
- recommendations.sort((a, b) => {
452
- if (a.priority !== b.priority)
453
- return a.priority - b.priority;
454
- return b.impact - a.impact;
1037
+ // Sort by impact percentage and limit to top 10
1038
+ const sorted = recommendations
1039
+ .sort((a, b) => (b.impactPercentage || 0) - (a.impactPercentage || 0))
1040
+ .slice(0, 10);
1041
+ // Reassign priorities
1042
+ sorted.forEach((rec, idx) => {
1043
+ rec.priority = idx + 1;
455
1044
  });
456
- return recommendations;
1045
+ return sorted;
1046
+ }
1047
+ formatHeuristicName(heuristic) {
1048
+ return heuristic
1049
+ .split('-')
1050
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
1051
+ .join(' ');
457
1052
  }
458
1053
  /**
459
1054
  * Calculate overall QX score
@@ -668,17 +1263,357 @@ class QXHeuristicsEngine {
668
1263
  }
669
1264
  return results;
670
1265
  }
671
- async apply(heuristic, _context, _problemAnalysis, _userNeeds, _businessNeeds) {
672
- // Simplified heuristic application
673
- // In real implementation, each heuristic would have specific logic
1266
+ async apply(heuristic, context, problemAnalysis, userNeeds, businessNeeds) {
1267
+ const category = this.getHeuristicCategory(heuristic);
1268
+ const findings = [];
1269
+ const issues = [];
1270
+ const recommendations = [];
1271
+ let score = 75; // Base score
1272
+ // Apply specific heuristic logic based on type
1273
+ switch (heuristic) {
1274
+ case qx_1.QXHeuristic.CONSISTENCY_ANALYSIS:
1275
+ if (context.domMetrics?.semanticStructure?.hasHeader && context.domMetrics?.semanticStructure?.hasFooter) {
1276
+ score = 85;
1277
+ findings.push('Consistent page structure with header and footer');
1278
+ }
1279
+ else {
1280
+ score = 60;
1281
+ recommendations.push('Add consistent header/footer structure');
1282
+ }
1283
+ break;
1284
+ case qx_1.QXHeuristic.INTUITIVE_DESIGN:
1285
+ const hasNav = context.domMetrics?.semanticStructure?.hasNav;
1286
+ const focusable = context.accessibility?.focusableElementsCount || 0;
1287
+ if (hasNav && focusable > 10) {
1288
+ score = 82;
1289
+ findings.push('Intuitive navigation and interaction design');
1290
+ }
1291
+ else {
1292
+ score = 55;
1293
+ issues.push({ description: 'Navigation or interaction patterns unclear', severity: 'medium' });
1294
+ }
1295
+ break;
1296
+ case qx_1.QXHeuristic.EXACTNESS_AND_CLARITY:
1297
+ // Visual hierarchy and clarity (Design category)
1298
+ score = 70;
1299
+ const hasSemanticStructure = context.domMetrics?.semanticStructure;
1300
+ const structureScore = [
1301
+ hasSemanticStructure?.hasHeader,
1302
+ hasSemanticStructure?.hasMain,
1303
+ hasSemanticStructure?.hasNav,
1304
+ hasSemanticStructure?.hasFooter
1305
+ ].filter(Boolean).length;
1306
+ score = 50 + (structureScore * 10);
1307
+ if (structureScore >= 3) {
1308
+ findings.push('Strong visual hierarchy with semantic HTML elements');
1309
+ }
1310
+ else if (structureScore >= 2) {
1311
+ findings.push('Moderate visual hierarchy - some semantic elements present');
1312
+ recommendations.push('Add more semantic HTML5 elements for clarity');
1313
+ }
1314
+ else {
1315
+ issues.push({ description: 'Weak visual hierarchy - missing semantic structure', severity: 'high' });
1316
+ recommendations.push('Implement semantic HTML5: header, nav, main, footer');
1317
+ }
1318
+ // Title and description clarity
1319
+ if (context.metadata?.description && context.metadata.description.length > 20) {
1320
+ score += 10;
1321
+ findings.push('Page has descriptive metadata');
1322
+ }
1323
+ break;
1324
+ case qx_1.QXHeuristic.USER_FEELINGS_IMPACT:
1325
+ // Comprehensive user feelings analysis (Interaction + Accessibility)
1326
+ const altCoverage = context.accessibility?.altTextsCoverage || 0;
1327
+ const loadTime = context.performance?.loadTime || 0;
1328
+ const ariaLabels = context.accessibility?.ariaLabelsCount || 0;
1329
+ const focusableElements = context.accessibility?.focusableElementsCount || 0;
1330
+ score = 60; // Base
1331
+ // Accessibility impact on feelings (35% weight)
1332
+ if (altCoverage >= 90) {
1333
+ score += 20;
1334
+ findings.push('Excellent accessibility (90%+ alt coverage) creates inclusive, positive experience');
1335
+ }
1336
+ else if (altCoverage >= 70) {
1337
+ score += 12;
1338
+ findings.push('Good accessibility creates generally positive user feelings');
1339
+ }
1340
+ else if (altCoverage < 50) {
1341
+ score -= 15;
1342
+ issues.push({ description: 'Poor accessibility (<50% alt coverage) frustrates users with disabilities', severity: 'high' });
1343
+ recommendations.push('Improve alt text coverage to at least 80% for better accessibility');
1344
+ }
1345
+ // ARIA support impact
1346
+ if (ariaLabels > 5) {
1347
+ score += 8;
1348
+ findings.push('Strong ARIA labeling enhances screen reader experience');
1349
+ }
1350
+ // Performance impact on feelings (35% weight)
1351
+ if (loadTime < 1500) {
1352
+ score += 15;
1353
+ findings.push('Very fast load time (<1.5s) delights users');
1354
+ }
1355
+ else if (loadTime < 2500) {
1356
+ score += 8;
1357
+ findings.push('Fast load time enhances user satisfaction');
1358
+ }
1359
+ else if (loadTime > 4000) {
1360
+ score -= 20;
1361
+ issues.push({ description: 'Very slow load time (>4s) causes significant frustration', severity: 'critical' });
1362
+ recommendations.push('Optimize page load time - target under 2.5 seconds');
1363
+ }
1364
+ else if (loadTime > 3000) {
1365
+ score -= 12;
1366
+ issues.push({ description: 'Slow load time causes user frustration', severity: 'high' });
1367
+ }
1368
+ // Error visibility impact (15% weight)
1369
+ if (context.errorIndicators?.hasErrorMessages) {
1370
+ score -= 12;
1371
+ issues.push({ description: 'Visible errors reduce user confidence and satisfaction', severity: 'high' });
1372
+ recommendations.push('Review and fix visible error messages');
1373
+ }
1374
+ // Interaction capability (15% weight)
1375
+ if (focusableElements > 20) {
1376
+ score += 5;
1377
+ findings.push('Rich interactive elements provide user control and engagement');
1378
+ }
1379
+ else if (focusableElements < 5) {
1380
+ score -= 8;
1381
+ issues.push({ description: 'Limited interactivity may feel restrictive', severity: 'medium' });
1382
+ }
1383
+ score = Math.max(20, Math.min(100, score));
1384
+ break;
1385
+ case qx_1.QXHeuristic.GUI_FLOW_IMPACT:
1386
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
1387
+ const forms = context.domMetrics?.forms || 0;
1388
+ if (interactiveElements > 20) {
1389
+ score = 75;
1390
+ findings.push(`${interactiveElements} interactive elements provide user control`);
1391
+ }
1392
+ if (forms > 0) {
1393
+ findings.push(`${forms} forms impact user input flows`);
1394
+ score = Math.min(100, score + 10);
1395
+ }
1396
+ if (interactiveElements === 0) {
1397
+ score = 30;
1398
+ issues.push({ description: 'Limited user interaction capability', severity: 'high' });
1399
+ }
1400
+ break;
1401
+ case qx_1.QXHeuristic.CROSS_FUNCTIONAL_IMPACT:
1402
+ score = 70;
1403
+ if (context.accessibility && (context.accessibility.altTextsCoverage || 0) < 100) {
1404
+ findings.push('Content team needed for alt text creation');
1405
+ }
1406
+ if (context.performance && (context.performance.loadTime || 0) > 2000) {
1407
+ findings.push('Engineering team needed for performance optimization');
1408
+ }
1409
+ if (problemAnalysis.complexity === 'complex') {
1410
+ findings.push('QA team needed for comprehensive testing');
1411
+ }
1412
+ score = 70 + (findings.length * 5);
1413
+ break;
1414
+ case qx_1.QXHeuristic.DATA_DEPENDENT_IMPACT:
1415
+ if (context.domMetrics?.forms && context.domMetrics.forms > 0) {
1416
+ score = 75;
1417
+ findings.push(`${context.domMetrics.forms} forms depend on backend data processing`);
1418
+ }
1419
+ else {
1420
+ score = 50;
1421
+ findings.push('Limited data-dependent features');
1422
+ }
1423
+ break;
1424
+ case qx_1.QXHeuristic.PROBLEM_UNDERSTANDING:
1425
+ score = problemAnalysis.clarityScore;
1426
+ if (problemAnalysis.clarityScore > 80) {
1427
+ findings.push('Problem is well-defined');
1428
+ }
1429
+ else {
1430
+ issues.push({ description: 'Problem clarity needs improvement', severity: 'medium' });
1431
+ }
1432
+ findings.push(...problemAnalysis.breakdown);
1433
+ break;
1434
+ case qx_1.QXHeuristic.RULE_OF_THREE:
1435
+ score = problemAnalysis.potentialFailures.length >= 3 ? 85 : 60;
1436
+ findings.push(`${problemAnalysis.potentialFailures.length} potential failure modes identified`);
1437
+ if (problemAnalysis.potentialFailures.length < 3) {
1438
+ recommendations.push('Identify at least 3 potential failure modes');
1439
+ }
1440
+ break;
1441
+ case qx_1.QXHeuristic.PROBLEM_COMPLEXITY:
1442
+ score = problemAnalysis.complexity === 'simple' ? 90 :
1443
+ problemAnalysis.complexity === 'moderate' ? 75 : 60;
1444
+ findings.push(`Problem complexity: ${problemAnalysis.complexity}`);
1445
+ break;
1446
+ case qx_1.QXHeuristic.USER_NEEDS_IDENTIFICATION:
1447
+ score = userNeeds.alignmentScore;
1448
+ findings.push(`${userNeeds.needs.length} user needs identified`);
1449
+ const mustHave = userNeeds.needs.filter(n => n.priority === 'must-have').length;
1450
+ findings.push(`${mustHave} must-have features`);
1451
+ if (userNeeds.challenges.length > 0) {
1452
+ issues.push({ description: `${userNeeds.challenges.length} user need challenges found`, severity: 'medium' });
1453
+ }
1454
+ break;
1455
+ case qx_1.QXHeuristic.USER_NEEDS_SUITABILITY:
1456
+ score = userNeeds.suitability === 'excellent' ? 95 :
1457
+ userNeeds.suitability === 'good' ? 80 :
1458
+ userNeeds.suitability === 'adequate' ? 65 : 45;
1459
+ findings.push(`User needs suitability: ${userNeeds.suitability}`);
1460
+ break;
1461
+ case qx_1.QXHeuristic.USER_NEEDS_VALIDATION:
1462
+ const addressedNeeds = userNeeds.needs.filter(n => n.addressed).length;
1463
+ score = userNeeds.needs.length > 0 ? (addressedNeeds / userNeeds.needs.length) * 100 : 50;
1464
+ findings.push(`${addressedNeeds}/${userNeeds.needs.length} needs validated and addressed`);
1465
+ break;
1466
+ case qx_1.QXHeuristic.BUSINESS_NEEDS_IDENTIFICATION:
1467
+ score = businessNeeds.alignmentScore;
1468
+ findings.push(`Primary goal: ${businessNeeds.primaryGoal}`);
1469
+ findings.push(`${businessNeeds.kpisAffected.length} KPIs affected`);
1470
+ findings.push(`${businessNeeds.crossTeamImpact.length} cross-team impacts`);
1471
+ break;
1472
+ case qx_1.QXHeuristic.USER_VS_BUSINESS_BALANCE:
1473
+ const balanceScore = 100 - Math.abs(userNeeds.alignmentScore - businessNeeds.alignmentScore);
1474
+ score = balanceScore;
1475
+ if (balanceScore > 80) {
1476
+ findings.push('Good balance between user and business needs');
1477
+ }
1478
+ else {
1479
+ issues.push({ description: 'Imbalance between user and business priorities', severity: 'medium' });
1480
+ recommendations.push('Align user and business objectives more closely');
1481
+ }
1482
+ break;
1483
+ case qx_1.QXHeuristic.KPI_IMPACT_ANALYSIS:
1484
+ score = businessNeeds.impactsKPIs ? 85 : 50;
1485
+ findings.push(`KPIs impacted: ${businessNeeds.kpisAffected.join(', ')}`);
1486
+ if (businessNeeds.compromisesUX) {
1487
+ issues.push({ description: 'Business ease compromises user experience', severity: 'high' });
1488
+ score -= 20;
1489
+ }
1490
+ break;
1491
+ case qx_1.QXHeuristic.ORACLE_PROBLEM_DETECTION:
1492
+ // This is handled separately, score based on whether we can detect issues
1493
+ score = 75;
1494
+ findings.push('Oracle problem detection capability active');
1495
+ break;
1496
+ case qx_1.QXHeuristic.WHAT_MUST_NOT_CHANGE:
1497
+ score = 80;
1498
+ if (context.domMetrics?.semanticStructure?.hasMain) {
1499
+ findings.push('Main content structure is immutable');
1500
+ }
1501
+ if (context.accessibility && (context.accessibility.focusableElementsCount || 0) > 0) {
1502
+ findings.push('Keyboard navigation support must be maintained');
1503
+ }
1504
+ break;
1505
+ case qx_1.QXHeuristic.SUPPORTING_DATA_ANALYSIS:
1506
+ score = 75;
1507
+ const hasData = (context.domMetrics?.forms || 0) > 0 || (context.domMetrics?.interactiveElements || 0) > 20;
1508
+ if (hasData) {
1509
+ score = 82;
1510
+ findings.push('Sufficient data points for informed decision-making');
1511
+ }
1512
+ else {
1513
+ score = 60;
1514
+ issues.push({ description: 'Limited data for comprehensive analysis', severity: 'medium' });
1515
+ recommendations.push('Collect more user interaction data');
1516
+ }
1517
+ break;
1518
+ case qx_1.QXHeuristic.COMPETITIVE_ANALYSIS:
1519
+ score = 70; // Baseline - actual comparison would need competitor data
1520
+ findings.push('Competitive analysis capability available');
1521
+ if (context.domMetrics?.semanticStructure?.hasNav && context.domMetrics?.interactiveElements && context.domMetrics.interactiveElements > 15) {
1522
+ score = 78;
1523
+ findings.push('Navigation and interaction patterns follow industry standards');
1524
+ }
1525
+ else {
1526
+ recommendations.push('Compare interaction patterns with leading competitors');
1527
+ }
1528
+ break;
1529
+ case qx_1.QXHeuristic.DOMAIN_INSPIRATION:
1530
+ score = 72;
1531
+ const hasModernElements = context.accessibility && (context.accessibility.ariaLabelsCount || 0) > 0;
1532
+ if (hasModernElements) {
1533
+ score = 80;
1534
+ findings.push('Modern accessibility patterns show domain inspiration');
1535
+ }
1536
+ else {
1537
+ recommendations.push('Research domain-specific design patterns and best practices');
1538
+ }
1539
+ break;
1540
+ case qx_1.QXHeuristic.INNOVATIVE_SOLUTIONS:
1541
+ score = 65; // Most sites are conventional
1542
+ const hasAdvancedFeatures = (context.accessibility?.landmarkRoles || 0) > 3;
1543
+ if (hasAdvancedFeatures) {
1544
+ score = 75;
1545
+ findings.push('Advanced accessibility features show innovative thinking');
1546
+ }
1547
+ else {
1548
+ recommendations.push('Explore innovative UX patterns to differentiate experience');
1549
+ }
1550
+ break;
1551
+ case qx_1.QXHeuristic.COUNTER_INTUITIVE_DESIGN:
1552
+ score = 85; // High score means few counter-intuitive elements (good)
1553
+ const confusingNav = !context.domMetrics?.semanticStructure?.hasNav && (context.domMetrics?.interactiveElements || 0) > 10;
1554
+ const poorStructure = !context.domMetrics?.semanticStructure?.hasHeader && !context.domMetrics?.semanticStructure?.hasFooter;
1555
+ if (confusingNav) {
1556
+ score = 45;
1557
+ issues.push({ description: 'Navigation structure may be counter-intuitive', severity: 'high' });
1558
+ recommendations.push('Add semantic navigation elements');
1559
+ }
1560
+ if (poorStructure) {
1561
+ score -= 15;
1562
+ issues.push({ description: 'Page structure lacks expected header/footer', severity: 'medium' });
1563
+ }
1564
+ if (score > 75) {
1565
+ findings.push('No counter-intuitive design patterns detected');
1566
+ }
1567
+ break;
1568
+ case qx_1.QXHeuristic.SUPPORTING_DATA_ANALYSIS:
1569
+ score = 70;
1570
+ if (context.performance)
1571
+ findings.push('Performance data available');
1572
+ if (context.accessibility)
1573
+ findings.push('Accessibility metrics available');
1574
+ if (context.domMetrics)
1575
+ findings.push('DOM structure data available');
1576
+ score = 60 + (findings.length * 10);
1577
+ break;
1578
+ case qx_1.QXHeuristic.COMPETITIVE_ANALYSIS:
1579
+ score = 65;
1580
+ findings.push('Competitive analysis capability available');
1581
+ recommendations.push('Compare with competitor sites for benchmarking');
1582
+ break;
1583
+ case qx_1.QXHeuristic.DOMAIN_INSPIRATION:
1584
+ score = 70;
1585
+ findings.push('Consider best practices from similar domains');
1586
+ break;
1587
+ case qx_1.QXHeuristic.INNOVATIVE_SOLUTIONS:
1588
+ score = 68;
1589
+ findings.push('Opportunity for innovative UX solutions');
1590
+ break;
1591
+ case qx_1.QXHeuristic.COUNTER_INTUITIVE_DESIGN:
1592
+ score = 75;
1593
+ findings.push('No counter-intuitive design patterns detected');
1594
+ break;
1595
+ default:
1596
+ // Generic heuristic evaluation based on category
1597
+ if (category === 'user-needs') {
1598
+ score = userNeeds.alignmentScore;
1599
+ }
1600
+ else if (category === 'business-needs') {
1601
+ score = businessNeeds.alignmentScore;
1602
+ }
1603
+ else if (category === 'problem') {
1604
+ score = problemAnalysis.clarityScore;
1605
+ }
1606
+ break;
1607
+ }
674
1608
  return {
675
1609
  name: heuristic,
676
- category: this.getHeuristicCategory(heuristic),
1610
+ heuristicType: heuristic,
1611
+ category,
677
1612
  applied: true,
678
- score: 75, // Placeholder
679
- findings: [],
680
- issues: [],
681
- recommendations: []
1613
+ score: Math.min(100, Math.max(0, score)),
1614
+ findings,
1615
+ issues,
1616
+ recommendations
682
1617
  };
683
1618
  }
684
1619
  getHeuristicCategory(heuristic) {
@@ -733,6 +1668,67 @@ class OracleDetector {
733
1668
  ]
734
1669
  });
735
1670
  }
1671
+ // ENHANCED: Detect contextual oracle problems even for well-built sites
1672
+ const titleLower = (context.title || '').toLowerCase();
1673
+ const descLower = (context.metadata?.description || '').toLowerCase();
1674
+ // E-commerce/Travel booking: Conversion vs UX quality
1675
+ if (titleLower.includes('hotel') || titleLower.includes('booking') || titleLower.includes('travel') ||
1676
+ titleLower.includes('shop') || titleLower.includes('store') || descLower.includes('book')) {
1677
+ if (businessNeeds.kpisAffected.some(k => k.toLowerCase().includes('conversion') || k.toLowerCase().includes('engagement'))) {
1678
+ problems.push({
1679
+ type: 'user-vs-business',
1680
+ description: 'Potential conflict between conversion optimization (business) and user experience quality (user trust)',
1681
+ severity: 'medium',
1682
+ stakeholders: ['Marketing', 'Product', 'Users'],
1683
+ resolutionApproach: [
1684
+ 'A/B test aggressive vs. subtle conversion tactics',
1685
+ 'Measure both conversion rate and user satisfaction metrics',
1686
+ 'Balance urgency messaging with transparent communication'
1687
+ ]
1688
+ });
1689
+ }
1690
+ // Price transparency oracle
1691
+ problems.push({
1692
+ type: 'unclear-criteria',
1693
+ description: 'Unclear criteria for price display timing - when to show fees, taxes, and final price',
1694
+ severity: 'medium',
1695
+ stakeholders: ['Users', 'Legal', 'Business'],
1696
+ resolutionApproach: [
1697
+ 'Define regulatory compliance requirements for price display',
1698
+ 'Balance business desire for competitive base pricing vs user need for full price transparency',
1699
+ 'Establish clear standards for fee disclosure timing'
1700
+ ]
1701
+ });
1702
+ }
1703
+ // Content sites: Quality vs. Quantity
1704
+ if (titleLower.includes('blog') || titleLower.includes('article') || titleLower.includes('news') ||
1705
+ titleLower.includes('magazine') || titleLower.includes('testing')) {
1706
+ problems.push({
1707
+ type: 'user-vs-business',
1708
+ description: 'Content depth (user need) vs. publication frequency (business engagement goals) trade-off',
1709
+ severity: 'low',
1710
+ stakeholders: ['Readers', 'Content Team', 'Editorial'],
1711
+ resolutionApproach: [
1712
+ 'Define content quality standards and acceptance criteria',
1713
+ 'Balance editorial calendar with quality thresholds',
1714
+ 'Consider mix of in-depth and quick-read content formats'
1715
+ ]
1716
+ });
1717
+ }
1718
+ // Complex sites: Technical constraints
1719
+ if ((context.domMetrics?.totalElements || 0) > 500 || (context.domMetrics?.interactiveElements || 0) > 50) {
1720
+ problems.push({
1721
+ type: 'technical-constraint',
1722
+ description: 'Platform technical limitations may restrict advanced UX features or accessibility enhancements',
1723
+ severity: 'low',
1724
+ stakeholders: ['Development', 'Product', 'Users'],
1725
+ resolutionApproach: [
1726
+ 'Evaluate platform capabilities and constraints',
1727
+ 'Prioritize features based on user impact vs. implementation complexity',
1728
+ 'Consider gradual enhancement approach'
1729
+ ]
1730
+ });
1731
+ }
736
1732
  return problems.filter(p => this.meetsMinimumSeverity(p.severity));
737
1733
  }
738
1734
  meetsMinimumSeverity(severity) {
@@ -746,23 +1742,89 @@ class OracleDetector {
746
1742
  * Impact Analyzer
747
1743
  */
748
1744
  class ImpactAnalyzer {
749
- async analyze(_context, _problemAnalysis) {
750
- // Simplified impact analysis
751
- // In real implementation, this would perform deep analysis
1745
+ async analyze(context, problemAnalysis) {
1746
+ const guiFlowEndUser = [];
1747
+ const guiFlowInternal = [];
1748
+ const userFeelings = [];
1749
+ const performance = [];
1750
+ const security = [];
1751
+ const immutableRequirements = [];
1752
+ // Analyze visible impacts
1753
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
1754
+ const forms = context.domMetrics?.forms || 0;
1755
+ if (interactiveElements > 0) {
1756
+ guiFlowEndUser.push(`${interactiveElements} interactive elements affect user journey`);
1757
+ }
1758
+ if (forms > 0) {
1759
+ guiFlowEndUser.push(`${forms} forms impact user input flows`);
1760
+ }
1761
+ // User feelings based on quality metrics
1762
+ const altCoverage = context.accessibility?.altTextsCoverage || 0;
1763
+ if (altCoverage > 80) {
1764
+ userFeelings.push('Positive - Good accessibility creates inclusive experience');
1765
+ }
1766
+ else if (altCoverage < 50) {
1767
+ userFeelings.push('Frustrated - Poor accessibility excludes some users');
1768
+ }
1769
+ const loadTime = context.performance?.loadTime || 0;
1770
+ if (loadTime > 3000) {
1771
+ userFeelings.push('Impatient - Slow load time causes frustration');
1772
+ }
1773
+ else if (loadTime < 2000) {
1774
+ userFeelings.push('Satisfied - Fast load time enhances experience');
1775
+ }
1776
+ if (context.errorIndicators?.hasErrorMessages) {
1777
+ userFeelings.push('Confused - Visible errors reduce confidence');
1778
+ }
1779
+ // Analyze invisible impacts
1780
+ if (loadTime > 2000) {
1781
+ performance.push(`Load time ${loadTime}ms impacts user retention`);
1782
+ }
1783
+ if (!context.metadata?.viewport) {
1784
+ performance.push('Missing viewport tag affects mobile performance');
1785
+ }
1786
+ // Immutable requirements
1787
+ if (context.domMetrics?.semanticStructure?.hasMain) {
1788
+ immutableRequirements.push('Must maintain main content accessibility');
1789
+ }
1790
+ if (context.accessibility && (context.accessibility.focusableElementsCount || 0) > 0) {
1791
+ immutableRequirements.push('Must support keyboard navigation');
1792
+ }
1793
+ if (problemAnalysis.complexity === 'complex') {
1794
+ immutableRequirements.push('Must maintain system stability with complex interactions');
1795
+ }
1796
+ // Calculate impact scores
1797
+ let visibleScore = 50;
1798
+ if (guiFlowEndUser.length > 0)
1799
+ visibleScore += 15;
1800
+ if (userFeelings.some(f => f.includes('Positive') || f.includes('Satisfied')))
1801
+ visibleScore += 20;
1802
+ if (userFeelings.some(f => f.includes('Frustrated') || f.includes('Confused')))
1803
+ visibleScore -= 15;
1804
+ visibleScore = Math.max(0, Math.min(100, visibleScore));
1805
+ let invisibleScore = 50;
1806
+ if (performance.length === 0)
1807
+ invisibleScore += 20;
1808
+ if (security.length === 0)
1809
+ invisibleScore += 10;
1810
+ invisibleScore = Math.max(0, Math.min(100, invisibleScore));
1811
+ const overallImpactScore = Math.round((visibleScore + invisibleScore) / 2);
752
1812
  return {
753
1813
  visible: {
754
1814
  guiFlow: {
755
- forEndUser: [],
756
- forInternalUser: []
1815
+ forEndUser: guiFlowEndUser,
1816
+ forInternalUser: guiFlowInternal
757
1817
  },
758
- userFeelings: []
1818
+ userFeelings,
1819
+ score: visibleScore
759
1820
  },
760
1821
  invisible: {
761
- performance: [],
762
- security: []
1822
+ performance,
1823
+ security,
1824
+ score: invisibleScore
763
1825
  },
764
- immutableRequirements: [],
765
- overallImpactScore: 30 // Lower is better
1826
+ immutableRequirements,
1827
+ overallImpactScore
766
1828
  };
767
1829
  }
768
1830
  }