agentic-qe 2.0.0 → 2.1.1

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 (144) 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 +86 -0
  44. package/README.md +23 -16
  45. package/dist/agents/QXPartnerAgent.d.ts +47 -1
  46. package/dist/agents/QXPartnerAgent.d.ts.map +1 -1
  47. package/dist/agents/QXPartnerAgent.js +2086 -125
  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/memory/ReflexionMemoryAdapter.d.ts +109 -0
  60. package/dist/core/memory/ReflexionMemoryAdapter.d.ts.map +1 -0
  61. package/dist/core/memory/ReflexionMemoryAdapter.js +306 -0
  62. package/dist/core/memory/ReflexionMemoryAdapter.js.map +1 -0
  63. package/dist/core/memory/RuVectorPatternStore.d.ts +28 -0
  64. package/dist/core/memory/RuVectorPatternStore.d.ts.map +1 -1
  65. package/dist/core/memory/RuVectorPatternStore.js +70 -0
  66. package/dist/core/memory/RuVectorPatternStore.js.map +1 -1
  67. package/dist/core/memory/SparseVectorSearch.d.ts +55 -0
  68. package/dist/core/memory/SparseVectorSearch.d.ts.map +1 -0
  69. package/dist/core/memory/SparseVectorSearch.js +130 -0
  70. package/dist/core/memory/SparseVectorSearch.js.map +1 -0
  71. package/dist/core/memory/TieredCompression.d.ts +81 -0
  72. package/dist/core/memory/TieredCompression.d.ts.map +1 -0
  73. package/dist/core/memory/TieredCompression.js +270 -0
  74. package/dist/core/memory/TieredCompression.js.map +1 -0
  75. package/dist/core/memory/index.d.ts +6 -0
  76. package/dist/core/memory/index.d.ts.map +1 -1
  77. package/dist/core/memory/index.js +29 -1
  78. package/dist/core/memory/index.js.map +1 -1
  79. package/dist/core/metrics/MetricsAggregator.d.ts +228 -0
  80. package/dist/core/metrics/MetricsAggregator.d.ts.map +1 -0
  81. package/dist/core/metrics/MetricsAggregator.js +482 -0
  82. package/dist/core/metrics/MetricsAggregator.js.map +1 -0
  83. package/dist/core/metrics/index.d.ts +5 -0
  84. package/dist/core/metrics/index.d.ts.map +1 -0
  85. package/dist/core/metrics/index.js +11 -0
  86. package/dist/core/metrics/index.js.map +1 -0
  87. package/dist/core/optimization/SwarmOptimizer.d.ts +5 -0
  88. package/dist/core/optimization/SwarmOptimizer.d.ts.map +1 -1
  89. package/dist/core/optimization/SwarmOptimizer.js +17 -0
  90. package/dist/core/optimization/SwarmOptimizer.js.map +1 -1
  91. package/dist/core/orchestration/AdaptiveScheduler.d.ts +190 -0
  92. package/dist/core/orchestration/AdaptiveScheduler.d.ts.map +1 -0
  93. package/dist/core/orchestration/AdaptiveScheduler.js +460 -0
  94. package/dist/core/orchestration/AdaptiveScheduler.js.map +1 -0
  95. package/dist/core/orchestration/WorkflowOrchestrator.d.ts +13 -0
  96. package/dist/core/orchestration/WorkflowOrchestrator.d.ts.map +1 -1
  97. package/dist/core/orchestration/WorkflowOrchestrator.js +32 -0
  98. package/dist/core/orchestration/WorkflowOrchestrator.js.map +1 -1
  99. package/dist/core/recovery/CircuitBreaker.d.ts +176 -0
  100. package/dist/core/recovery/CircuitBreaker.d.ts.map +1 -0
  101. package/dist/core/recovery/CircuitBreaker.js +382 -0
  102. package/dist/core/recovery/CircuitBreaker.js.map +1 -0
  103. package/dist/core/recovery/RecoveryOrchestrator.d.ts +186 -0
  104. package/dist/core/recovery/RecoveryOrchestrator.d.ts.map +1 -0
  105. package/dist/core/recovery/RecoveryOrchestrator.js +476 -0
  106. package/dist/core/recovery/RecoveryOrchestrator.js.map +1 -0
  107. package/dist/core/recovery/RetryStrategy.d.ts +127 -0
  108. package/dist/core/recovery/RetryStrategy.d.ts.map +1 -0
  109. package/dist/core/recovery/RetryStrategy.js +314 -0
  110. package/dist/core/recovery/RetryStrategy.js.map +1 -0
  111. package/dist/core/recovery/index.d.ts +8 -0
  112. package/dist/core/recovery/index.d.ts.map +1 -0
  113. package/dist/core/recovery/index.js +27 -0
  114. package/dist/core/recovery/index.js.map +1 -0
  115. package/dist/core/skills/DependencyResolver.d.ts +99 -0
  116. package/dist/core/skills/DependencyResolver.d.ts.map +1 -0
  117. package/dist/core/skills/DependencyResolver.js +260 -0
  118. package/dist/core/skills/DependencyResolver.js.map +1 -0
  119. package/dist/core/skills/ManifestGenerator.d.ts +114 -0
  120. package/dist/core/skills/ManifestGenerator.d.ts.map +1 -0
  121. package/dist/core/skills/ManifestGenerator.js +449 -0
  122. package/dist/core/skills/ManifestGenerator.js.map +1 -0
  123. package/dist/core/skills/index.d.ts +9 -0
  124. package/dist/core/skills/index.d.ts.map +1 -0
  125. package/dist/core/skills/index.js +24 -0
  126. package/dist/core/skills/index.js.map +1 -0
  127. package/dist/mcp/handlers/chaos/chaos-inject-failure.d.ts +5 -0
  128. package/dist/mcp/handlers/chaos/chaos-inject-failure.d.ts.map +1 -1
  129. package/dist/mcp/handlers/chaos/chaos-inject-failure.js +36 -2
  130. package/dist/mcp/handlers/chaos/chaos-inject-failure.js.map +1 -1
  131. package/dist/mcp/handlers/chaos/chaos-inject-latency.d.ts +5 -0
  132. package/dist/mcp/handlers/chaos/chaos-inject-latency.d.ts.map +1 -1
  133. package/dist/mcp/handlers/chaos/chaos-inject-latency.js +36 -2
  134. package/dist/mcp/handlers/chaos/chaos-inject-latency.js.map +1 -1
  135. package/dist/mcp/server.d.ts +9 -9
  136. package/dist/mcp/server.d.ts.map +1 -1
  137. package/dist/mcp/server.js +1 -2
  138. package/dist/mcp/server.js.map +1 -1
  139. package/dist/types/qx.d.ts +113 -7
  140. package/dist/types/qx.d.ts.map +1 -1
  141. package/dist/types/qx.js.map +1 -1
  142. package/dist/visualization/api/RestEndpoints.js +1 -1
  143. package/dist/visualization/api/RestEndpoints.js.map +1 -1
  144. package/package.json +15 -54
@@ -16,11 +16,50 @@
16
16
  * - Integration with testability scoring
17
17
  * - Contextual recommendations generation
18
18
  */
19
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ var desc = Object.getOwnPropertyDescriptor(m, k);
22
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
23
+ desc = { enumerable: true, get: function() { return m[k]; } };
24
+ }
25
+ Object.defineProperty(o, k2, desc);
26
+ }) : (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ o[k2] = m[k];
29
+ }));
30
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
31
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32
+ }) : function(o, v) {
33
+ o["default"] = v;
34
+ });
35
+ var __importStar = (this && this.__importStar) || (function () {
36
+ var ownKeys = function(o) {
37
+ ownKeys = Object.getOwnPropertyNames || function (o) {
38
+ var ar = [];
39
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
40
+ return ar;
41
+ };
42
+ return ownKeys(o);
43
+ };
44
+ return function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
48
+ __setModuleDefault(result, mod);
49
+ return result;
50
+ };
51
+ })();
19
52
  Object.defineProperty(exports, "__esModule", { value: true });
20
53
  exports.QXPartnerAgent = void 0;
21
54
  const BaseAgent_1 = require("./BaseAgent");
22
55
  const types_1 = require("../types");
56
+ const playwright_1 = require("playwright");
57
+ const fs = __importStar(require("fs"));
58
+ const path = __importStar(require("path"));
59
+ const child_process_1 = require("child_process");
60
+ const util_1 = require("util");
23
61
  const qx_1 = require("../types/qx");
62
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
24
63
  class ConsoleLogger {
25
64
  info(message, ...args) {
26
65
  console.log(`[INFO] ${message}`, ...args);
@@ -47,6 +86,8 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
47
86
  };
48
87
  super(baseConfig);
49
88
  this.logger = new ConsoleLogger();
89
+ this.browser = null;
90
+ this.page = null;
50
91
  this.config = {
51
92
  analysisMode: config.analysisMode || 'full',
52
93
  heuristics: config.heuristics || {
@@ -192,6 +233,15 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
192
233
  async cleanup() {
193
234
  try {
194
235
  this.logger.info(`QXPartnerAgent ${this.agentId.id} cleaning up resources`);
236
+ // Close browser if open
237
+ if (this.page) {
238
+ await this.page.close();
239
+ this.page = null;
240
+ }
241
+ if (this.browser) {
242
+ await this.browser.close();
243
+ this.browser = null;
244
+ }
195
245
  // Save current QX analysis state
196
246
  await this.saveQXState();
197
247
  // Store learned patterns
@@ -252,14 +302,18 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
252
302
  const userNeeds = await this.analyzeUserNeeds(context, problemAnalysis);
253
303
  // 4. Analyze business needs
254
304
  const businessNeeds = await this.analyzeBusinessNeeds(context, problemAnalysis);
255
- // 5. Detect oracle problems
305
+ // 5. Analyze creativity - drawing inspiration from diverse domains
306
+ const creativityAnalysis = await this.analyzeCreativity(context, problemAnalysis);
307
+ // 6. Analyze design - exactness, intuitive, and counter-intuitive design
308
+ const designAnalysis = await this.analyzeDesign(context, problemAnalysis);
309
+ // 7. Detect oracle problems
256
310
  const oracleProblems = this.config.detectOracleProblems
257
311
  ? await this.detectOracleProblemsFromContext(context, userNeeds, businessNeeds)
258
312
  : [];
259
- // 6. Perform impact analysis
313
+ // 8. Perform impact analysis
260
314
  const impactAnalysis = await this.analyzeImpact(context, problemAnalysis);
261
- // 7. Apply heuristics
262
- const heuristics = await this.applyAllHeuristics(context, problemAnalysis, userNeeds, businessNeeds);
315
+ // 9. Apply heuristics
316
+ const heuristics = await this.applyAllHeuristics(context, problemAnalysis, userNeeds, businessNeeds, creativityAnalysis, designAnalysis);
263
317
  // 8. Integrate testability (if enabled)
264
318
  const testabilityIntegration = this.config.integrateTestability
265
319
  ? await this.integrateTestabilityScoring(params)
@@ -267,7 +321,7 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
267
321
  // 9. Generate recommendations
268
322
  const recommendations = await this.generateRecommendations(problemAnalysis, userNeeds, businessNeeds, oracleProblems, impactAnalysis, heuristics, testabilityIntegration);
269
323
  // 10. Calculate overall score
270
- const overallScore = this.calculateOverallQXScore(problemAnalysis, userNeeds, businessNeeds, impactAnalysis, heuristics);
324
+ const overallScore = this.calculateOverallQXScore(problemAnalysis, userNeeds, businessNeeds, creativityAnalysis, designAnalysis, impactAnalysis, heuristics);
271
325
  const grade = this.scoreToGrade(overallScore);
272
326
  const analysis = {
273
327
  overallScore,
@@ -277,6 +331,8 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
277
331
  problemAnalysis,
278
332
  userNeeds,
279
333
  businessNeeds,
334
+ creativityAnalysis,
335
+ designAnalysis,
280
336
  oracleProblems,
281
337
  impactAnalysis,
282
338
  heuristics,
@@ -288,66 +344,1385 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
288
344
  await this.storeMemory(`qx-analysis:${target}`, analysis);
289
345
  const duration = Date.now() - startTime;
290
346
  this.logger.info(`QX analysis completed in ${duration}ms. Score: ${overallScore}/100 (${grade})`);
347
+ // Generate HTML report and auto-launch
348
+ try {
349
+ const reportPath = await this.generateHTMLReport(analysis);
350
+ this.logger.info(`HTML report generated: ${reportPath}`);
351
+ // Auto-launch browser
352
+ await this.launchReportInBrowser(reportPath);
353
+ this.logger.info(`Report launched in browser`);
354
+ }
355
+ catch (error) {
356
+ this.logger.warn(`Failed to generate/launch HTML report:`, error);
357
+ // Don't fail the analysis if report generation fails
358
+ }
291
359
  return analysis;
292
360
  }
293
361
  /**
294
- * Collect QX context from target
362
+ * Generate HTML report from QX analysis
363
+ */
364
+ async generateHTMLReport(analysis) {
365
+ const sanitizedTarget = analysis.target.replace(/[^a-zA-Z0-9]/g, '-').substring(0, 50);
366
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19);
367
+ const filename = `qx-report-${sanitizedTarget}-${timestamp}.html`;
368
+ const reportsDir = path.join(process.cwd(), 'docs', 'qx-reports');
369
+ // Ensure reports directory exists
370
+ if (!fs.existsSync(reportsDir)) {
371
+ fs.mkdirSync(reportsDir, { recursive: true });
372
+ }
373
+ const reportPath = path.join(reportsDir, filename);
374
+ const html = this.generateHTMLContent(analysis);
375
+ fs.writeFileSync(reportPath, html, 'utf8');
376
+ return reportPath;
377
+ }
378
+ /**
379
+ * Generate HTML content for the report
380
+ */
381
+ generateHTMLContent(analysis) {
382
+ const date = analysis.timestamp.toLocaleDateString();
383
+ const time = analysis.timestamp.toLocaleTimeString();
384
+ return `<!DOCTYPE html>
385
+ <html lang="en">
386
+ <head>
387
+ <meta charset="UTF-8">
388
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
389
+ <title>QX Analysis: ${this.escapeHtml(analysis.target)}</title>
390
+ ${this.getReportStyles()}
391
+ </head>
392
+ <body>
393
+ <div class="container">
394
+ <div class="header">
395
+ <h1>🍵 Quality Experience (QX) Analysis</h1>
396
+ <div class="subtitle">${this.escapeHtml(analysis.target)}</div>
397
+ <div class="meta">
398
+ <strong>URL:</strong> ${this.escapeHtml(analysis.target)}<br>
399
+ <strong>Analysis Date:</strong> ${date} at ${time}<br>
400
+ <strong>Framework:</strong> QX Partner (Quality + UX Advocacy)
401
+ </div>
402
+ </div>
403
+
404
+ <div class="content">
405
+ <!-- Executive Summary -->
406
+ <div class="section">
407
+ <h2>📊 Executive Summary</h2>
408
+ <p>
409
+ This comprehensive QX analysis evaluates <strong>${this.escapeHtml(analysis.context.title || analysis.target)}</strong>
410
+ through the lens of Quality Experience, examining how quality is co-created for all stakeholders.
411
+ </p>
412
+ </div>
413
+
414
+ <!-- Overall QX Score -->
415
+ <div class="section">
416
+ <h2>🎯 Overall QX Score</h2>
417
+ <div class="score-card">
418
+ <div class="score-item">
419
+ <h4>Problem Clarity</h4>
420
+ <div class="score-value">${analysis.problemAnalysis.clarityScore}</div>
421
+ <div class="score-label">/ 100</div>
422
+ </div>
423
+ <div class="score-item">
424
+ <h4>User Needs Alignment</h4>
425
+ <div class="score-value">${analysis.userNeeds.alignmentScore}</div>
426
+ <div class="score-label">/ 100</div>
427
+ </div>
428
+ <div class="score-item">
429
+ <h4>Business Alignment</h4>
430
+ <div class="score-value">${analysis.businessNeeds.alignmentScore}</div>
431
+ <div class="score-label">/ 100</div>
432
+ </div>
433
+ <div class="score-item">
434
+ <h4>Impact Assessment</h4>
435
+ <div class="score-value">${100 - analysis.impactAnalysis.overallImpactScore}</div>
436
+ <div class="score-label">/ 100</div>
437
+ </div>
438
+ </div>
439
+ <div style="text-align: center; margin-top: 30px;">
440
+ <div class="score-value" style="font-size: 3em; color: ${this.getScoreColor(analysis.overallScore)};">${analysis.overallScore}</div>
441
+ <div class="score-label" style="font-size: 1.2em;">OVERALL QX SCORE (Grade: ${analysis.grade})</div>
442
+ </div>
443
+ </div>
444
+
445
+ <!-- Problem Analysis -->
446
+ <div class="section">
447
+ <h2>🔍 Problem Analysis</h2>
448
+ <div class="info-box">
449
+ <h3>Problem Statement</h3>
450
+ <p>${this.escapeHtml(analysis.problemAnalysis.problemStatement)}</p>
451
+ <p><strong>Complexity:</strong> ${analysis.problemAnalysis.complexity}</p>
452
+ <p><strong>Clarity Score:</strong> ${analysis.problemAnalysis.clarityScore}/100</p>
453
+ </div>
454
+ ${analysis.problemAnalysis.potentialFailures.length > 0 ? `
455
+ <div class="improvements">
456
+ <h3>⚠️ Potential Failure Modes</h3>
457
+ <ul>
458
+ ${analysis.problemAnalysis.potentialFailures.map(f => `
459
+ <li>
460
+ <strong>[${f.severity.toUpperCase()}]</strong> ${this.escapeHtml(f.description)}
461
+ <br><small>Likelihood: ${f.likelihood}</small>
462
+ </li>
463
+ `).join('')}
464
+ </ul>
465
+ </div>
466
+ ` : ''}
467
+ </div>
468
+
469
+ <!-- User Needs -->
470
+ <div class="section">
471
+ <h2>👥 User Needs Analysis</h2>
472
+ <p><strong>Suitability:</strong> ${analysis.userNeeds.suitability} | <strong>Score:</strong> ${analysis.userNeeds.alignmentScore}/100</p>
473
+ ${analysis.userNeeds.needs.length > 0 ? `
474
+ <div class="strengths">
475
+ <h3>✅ User Needs</h3>
476
+ <ul>
477
+ ${analysis.userNeeds.needs.map(n => `
478
+ <li>
479
+ <strong>[${n.priority}]</strong> ${this.escapeHtml(n.description)}
480
+ ${n.addressed ? '✓ Addressed' : '✗ Not Addressed'}
481
+ </li>
482
+ `).join('')}
483
+ </ul>
484
+ </div>
485
+ ` : ''}
486
+ ${analysis.userNeeds.challenges.length > 0 ? `
487
+ <div class="improvements">
488
+ <h3>⚠️ User Challenges</h3>
489
+ <ul>
490
+ ${analysis.userNeeds.challenges.map(c => `<li>${this.escapeHtml(c)}</li>`).join('')}
491
+ </ul>
492
+ </div>
493
+ ` : ''}
494
+ </div>
495
+
496
+ <!-- Business Needs -->
497
+ <div class="section">
498
+ <h2>💼 Business Needs Analysis</h2>
499
+ <p><strong>Primary Goal:</strong> ${analysis.businessNeeds.primaryGoal}</p>
500
+ <p><strong>Compromises UX:</strong> ${analysis.businessNeeds.compromisesUX ? 'Yes ⚠️' : 'No ✓'}</p>
501
+ ${analysis.businessNeeds.kpisAffected.length > 0 ? `
502
+ <div class="info-box">
503
+ <h3>KPIs Affected</h3>
504
+ <ul>
505
+ ${analysis.businessNeeds.kpisAffected.map(k => `<li>${this.escapeHtml(k)}</li>`).join('')}
506
+ </ul>
507
+ </div>
508
+ ` : ''}
509
+ </div>
510
+
511
+ <!-- Creativity Analysis -->
512
+ <div class="section">
513
+ <h2>🎨 Creativity & Innovation Analysis</h2>
514
+ <p><strong>Creativity Score:</strong> ${analysis.creativityAnalysis.creativityScore}/100</p>
515
+ <p><strong>Domains Explored:</strong> ${analysis.creativityAnalysis.domainsExplored.join(', ')}</p>
516
+ ${analysis.creativityAnalysis.innovativeApproaches.length > 0 ? `
517
+ <div class="strengths">
518
+ <h3>✨ Innovative Testing Approaches</h3>
519
+ <ul>
520
+ ${analysis.creativityAnalysis.innovativeApproaches.map(a => `
521
+ <li>
522
+ <strong>[${a.inspirationSource.toUpperCase()}]</strong> ${this.escapeHtml(a.description)}
523
+ <br><small>Applicability: ${a.applicability} | Novelty: ${a.novelty}</small>
524
+ </li>
525
+ `).join('')}
526
+ </ul>
527
+ </div>
528
+ ` : ''}
529
+ ${analysis.creativityAnalysis.perspectives.length > 0 ? `
530
+ <div class="info-box">
531
+ <h3>🔍 Testing Perspectives Applied</h3>
532
+ <ul>
533
+ ${analysis.creativityAnalysis.perspectives.map(p => `<li>${this.escapeHtml(p)}</li>`).join('')}
534
+ </ul>
535
+ </div>
536
+ ` : ''}
537
+ </div>
538
+
539
+ <!-- Design Quality Analysis -->
540
+ <div class="section">
541
+ <h2>🎯 Design Quality Analysis</h2>
542
+ <p><strong>Overall Design Score:</strong> ${analysis.designAnalysis.overallDesignScore}/100</p>
543
+
544
+ <!-- Exactness -->
545
+ <div class="info-box" style="margin-top: 20px;">
546
+ <h3>📏 Exactness & Clarity (${analysis.designAnalysis.exactness.score}/100)</h3>
547
+ <p><strong>Clarity Level:</strong> ${analysis.designAnalysis.exactness.clarity}</p>
548
+ ${analysis.designAnalysis.exactness.clearElements.length > 0 ? `
549
+ <div style="margin-top: 15px;">
550
+ <strong>✓ Clear Elements:</strong>
551
+ <ul>
552
+ ${analysis.designAnalysis.exactness.clearElements.map(e => `<li>${this.escapeHtml(e)}</li>`).join('')}
553
+ </ul>
554
+ </div>
555
+ ` : ''}
556
+ ${analysis.designAnalysis.exactness.unclearElements.length > 0 ? `
557
+ <div style="margin-top: 15px;">
558
+ <strong>⚠️ Unclear Elements:</strong>
559
+ <ul>
560
+ ${analysis.designAnalysis.exactness.unclearElements.map(e => `<li>${this.escapeHtml(e)}</li>`).join('')}
561
+ </ul>
562
+ </div>
563
+ ` : ''}
564
+ </div>
565
+
566
+ <!-- Intuitive Design -->
567
+ <div class="strengths" style="margin-top: 20px;">
568
+ <h3>🧭 Intuitive Design (${analysis.designAnalysis.intuitive.score}/100)</h3>
569
+ <p><strong>Follows Conventions:</strong> ${analysis.designAnalysis.intuitive.followsConventions ? 'Yes ✓' : 'No ⚠️'}</p>
570
+ ${analysis.designAnalysis.intuitive.intuitivePatterns.length > 0 ? `
571
+ <div style="margin-top: 15px;">
572
+ <strong>Intuitive Patterns Detected:</strong>
573
+ <ul>
574
+ ${analysis.designAnalysis.intuitive.intuitivePatterns.map(p => `<li>${this.escapeHtml(p)}</li>`).join('')}
575
+ </ul>
576
+ </div>
577
+ ` : ''}
578
+ ${analysis.designAnalysis.intuitive.culturalIssues.length > 0 ? `
579
+ <div style="margin-top: 15px;">
580
+ <strong>⚠️ Cultural Considerations:</strong>
581
+ <ul>
582
+ ${analysis.designAnalysis.intuitive.culturalIssues.map(i => `<li>${this.escapeHtml(i)}</li>`).join('')}
583
+ </ul>
584
+ </div>
585
+ ` : ''}
586
+ </div>
587
+
588
+ <!-- Counter-intuitive Design -->
589
+ ${analysis.designAnalysis.counterIntuitive.deviations.length > 0 ? `
590
+ <div class="improvements" style="margin-top: 20px;">
591
+ <h3>🔄 Counter-intuitive Design Patterns</h3>
592
+ <p><strong>Issues Found:</strong> ${analysis.designAnalysis.counterIntuitive.issuesCount}</p>
593
+ <p><strong>Fresh Eyes Perspective Applied:</strong> ${analysis.designAnalysis.counterIntuitive.freshEyesPerspective ? 'Yes ✓' : 'No'}</p>
594
+ <ul>
595
+ ${analysis.designAnalysis.counterIntuitive.deviations.map(d => `
596
+ <li>
597
+ <strong>${this.escapeHtml(d.element)}</strong>
598
+ <br>Expected: ${this.escapeHtml(d.expectedBehavior)}
599
+ <br>Actual: ${this.escapeHtml(d.actualBehavior)}
600
+ <br><small>Impact: ${d.impact} ${d.justification ? '| ' + this.escapeHtml(d.justification) : ''}</small>
601
+ </li>
602
+ `).join('')}
603
+ </ul>
604
+ </div>
605
+ ` : ''}
606
+ </div>
607
+
608
+ <!-- Oracle Problems -->
609
+ ${analysis.oracleProblems.length > 0 ? `
610
+ <div class="section">
611
+ <h2>🔮 Oracle Problems Detected</h2>
612
+ ${analysis.oracleProblems.map(p => `
613
+ <div class="improvements">
614
+ <h3>[${p.severity.toUpperCase()}] ${p.type}</h3>
615
+ <p>${this.escapeHtml(p.description)}</p>
616
+ ${p.stakeholders ? `<p><strong>Stakeholders:</strong> ${p.stakeholders.join(', ')}</p>` : ''}
617
+ ${p.resolutionApproach ? `
618
+ <p><strong>Resolution Approach:</strong></p>
619
+ <ul>
620
+ ${p.resolutionApproach.map(r => `<li>${this.escapeHtml(r)}</li>`).join('')}
621
+ </ul>
622
+ ` : ''}
623
+ </div>
624
+ `).join('')}
625
+ </div>
626
+ ` : ''}
627
+
628
+ <!-- Recommendations -->
629
+ <div class="section">
630
+ <h2>💡 Strategic Recommendations</h2>
631
+ ${analysis.recommendations.slice(0, 10).map((rec, idx) => `
632
+ <div class="recommendations">
633
+ <h3>🎯 Priority ${idx + 1}: ${this.escapeHtml(rec.principle)}</h3>
634
+ <p>${this.escapeHtml(rec.recommendation)}</p>
635
+ <p>
636
+ <strong>Severity:</strong> ${rec.severity} |
637
+ <strong>Impact:</strong> ${rec.impactPercentage || rec.impact}% |
638
+ <strong>Effort:</strong> ${rec.estimatedEffort || rec.effort}
639
+ </p>
640
+ </div>
641
+ `).join('')}
642
+ </div>
643
+
644
+ <!-- Heuristics Results -->
645
+ ${analysis.heuristics.length > 0 ? `
646
+ <div class="section">
647
+ <h2>📐 Heuristics Analysis</h2>
648
+ <div class="score-card">
649
+ ${analysis.heuristics.slice(0, 8).map(h => `
650
+ <div class="score-item">
651
+ <h4>${this.formatHeuristicName(h.name)}</h4>
652
+ <div class="score-value" style="font-size: 1.8em; color: ${this.getScoreColor(h.score)};">${h.score}</div>
653
+ <div class="score-label">/ 100</div>
654
+ </div>
655
+ `).join('')}
656
+ </div>
657
+ </div>
658
+ ` : ''}
659
+
660
+ <!-- Impact Analysis -->
661
+ <div class="section">
662
+ <h2>⚡ Impact Analysis</h2>
663
+ <div class="score-card">
664
+ <div class="score-item">
665
+ <h4>Visible Impact</h4>
666
+ <div class="score-value">${analysis.impactAnalysis.visible.score}</div>
667
+ <div class="score-label">/ 100</div>
668
+ </div>
669
+ <div class="score-item">
670
+ <h4>Invisible Impact</h4>
671
+ <div class="score-value">${analysis.impactAnalysis.invisible.score}</div>
672
+ <div class="score-label">/ 100</div>
673
+ </div>
674
+ </div>
675
+ ${analysis.impactAnalysis.visible.userFeelings && analysis.impactAnalysis.visible.userFeelings.length > 0 ? `
676
+ <div class="info-box">
677
+ <h3>User Feelings</h3>
678
+ <ul>
679
+ ${analysis.impactAnalysis.visible.userFeelings.map(f => {
680
+ if (typeof f === 'string') {
681
+ return `<li>${this.escapeHtml(f)}</li>`;
682
+ }
683
+ else {
684
+ return `<li><strong>${f.feeling}</strong> (${f.likelihood}): ${this.escapeHtml(f.context)}</li>`;
685
+ }
686
+ }).join('')}
687
+ </ul>
688
+ </div>
689
+ ` : ''}
690
+ </div>
691
+
692
+ <!-- Conclusion -->
693
+ <div class="section">
694
+ <h2>🎓 Conclusion</h2>
695
+ <p>
696
+ This QX analysis reveals an overall score of <strong>${analysis.overallScore}/100 (Grade: ${analysis.grade})</strong>.
697
+ ${this.getScoreInterpretation(analysis.overallScore)}
698
+ </p>
699
+ </div>
700
+
701
+ <!-- QX Methodology -->
702
+ <div class="section" style="background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%); padding: 30px; border-radius: 8px; margin-top: 40px;">
703
+ <h2>📚 QX Methodology: Key Concepts</h2>
704
+
705
+ <div style="margin-top: 20px;">
706
+ <h3 style="color: #667eea; margin-bottom: 10px;">🤝 What is Quality Experience (QX)?</h3>
707
+ <p style="text-align: justify;">
708
+ <strong>Quality Experience (QX)</strong> is the marriage between <strong>Quality Advocacy (QA)</strong> and
709
+ <strong>User Experience (UX)</strong>. Unlike traditional testing that focuses solely on defects, QX recognizes
710
+ that quality is co-created by everyone associated with the product—developers, testers, designers, users, and
711
+ business stakeholders. QX enables testers to collaborate meaningfully with UX professionals by understanding
712
+ design effectiveness beyond technical correctness.
713
+ </p>
714
+ </div>
715
+
716
+ <div style="margin-top: 25px;">
717
+ <h3 style="color: #667eea; margin-bottom: 10px;">🎨 Creativity in Testing</h3>
718
+ <p style="text-align: justify;">
719
+ QX encourages drawing inspiration from <strong>diverse domains</strong>—philosophy, social science, medicine,
720
+ e-commerce, fashion, gaming—to generate innovative test ideas when conventional approaches fall short. This
721
+ cross-disciplinary perspective helps testers uncover unconventional risks and approach problems from fresh angles
722
+ that technical testing alone might miss.
723
+ </p>
724
+ </div>
725
+
726
+ <div style="margin-top: 25px;">
727
+ <h3 style="color: #667eea; margin-bottom: 10px;">📏 Exactness & Clarity</h3>
728
+ <p style="text-align: justify;">
729
+ <strong>Exactness</strong> evaluates how clearly a product communicates its intent to users. Testers should assess
730
+ whether menu items, buttons, labels, and terminology are self-evident. Are component interactions obvious? Do users
731
+ understand what will happen when they click? Exactness testing identifies ambiguities that create confusion, focusing
732
+ on <em>clarity of communication</em> rather than just functional correctness.
733
+ </p>
734
+ </div>
735
+
736
+ <div style="margin-top: 25px;">
737
+ <h3 style="color: #667eea; margin-bottom: 10px;">🧭 Intuitive Design</h3>
738
+ <p style="text-align: justify;">
739
+ <strong>Intuitive design</strong> follows common conventions and user expectations. QX testing evaluates whether
740
+ component interactions follow familiar patterns, respect cultural sensitivities, and align with mental models users
741
+ bring from other products. Intuitive design reduces cognitive load and makes products immediately usable without
742
+ extensive training.
743
+ </p>
744
+ </div>
745
+
746
+ <div style="margin-top: 25px;">
747
+ <h3 style="color: #667eea; margin-bottom: 10px;">🔄 Counter-intuitive Design Detection</h3>
748
+ <p style="text-align: justify;">
749
+ QX testing deliberately <strong>looks at products like an unexperienced user</strong> to spot design elements that
750
+ deviate from expectations. Counter-intuitive patterns aren't always bad—they might represent innovation—but they
751
+ require justification. The key is distinguishing between deliberate, valuable innovations and accidental friction
752
+ that experienced users have normalized but newcomers will struggle with.
753
+ </p>
754
+ </div>
755
+
756
+ <div style="margin-top: 25px; padding: 20px; background: white; border-left: 4px solid #667eea; border-radius: 4px;">
757
+ <p style="text-align: justify; font-style: italic;">
758
+ <strong>The QX Advantage:</strong> By combining quality advocacy with UX design thinking, QX enables testers to
759
+ contribute informed insights about user-facing quality, bridge the gap between QA and UX teams, and ensure that
760
+ testing serves the genuine experience quality that all stakeholders care about—not just technical correctness.
761
+ </p>
762
+ </div>
763
+
764
+ <div style="margin-top: 20px; text-align: center;">
765
+ <p style="font-size: 0.9em;">
766
+ <strong>Learn more:</strong>
767
+ <a href="https://talesoftesting.com/quality-experienceqx-co-creating-quality-experience-for-everyone-associated-with-the-product/"
768
+ target="_blank" rel="noopener noreferrer" style="color: #667eea; text-decoration: none;">
769
+ Quality Experience (QX) - Tales of Testing
770
+ </a>
771
+ </p>
772
+ </div>
773
+ </div>
774
+ </div>
775
+
776
+ <div class="footer">
777
+ <p><strong>QX Analysis Report</strong></p>
778
+ <p>Generated by: Agentic QE Fleet v2.1.0 - QX Partner Agent</p>
779
+ <p>Framework: Quality Experience (QX) Analysis</p>
780
+ <p>Analysis Date: ${date} at ${time}</p>
781
+ <hr style="margin: 20px 0; border: none; border-top: 1px solid #dee2e6;">
782
+ <p style="font-size: 0.9em; line-height: 1.6;">
783
+ This report applies the QX framework developed to bridge Quality Advocacy (QA) and User Experience (UX).<br>
784
+ <em>"Quality is value to someone who matters"</em> - when multiple stakeholders matter simultaneously,<br>
785
+ QX helps find balance and solve oracle problems. It is one of the key concepts originally covered in
786
+ <a href="https://talesoftesting.com/qcsd/" target="_blank" rel="noopener noreferrer" style="color: #007bff; text-decoration: none;">
787
+ QCSD - Quality Conscious Software Delivery Framework</a> created by Lalitkumar Bhamare.
788
+ </p>
789
+ </div>
790
+ </div>
791
+ </body>
792
+ </html>`;
793
+ }
794
+ /**
795
+ * Get CSS styles for the report
796
+ */
797
+ getReportStyles() {
798
+ return `
799
+ <style>
800
+ * { margin: 0; padding: 0; box-sizing: border-box; }
801
+ body {
802
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
803
+ line-height: 1.6;
804
+ color: #333;
805
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
806
+ padding: 20px;
807
+ }
808
+ .container {
809
+ max-width: 1200px;
810
+ margin: 0 auto;
811
+ background: white;
812
+ border-radius: 12px;
813
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
814
+ overflow: hidden;
815
+ }
816
+ .header {
817
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
818
+ color: white;
819
+ padding: 40px;
820
+ text-align: center;
821
+ }
822
+ .header h1 { font-size: 2.5em; margin-bottom: 10px; font-weight: 700; }
823
+ .header .subtitle { font-size: 1.2em; opacity: 0.9; }
824
+ .header .meta { margin-top: 20px; font-size: 0.9em; opacity: 0.8; }
825
+ .content { padding: 40px; }
826
+ .section { margin-bottom: 40px; }
827
+ .section h2 {
828
+ color: #667eea;
829
+ font-size: 1.8em;
830
+ margin-bottom: 20px;
831
+ padding-bottom: 10px;
832
+ border-bottom: 3px solid #667eea;
833
+ }
834
+ .section h3 { color: #764ba2; font-size: 1.4em; margin: 25px 0 15px 0; }
835
+ .section p { margin-bottom: 15px; text-align: justify; }
836
+ .score-card {
837
+ display: grid;
838
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
839
+ gap: 20px;
840
+ margin: 30px 0;
841
+ }
842
+ .score-item {
843
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
844
+ padding: 20px;
845
+ border-radius: 8px;
846
+ text-align: center;
847
+ transition: transform 0.3s ease;
848
+ }
849
+ .score-item:hover { transform: translateY(-5px); box-shadow: 0 5px 20px rgba(0,0,0,0.1); }
850
+ .score-item h4 { color: #667eea; font-size: 1.1em; margin-bottom: 10px; }
851
+ .score-value {
852
+ font-size: 2.5em;
853
+ font-weight: bold;
854
+ color: #764ba2;
855
+ margin: 10px 0;
856
+ }
857
+ .score-label {
858
+ font-size: 0.9em;
859
+ color: #666;
860
+ text-transform: uppercase;
861
+ letter-spacing: 1px;
862
+ }
863
+ .strengths, .improvements, .recommendations, .info-box {
864
+ background: #f8f9fa;
865
+ padding: 25px;
866
+ border-radius: 8px;
867
+ margin: 20px 0;
868
+ }
869
+ .strengths { border-left: 5px solid #28a745; }
870
+ .improvements { border-left: 5px solid #ffc107; }
871
+ .recommendations { border-left: 5px solid #17a2b8; }
872
+ .info-box { border-left: 5px solid #6c757d; }
873
+ .strengths h3 { color: #28a745; }
874
+ .improvements h3 { color: #ffc107; }
875
+ .recommendations h3 { color: #17a2b8; }
876
+ .info-box h3 { color: #6c757d; }
877
+ ul { margin-left: 20px; margin-top: 10px; }
878
+ li { margin-bottom: 10px; line-height: 1.8; }
879
+ .footer {
880
+ background: #f8f9fa;
881
+ padding: 30px;
882
+ text-align: center;
883
+ color: #666;
884
+ border-top: 1px solid #e0e0e0;
885
+ }
886
+ .footer p { margin-bottom: 10px; }
887
+ @media print {
888
+ body { background: white; padding: 0; }
889
+ .container { box-shadow: none; }
890
+ }
891
+ </style>`;
892
+ }
893
+ /**
894
+ * Escape HTML special characters
895
+ */
896
+ escapeHtml(text) {
897
+ const map = {
898
+ '&': '&amp;',
899
+ '<': '&lt;',
900
+ '>': '&gt;',
901
+ '"': '&quot;',
902
+ "'": '&#039;'
903
+ };
904
+ return text.replace(/[&<>"']/g, m => map[m]);
905
+ }
906
+ /**
907
+ * Format heuristic name for display
908
+ */
909
+ formatHeuristicName(name) {
910
+ return name
911
+ .split('-')
912
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
913
+ .join(' ');
914
+ }
915
+ /**
916
+ * Get color based on score
917
+ */
918
+ getScoreColor(score) {
919
+ if (score >= 90)
920
+ return '#28a745'; // Green
921
+ if (score >= 80)
922
+ return '#17a2b8'; // Blue
923
+ if (score >= 70)
924
+ return '#ffc107'; // Yellow
925
+ if (score >= 60)
926
+ return '#fd7e14'; // Orange
927
+ return '#dc3545'; // Red
928
+ }
929
+ /**
930
+ * Get score interpretation
931
+ */
932
+ getScoreInterpretation(score) {
933
+ if (score >= 90) {
934
+ return 'Excellent quality experience with strong alignment across all dimensions.';
935
+ }
936
+ else if (score >= 80) {
937
+ return 'Good quality experience with minor areas for improvement.';
938
+ }
939
+ else if (score >= 70) {
940
+ return 'Adequate quality experience but significant improvements recommended.';
941
+ }
942
+ else if (score >= 60) {
943
+ return 'Below target quality experience. Priority improvements required.';
944
+ }
945
+ else {
946
+ return 'Poor quality experience. Immediate action needed across multiple areas.';
947
+ }
948
+ }
949
+ /**
950
+ * Launch report in default browser
951
+ */
952
+ async launchReportInBrowser(reportPath) {
953
+ try {
954
+ const platform = process.platform;
955
+ let command;
956
+ if (platform === 'darwin') {
957
+ command = `open "${reportPath}"`;
958
+ }
959
+ else if (platform === 'win32') {
960
+ command = `start "" "${reportPath}"`;
961
+ }
962
+ else {
963
+ // Linux and others
964
+ command = `xdg-open "${reportPath}"`;
965
+ }
966
+ await execAsync(command);
967
+ this.logger.info(`Launched report in browser: ${reportPath}`);
968
+ }
969
+ catch (error) {
970
+ this.logger.warn(`Failed to auto-launch browser:`, error);
971
+ this.logger.info(`Report available at: ${reportPath}`);
972
+ }
973
+ }
974
+ /**
975
+ * Collect QX context from target using Playwright
295
976
  */
296
977
  async collectQXContext(target, additionalContext) {
297
978
  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;
979
+ try {
980
+ // Launch browser if not already running
981
+ if (!this.browser) {
982
+ this.logger.debug('Launching browser...');
983
+ this.browser = await playwright_1.chromium.launch({
984
+ headless: true,
985
+ timeout: 30000, // 30 second timeout for launch
986
+ args: [
987
+ '--no-sandbox',
988
+ '--disable-setuid-sandbox',
989
+ '--disable-dev-shm-usage',
990
+ '--disable-accelerated-2d-canvas',
991
+ '--no-first-run',
992
+ '--no-zygote',
993
+ '--single-process', // Important for containers
994
+ '--disable-gpu'
995
+ ]
996
+ });
997
+ }
998
+ // Create new page
999
+ this.page = await this.browser.newPage();
1000
+ this.logger.debug(`Navigating to ${target}...`);
1001
+ // Navigate to target with timeout - try quick load first
1002
+ try {
1003
+ await this.page.goto(target, { waitUntil: 'commit', timeout: 15000 });
1004
+ }
1005
+ catch (navError) {
1006
+ this.logger.warn(`Quick navigation failed, trying basic load: ${navError}`);
1007
+ // Fallback: just navigate without waiting
1008
+ await this.page.goto(target, { waitUntil: 'commit', timeout: 10000 });
1009
+ }
1010
+ // Wait a bit for some content to load
1011
+ await this.page.waitForTimeout(1000);
1012
+ this.logger.debug('Extracting page context...');
1013
+ // Extract page context WITH ACTUAL CONTENT for contextual analysis
1014
+ const pageContext = await this.page.evaluate(() => {
1015
+ const countElements = (selector) => document.querySelectorAll(selector).length;
1016
+ const getText = (selector, limit = 5) => Array.from(document.querySelectorAll(selector)).slice(0, limit).map(el => el.textContent?.trim() || '').filter(t => t.length > 0);
1017
+ // Extract navigation items for context understanding
1018
+ const navItems = getText('nav a, nav button, [role="navigation"] a');
1019
+ const headings = {
1020
+ h1: getText('h1', 3),
1021
+ h2: getText('h2', 5),
1022
+ h3: getText('h3', 5)
1023
+ };
1024
+ // Extract form purposes from labels/placeholders
1025
+ const formPurposes = Array.from(document.querySelectorAll('form')).map(form => {
1026
+ const labels = Array.from(form.querySelectorAll('label, input[placeholder]')).slice(0, 3);
1027
+ return labels.map(el => el.placeholder || el.textContent?.trim() || '').filter(t => t.length > 0).join(', ');
1028
+ });
1029
+ // Extract button purposes
1030
+ const buttonPurposes = getText('button, [role="button"], input[type="submit"]', 10);
1031
+ // Extract link context (first 20 meaningful links)
1032
+ const linkTexts = getText('a[href]:not([href="#"]):not([href=""])', 20);
1033
+ // Extract main content snippets for purpose understanding
1034
+ const mainContent = document.querySelector('main, article, [role="main"]');
1035
+ const contentSnippet = mainContent?.textContent?.trim().substring(0, 300) || '';
1036
+ return {
1037
+ title: document.title,
1038
+ url: window.location.href,
1039
+ viewport: {
1040
+ width: window.innerWidth,
1041
+ height: window.innerHeight
1042
+ },
1043
+ content: {
1044
+ headings,
1045
+ navigationItems: navItems,
1046
+ buttonPurposes,
1047
+ formPurposes,
1048
+ linkTexts,
1049
+ mainContentSnippet: contentSnippet
1050
+ },
1051
+ elements: {
1052
+ total: document.querySelectorAll('*').length,
1053
+ buttons: countElements('button, [role="button"], input[type="button"], input[type="submit"]'),
1054
+ forms: countElements('form'),
1055
+ inputs: countElements('input, textarea, select'),
1056
+ links: countElements('a'),
1057
+ headings: {
1058
+ h1: countElements('h1'),
1059
+ h2: countElements('h2'),
1060
+ h3: countElements('h3')
1061
+ },
1062
+ images: countElements('img'),
1063
+ videos: countElements('video'),
1064
+ iframes: countElements('iframe')
1065
+ },
1066
+ semantic: {
1067
+ hasNav: countElements('nav') > 0,
1068
+ hasHeader: countElements('header') > 0,
1069
+ hasFooter: countElements('footer') > 0,
1070
+ hasMain: countElements('main') > 0,
1071
+ hasAside: countElements('aside') > 0,
1072
+ hasArticle: countElements('article') > 0,
1073
+ hasSection: countElements('section') > 0
1074
+ },
1075
+ accessibility: {
1076
+ ariaLabels: countElements('[aria-label]'),
1077
+ ariaDescriptions: countElements('[aria-describedby]'),
1078
+ altTexts: Array.from(document.querySelectorAll('img')).filter(img => img.hasAttribute('alt')).length,
1079
+ totalImages: countElements('img'),
1080
+ landmarkRoles: countElements('[role="banner"], [role="navigation"], [role="main"], [role="complementary"], [role="contentinfo"]'),
1081
+ focusableElements: countElements('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])')
1082
+ },
1083
+ errors: {
1084
+ consoleErrors: window.__errors || [],
1085
+ hasErrorMessages: countElements('.error, [role="alert"], .alert-danger, .text-danger') > 0
1086
+ },
1087
+ meta: {
1088
+ description: document.querySelector('meta[name="description"]')?.content || '',
1089
+ keywords: document.querySelector('meta[name="keywords"]')?.content || '',
1090
+ viewport: document.querySelector('meta[name="viewport"]')?.content || ''
1091
+ }
1092
+ };
1093
+ });
1094
+ // Capture performance metrics
1095
+ const performanceMetrics = await this.page.evaluate(() => {
1096
+ const perf = performance.getEntriesByType('navigation')[0];
1097
+ return {
1098
+ loadTime: perf?.loadEventEnd - perf?.fetchStart || 0,
1099
+ domReady: perf?.domContentLoadedEventEnd - perf?.fetchStart || 0,
1100
+ firstPaint: performance.getEntriesByType('paint').find(e => e.name === 'first-paint')?.startTime || 0
1101
+ };
1102
+ });
1103
+ const context = {
1104
+ url: target,
1105
+ title: pageContext.title,
1106
+ domMetrics: {
1107
+ totalElements: pageContext.elements.total,
1108
+ interactiveElements: pageContext.elements.buttons + pageContext.elements.inputs + pageContext.elements.links,
1109
+ forms: pageContext.elements.forms,
1110
+ inputs: pageContext.elements.inputs,
1111
+ buttons: pageContext.elements.buttons,
1112
+ semanticStructure: pageContext.semantic
1113
+ },
1114
+ accessibility: {
1115
+ ariaLabelsCount: pageContext.accessibility.ariaLabels,
1116
+ altTextsCoverage: pageContext.accessibility.totalImages > 0
1117
+ ? (pageContext.accessibility.altTexts / pageContext.accessibility.totalImages) * 100
1118
+ : 100,
1119
+ focusableElementsCount: pageContext.accessibility.focusableElements,
1120
+ landmarkRoles: pageContext.accessibility.landmarkRoles
1121
+ },
1122
+ performance: performanceMetrics,
1123
+ errorIndicators: pageContext.errors,
1124
+ metadata: pageContext.meta,
1125
+ custom: additionalContext || {}
1126
+ };
1127
+ // Close page but keep browser for potential reuse
1128
+ await this.page.close();
1129
+ this.page = null;
1130
+ // Store context for later retrieval
1131
+ await this.storeMemory(`qx-context:${target}`, context);
1132
+ this.logger.debug('Context collection completed successfully');
1133
+ return context;
1134
+ }
1135
+ catch (error) {
1136
+ this.logger.error(`Failed to collect QX context: ${error}`);
1137
+ // Clean up on error
1138
+ if (this.page) {
1139
+ try {
1140
+ await this.page.close();
1141
+ }
1142
+ catch (e) {
1143
+ // Ignore close errors
1144
+ }
1145
+ this.page = null;
1146
+ }
1147
+ // Return minimal context on error
1148
+ return {
1149
+ url: target,
1150
+ custom: additionalContext || {},
1151
+ error: error instanceof Error ? error.message : String(error)
1152
+ };
1153
+ }
1154
+ }
1155
+ /**
1156
+ * Detect domain-specific failure modes based on site type
1157
+ */
1158
+ detectDomainSpecificFailures(context, title, description, complexity) {
1159
+ const failures = [];
1160
+ const titleLower = title.toLowerCase();
1161
+ const descLower = description.toLowerCase();
1162
+ const forms = context.domMetrics?.forms || 0;
1163
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
1164
+ // E-commerce / Travel Booking sites
1165
+ if (titleLower.includes('hotel') || titleLower.includes('booking') || titleLower.includes('travel') ||
1166
+ titleLower.includes('shop') || titleLower.includes('store') || titleLower.includes('buy') ||
1167
+ descLower.includes('book') || descLower.includes('reservation') || descLower.includes('hotel')) {
1168
+ failures.push({
1169
+ description: 'Search and filter complexity may overwhelm users with too many options',
1170
+ severity: 'medium',
1171
+ likelihood: 'likely'
1172
+ });
1173
+ failures.push({
1174
+ description: 'Booking/checkout flow friction points may cause cart abandonment',
1175
+ severity: 'high',
1176
+ likelihood: 'likely'
1177
+ });
1178
+ failures.push({
1179
+ description: 'Price transparency issues or hidden fees may erode user trust',
1180
+ severity: 'high',
1181
+ likelihood: 'possible'
1182
+ });
1183
+ if (complexity === 'complex') {
1184
+ failures.push({
1185
+ description: 'Multi-step booking process may lose users if progress is not clearly indicated',
1186
+ severity: 'medium',
1187
+ likelihood: 'likely'
1188
+ });
1189
+ }
1190
+ }
1191
+ // Content/Blog/Magazine sites
1192
+ else if (titleLower.includes('blog') || titleLower.includes('article') || titleLower.includes('news') ||
1193
+ titleLower.includes('magazine') || titleLower.includes('testers') || titleLower.includes('testing')) {
1194
+ failures.push({
1195
+ description: 'Content discoverability - users may struggle to find relevant articles without robust search',
1196
+ severity: 'medium',
1197
+ likelihood: 'likely'
1198
+ });
1199
+ failures.push({
1200
+ description: 'Reading experience on mobile devices may not be optimized for long-form content',
1201
+ severity: 'medium',
1202
+ likelihood: 'possible'
1203
+ });
1204
+ failures.push({
1205
+ description: 'Archive navigation complexity may overwhelm readers looking for specific topics',
1206
+ severity: 'low',
1207
+ likelihood: 'possible'
1208
+ });
1209
+ }
1210
+ // SaaS / Web Applications
1211
+ else if (titleLower.includes('dashboard') || titleLower.includes('app') || titleLower.includes('platform') ||
1212
+ interactiveElements > 50) {
1213
+ failures.push({
1214
+ description: 'Complex workflows may confuse new users without proper onboarding',
1215
+ severity: 'medium',
1216
+ likelihood: 'likely'
1217
+ });
1218
+ failures.push({
1219
+ description: 'Data visualization and information density may cause cognitive overload',
1220
+ severity: 'medium',
1221
+ likelihood: 'possible'
1222
+ });
1223
+ failures.push({
1224
+ description: 'Error messages may not provide actionable recovery steps',
1225
+ severity: 'medium',
1226
+ likelihood: 'likely'
1227
+ });
1228
+ }
1229
+ // Form-heavy sites
1230
+ else if (forms > 0) {
1231
+ failures.push({
1232
+ description: 'Form validation errors may not be clearly communicated to users',
1233
+ severity: 'medium',
1234
+ likelihood: 'likely'
1235
+ });
1236
+ failures.push({
1237
+ description: 'Required field indicators may not be consistently applied',
1238
+ severity: 'low',
1239
+ likelihood: 'possible'
1240
+ });
1241
+ failures.push({
1242
+ description: 'Form submission failure recovery path may not be clear',
1243
+ severity: 'medium',
1244
+ likelihood: 'possible'
1245
+ });
1246
+ }
1247
+ // Return only the most relevant failures (max 5)
1248
+ return failures.slice(0, 5);
307
1249
  }
308
1250
  /**
309
1251
  * Analyze problem using Rule of Three and complexity assessment
310
1252
  */
311
- async analyzeProblem(_context) {
1253
+ async analyzeProblem(context) {
312
1254
  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
1255
+ const title = context.title || 'Untitled page';
1256
+ const description = context.metadata?.description || '';
1257
+ const hasError = context.errorIndicators?.hasErrorMessages || false;
1258
+ let problemStatement = `Evaluate quality experience of "${title}"`;
1259
+ if (description) {
1260
+ problemStatement += ` - ${description.substring(0, 100)}`;
1261
+ }
1262
+ const totalElements = context.domMetrics?.totalElements || 0;
1263
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
1264
+ const forms = context.domMetrics?.forms || 0;
1265
+ let complexity;
1266
+ if (totalElements > 500 || interactiveElements > 50 || forms > 3) {
1267
+ complexity = 'complex';
1268
+ }
1269
+ else if (totalElements > 200 || interactiveElements > 20 || forms > 1) {
1270
+ complexity = 'moderate';
1271
+ }
1272
+ else {
1273
+ complexity = 'simple';
1274
+ }
1275
+ const breakdown = [];
1276
+ if (context.domMetrics?.semanticStructure?.hasNav)
1277
+ breakdown.push('Navigation structure');
1278
+ if (forms > 0)
1279
+ breakdown.push(`Form interactions (${forms} forms)`);
1280
+ if (interactiveElements > 0)
1281
+ breakdown.push(`User interactions (${interactiveElements} elements)`);
1282
+ if (context.accessibility)
1283
+ breakdown.push('Accessibility compliance');
1284
+ if (context.performance)
1285
+ breakdown.push('Performance metrics');
1286
+ const potentialFailures = [];
1287
+ if (!context.domMetrics?.semanticStructure?.hasMain) {
1288
+ potentialFailures.push({
1289
+ description: 'Missing main content landmark - users may struggle to find primary content',
1290
+ severity: 'medium',
1291
+ likelihood: 'likely'
1292
+ });
1293
+ }
1294
+ if (context.accessibility && (context.accessibility.altTextsCoverage || 0) < 80) {
1295
+ potentialFailures.push({
1296
+ description: 'Poor image alt text coverage - screen reader users affected',
1297
+ severity: 'high',
1298
+ likelihood: 'very-likely'
1299
+ });
1300
+ }
1301
+ if (hasError) {
1302
+ potentialFailures.push({
1303
+ description: 'Visible error messages detected - potential usability issues',
1304
+ severity: 'medium',
1305
+ likelihood: 'likely'
1306
+ });
1307
+ }
1308
+ if (context.performance && (context.performance.loadTime || 0) > 3000) {
1309
+ potentialFailures.push({
1310
+ description: 'Slow load time - user frustration and abandonment risk',
1311
+ severity: 'high',
1312
+ likelihood: 'very-likely'
1313
+ });
1314
+ }
1315
+ if (!context.metadata?.viewport) {
1316
+ potentialFailures.push({
1317
+ description: 'Missing viewport meta tag - mobile responsiveness issues',
1318
+ severity: 'medium',
1319
+ likelihood: 'possible'
1320
+ });
1321
+ }
1322
+ // ENHANCED: Add domain-specific failure modes based on site type and context
1323
+ const domainFailures = this.detectDomainSpecificFailures(context, title, description, complexity);
1324
+ potentialFailures.push(...domainFailures);
1325
+ // Rule of Three: Ensure at least 3 failure modes are identified
1326
+ if (potentialFailures.length < 3) {
1327
+ // Add generic contextual failures for complex sites
1328
+ if (complexity === 'complex') {
1329
+ if (potentialFailures.length < 3) {
1330
+ potentialFailures.push({
1331
+ description: 'Complex interaction flows may confuse first-time users',
1332
+ severity: 'medium',
1333
+ likelihood: 'possible'
1334
+ });
1335
+ }
1336
+ if (potentialFailures.length < 3) {
1337
+ potentialFailures.push({
1338
+ description: 'Multiple interactive elements increase cognitive load',
1339
+ severity: 'low',
1340
+ likelihood: 'possible'
1341
+ });
1342
+ }
1343
+ if (potentialFailures.length < 3) {
1344
+ potentialFailures.push({
1345
+ description: 'Error recovery paths may not be clear in complex workflows',
1346
+ severity: 'medium',
1347
+ likelihood: 'possible'
1348
+ });
1349
+ }
1350
+ }
1351
+ }
1352
+ let clarityScore = 50;
1353
+ if (title && title !== 'Untitled page')
1354
+ clarityScore += 15;
1355
+ if (description)
1356
+ clarityScore += 15;
1357
+ if (breakdown.length >= 3)
1358
+ clarityScore += 10;
1359
+ if (context.domMetrics?.semanticStructure?.hasMain)
1360
+ clarityScore += 10;
1361
+ clarityScore = Math.min(100, clarityScore);
1362
+ return {
1363
+ problemStatement,
1364
+ complexity,
1365
+ breakdown,
1366
+ potentialFailures,
1367
+ clarityScore
321
1368
  };
322
- return analysis;
323
1369
  }
324
1370
  /**
325
1371
  * Analyze user needs
326
1372
  */
327
- async analyzeUserNeeds(_context, _problemAnalysis) {
1373
+ async analyzeUserNeeds(context, problemAnalysis) {
328
1374
  this.logger.debug('Analyzing user needs');
329
- const analysis = {
330
- needs: [],
331
- suitability: 'adequate',
332
- challenges: [],
333
- alignmentScore: 70
1375
+ const needs = [];
1376
+ const challenges = [];
1377
+ const semantic = context.domMetrics?.semanticStructure;
1378
+ const accessibility = context.accessibility;
1379
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
1380
+ const forms = context.domMetrics?.forms || 0;
1381
+ // Must-have features (critical for basic functionality)
1382
+ if (semantic?.hasNav) {
1383
+ needs.push({ description: 'Clear navigation to find content', priority: 'must-have', addressed: true });
1384
+ }
1385
+ else {
1386
+ challenges.push('Missing navigation structure - users cannot easily explore site');
1387
+ needs.push({ description: 'Clear navigation to find content', priority: 'must-have', addressed: false });
1388
+ }
1389
+ if (interactiveElements > 0) {
1390
+ needs.push({ description: 'Interactive elements for engagement', priority: 'must-have', addressed: true });
1391
+ }
1392
+ if (accessibility && (accessibility.focusableElementsCount || 0) > 0) {
1393
+ needs.push({ description: 'Keyboard navigation support', priority: 'must-have', addressed: true });
1394
+ }
1395
+ else {
1396
+ challenges.push('Limited keyboard navigation - inaccessible to some users');
1397
+ needs.push({ description: 'Keyboard navigation support', priority: 'must-have', addressed: false });
1398
+ }
1399
+ // Should-have features (important for good UX)
1400
+ if (semantic?.hasHeader) {
1401
+ needs.push({ description: 'Consistent page header for orientation', priority: 'should-have', addressed: true });
1402
+ }
1403
+ if (semantic?.hasFooter) {
1404
+ needs.push({ description: 'Footer with supporting information', priority: 'should-have', addressed: true });
1405
+ }
1406
+ if (accessibility && (accessibility.altTextsCoverage || 0) > 50) {
1407
+ needs.push({ description: 'Image descriptions for screen readers', priority: 'should-have', addressed: true });
1408
+ }
1409
+ else if (accessibility && (accessibility.altTextsCoverage || 0) < 50) {
1410
+ challenges.push('Poor alt text coverage - images not accessible');
1411
+ needs.push({ description: 'Image descriptions for screen readers', priority: 'should-have', addressed: false });
1412
+ }
1413
+ if (context.performance && (context.performance.loadTime || 0) < 3000) {
1414
+ needs.push({ description: 'Fast page load time', priority: 'should-have', addressed: true });
1415
+ }
1416
+ else if (context.performance && (context.performance.loadTime || 0) >= 3000) {
1417
+ challenges.push('Slow load time - user frustration risk');
1418
+ needs.push({ description: 'Fast page load time', priority: 'should-have', addressed: false });
1419
+ }
1420
+ // Nice-to-have features (enhancements)
1421
+ if (semantic?.hasAside) {
1422
+ needs.push({ description: 'Supplementary content sections', priority: 'nice-to-have', addressed: true });
1423
+ }
1424
+ if (accessibility && (accessibility.landmarkRoles || 0) > 3) {
1425
+ needs.push({ description: 'Rich ARIA landmarks for navigation', priority: 'nice-to-have', addressed: true });
1426
+ }
1427
+ if (forms > 0) {
1428
+ needs.push({ description: 'Form interactions for user input', priority: 'nice-to-have', addressed: true });
1429
+ }
1430
+ // Determine suitability
1431
+ const addressedMustHaves = needs.filter(n => n.priority === 'must-have' && n.addressed).length;
1432
+ const totalMustHaves = needs.filter(n => n.priority === 'must-have').length;
1433
+ let suitability;
1434
+ if (challenges.length === 0 && addressedMustHaves >= 3) {
1435
+ suitability = 'excellent';
1436
+ }
1437
+ else if (challenges.length <= 1 && addressedMustHaves >= 2) {
1438
+ suitability = 'good';
1439
+ }
1440
+ else if (challenges.length <= 2 && addressedMustHaves >= 2) {
1441
+ suitability = 'adequate';
1442
+ }
1443
+ else {
1444
+ suitability = 'poor';
1445
+ }
1446
+ // Calculate alignment score
1447
+ let alignmentScore = 40;
1448
+ alignmentScore += addressedMustHaves * 10;
1449
+ alignmentScore += needs.filter(n => n.priority === 'should-have' && n.addressed).length * 5;
1450
+ alignmentScore += needs.filter(n => n.priority === 'nice-to-have' && n.addressed).length * 2;
1451
+ alignmentScore -= challenges.length * 8;
1452
+ alignmentScore = Math.max(0, Math.min(100, alignmentScore));
1453
+ return {
1454
+ needs,
1455
+ suitability,
1456
+ challenges,
1457
+ alignmentScore
334
1458
  };
335
- return analysis;
336
1459
  }
337
1460
  /**
338
1461
  * Analyze business needs
339
1462
  */
340
- async analyzeBusinessNeeds(_context, _problemAnalysis) {
1463
+ async analyzeBusinessNeeds(context, problemAnalysis) {
341
1464
  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
1465
+ const forms = context.domMetrics?.forms || 0;
1466
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
1467
+ const performance = context.performance;
1468
+ const hasErrors = context.errorIndicators?.hasErrorMessages || false;
1469
+ let primaryGoal;
1470
+ let kpisAffected = [];
1471
+ if (forms > 2) {
1472
+ primaryGoal = 'business-ease'; // Conversion focus leans business
1473
+ kpisAffected = ['Form completion rate', 'Lead generation', 'User sign-ups'];
1474
+ }
1475
+ else if (interactiveElements > 30) {
1476
+ primaryGoal = 'user-experience'; // Engagement focus leans UX
1477
+ kpisAffected = ['Time on site', 'Click-through rate', 'User engagement'];
1478
+ }
1479
+ else {
1480
+ primaryGoal = 'balanced';
1481
+ kpisAffected = ['Content consumption', 'Bounce rate', 'Page views'];
1482
+ }
1483
+ const crossTeamImpact = [];
1484
+ if (forms > 0) {
1485
+ crossTeamImpact.push({
1486
+ team: 'Marketing',
1487
+ impactType: 'positive',
1488
+ description: 'Form conversion optimization needed'
1489
+ });
1490
+ crossTeamImpact.push({
1491
+ team: 'Development',
1492
+ impactType: 'neutral',
1493
+ description: 'Form validation and submission handling'
1494
+ });
1495
+ }
1496
+ if (context.accessibility && (context.accessibility.altTextsCoverage || 0) < 100) {
1497
+ crossTeamImpact.push({
1498
+ team: 'Content',
1499
+ impactType: 'negative',
1500
+ description: 'Image alt text creation required'
1501
+ });
1502
+ }
1503
+ if (performance && (performance.loadTime || 0) > 2000) {
1504
+ crossTeamImpact.push({
1505
+ team: 'Engineering',
1506
+ impactType: 'negative',
1507
+ description: 'Performance optimization needed'
1508
+ });
1509
+ }
1510
+ if (problemAnalysis.complexity === 'complex') {
1511
+ crossTeamImpact.push({
1512
+ team: 'QA',
1513
+ impactType: 'neutral',
1514
+ description: 'Comprehensive testing strategy required'
1515
+ });
1516
+ }
1517
+ let compromisesUX = false;
1518
+ if (hasErrors) {
1519
+ compromisesUX = true;
1520
+ }
1521
+ if (context.accessibility && (context.accessibility.altTextsCoverage || 0) < 50) {
1522
+ compromisesUX = true;
1523
+ }
1524
+ if (performance && (performance.loadTime || 0) > 4000) {
1525
+ compromisesUX = true;
1526
+ }
1527
+ const impactsKPIs = kpisAffected.length > 0;
1528
+ let alignmentScore = 50;
1529
+ if (kpisAffected.length > 0)
1530
+ alignmentScore += 15;
1531
+ if (crossTeamImpact.length > 0)
1532
+ alignmentScore += 10;
1533
+ if (!compromisesUX)
1534
+ alignmentScore += 20;
1535
+ if (impactsKPIs)
1536
+ alignmentScore += 5;
1537
+ alignmentScore = Math.min(100, alignmentScore);
1538
+ return {
1539
+ primaryGoal,
1540
+ kpisAffected,
1541
+ crossTeamImpact,
1542
+ compromisesUX,
1543
+ impactsKPIs,
1544
+ alignmentScore
1545
+ };
1546
+ }
1547
+ /**
1548
+ * Analyze Creativity - Drawing inspiration from diverse domains
1549
+ */
1550
+ async analyzeCreativity(context, problemAnalysis) {
1551
+ this.logger.debug('Analyzing creativity and innovation');
1552
+ const innovativeApproaches = [];
1553
+ // Analyze based on problem complexity
1554
+ if (problemAnalysis.complexity === 'complex' || problemAnalysis.complexity === 'moderate') {
1555
+ // Philosophy-inspired: Question fundamental assumptions
1556
+ innovativeApproaches.push({
1557
+ description: 'Question fundamental assumptions about user mental models and expected workflows',
1558
+ inspirationSource: 'philosophy',
1559
+ applicability: 'high',
1560
+ novelty: 'moderately-novel'
1561
+ });
1562
+ // Medicine-inspired: Diagnostic approach
1563
+ if (context.errorIndicators?.hasErrorMessages) {
1564
+ innovativeApproaches.push({
1565
+ description: 'Apply diagnostic testing - systematically isolate error sources through controlled scenarios',
1566
+ inspirationSource: 'medicine',
1567
+ applicability: 'high',
1568
+ novelty: 'moderately-novel'
1569
+ });
1570
+ }
1571
+ }
1572
+ // E-commerce/Fashion-inspired: User journey and aesthetics
1573
+ if (context.domMetrics?.forms && context.domMetrics.forms > 0) {
1574
+ innovativeApproaches.push({
1575
+ description: 'Test checkout/form flows like fashion retail - focus on friction points, abandonment triggers',
1576
+ inspirationSource: 'e-commerce',
1577
+ applicability: 'high',
1578
+ novelty: 'incremental'
1579
+ });
1580
+ }
1581
+ // Social Science-inspired: Cultural sensitivity and demographics
1582
+ innovativeApproaches.push({
1583
+ description: 'Analyze through diverse demographic lenses (age, gender, culture, ability) for inclusive testing',
1584
+ inspirationSource: 'social science',
1585
+ applicability: 'high',
1586
+ novelty: 'moderately-novel'
1587
+ });
1588
+ // Gaming-inspired: Edge cases and exploits
1589
+ if (context.domMetrics?.interactiveElements && context.domMetrics.interactiveElements > 20) {
1590
+ innovativeApproaches.push({
1591
+ description: 'Test for "game-breaking" exploits - unexpected interaction sequences, boundary conditions',
1592
+ inspirationSource: 'gaming',
1593
+ applicability: 'medium',
1594
+ novelty: 'highly-novel'
1595
+ });
1596
+ }
1597
+ const domainsExplored = [...new Set(innovativeApproaches.map(a => a.inspirationSource))];
1598
+ const perspectives = [
1599
+ 'Unexperienced user perspective (fresh eyes)',
1600
+ 'Power user perspective (efficiency focus)',
1601
+ 'Accessibility perspective (assistive tech users)',
1602
+ 'International perspective (cultural differences)'
1603
+ ];
1604
+ // Calculate creativity score
1605
+ let creativityScore = 50; // Base score
1606
+ creativityScore += innovativeApproaches.length * 8; // +8 per approach
1607
+ creativityScore += domainsExplored.length * 5; // +5 per domain
1608
+ creativityScore = Math.min(100, creativityScore);
1609
+ return {
1610
+ innovativeApproaches,
1611
+ domainsExplored,
1612
+ perspectives,
1613
+ creativityScore,
1614
+ notes: [
1615
+ 'Creativity draws from diverse domains to uncover unconventional testing approaches',
1616
+ 'Higher complexity problems benefit from cross-disciplinary inspiration',
1617
+ `Applied ${innovativeApproaches.length} creative approaches from ${domainsExplored.length} domains`
1618
+ ]
1619
+ };
1620
+ }
1621
+ /**
1622
+ * Analyze Design - Exactness, Intuitive, and Counter-intuitive Design
1623
+ */
1624
+ async analyzeDesign(context, _problemAnalysis) {
1625
+ this.logger.debug('Analyzing design quality');
1626
+ // 1. Exactness Analysis - How clearly the product communicates its intent
1627
+ const clearElements = [];
1628
+ const unclearElements = [];
1629
+ // Check semantic structure for clarity
1630
+ if (context.domMetrics?.semanticStructure?.hasNav) {
1631
+ clearElements.push('Navigation structure clearly defined with semantic <nav> element');
1632
+ }
1633
+ if (context.domMetrics?.semanticStructure?.hasMain) {
1634
+ clearElements.push('Main content area clearly identified');
1635
+ }
1636
+ if (context.domMetrics?.semanticStructure?.hasHeader && context.domMetrics?.semanticStructure?.hasFooter) {
1637
+ clearElements.push('Header and footer provide clear page structure');
1638
+ }
1639
+ // Check for unclear elements
1640
+ const ariaLabels = context.semanticQuality?.ariaLabels || 0;
1641
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
1642
+ if (interactiveElements > 0 && ariaLabels < interactiveElements * 0.5) {
1643
+ unclearElements.push('Many interactive elements lack ARIA labels for clarity');
1644
+ }
1645
+ if (context.errorIndicators?.hasErrorMessages) {
1646
+ unclearElements.push('Error messages present - may indicate unclear user guidance');
1647
+ }
1648
+ const exactnessClarity = unclearElements.length === 0 ? 'excellent' :
1649
+ unclearElements.length <= 2 ? 'good' :
1650
+ unclearElements.length <= 4 ? 'adequate' : 'poor';
1651
+ let exactnessScore = 50;
1652
+ exactnessScore += clearElements.length * 10;
1653
+ exactnessScore -= unclearElements.length * 8;
1654
+ exactnessScore = Math.max(0, Math.min(100, exactnessScore));
1655
+ // 2. Intuitive Design Analysis
1656
+ const intuitivePatterns = [];
1657
+ const culturalIssues = [];
1658
+ if (context.domMetrics?.semanticStructure?.hasNav) {
1659
+ intuitivePatterns.push('Standard navigation patterns (semantic nav element)');
1660
+ }
1661
+ if (context.domMetrics?.forms && context.domMetrics.forms > 0) {
1662
+ intuitivePatterns.push('Standard form patterns detected');
1663
+ }
1664
+ if (context.metadata?.viewport) {
1665
+ intuitivePatterns.push('Responsive design viewport configured');
1666
+ }
1667
+ // Cultural sensitivity check (basic)
1668
+ if (context.title && /[^\x00-\x7F]/.test(context.title || '')) {
1669
+ // Non-ASCII characters detected - could be good (internationalization) or need review
1670
+ culturalIssues.push('International characters detected - ensure cultural appropriateness');
1671
+ }
1672
+ const followsConventions = intuitivePatterns.length >= 2;
1673
+ let intuitiveScore = followsConventions ? 70 : 50;
1674
+ intuitiveScore += intuitivePatterns.length * 8;
1675
+ intuitiveScore -= culturalIssues.length * 10;
1676
+ intuitiveScore = Math.max(0, Math.min(100, intuitiveScore));
1677
+ // 3. Counter-intuitive Design Detection
1678
+ const deviations = [];
1679
+ // Check for potential counter-intuitive patterns
1680
+ const buttons = context.domMetrics?.buttons || 0;
1681
+ const forms = context.domMetrics?.forms || 0;
1682
+ if (buttons > 20 && forms === 0) {
1683
+ deviations.push({
1684
+ element: 'Multiple buttons without forms',
1685
+ expectedBehavior: 'Forms typically accompany many buttons',
1686
+ actualBehavior: 'Many buttons present without traditional forms',
1687
+ impact: 'neutral',
1688
+ justification: 'May be single-page app or API-driven interface'
1689
+ });
1690
+ }
1691
+ if (!context.domMetrics?.semanticStructure?.hasMain && (context.domMetrics?.totalElements || 0) > 50) {
1692
+ deviations.push({
1693
+ element: 'Complex page without main landmark',
1694
+ expectedBehavior: 'Complex pages typically have <main> landmark',
1695
+ actualBehavior: 'No main content landmark defined',
1696
+ impact: 'negative',
1697
+ justification: 'Reduces accessibility and clarity'
1698
+ });
1699
+ }
1700
+ const innovativeJustification = deviations.some(d => d.impact === 'positive' || d.justification?.includes('innovation'));
1701
+ const freshEyesPerspective = deviations.length > 0; // We're looking from unexperienced user view
1702
+ const issuesCount = deviations.filter(d => d.impact === 'negative').length;
1703
+ // 4. Overall Design Score
1704
+ const overallDesignScore = Math.round((exactnessScore + intuitiveScore) / 2);
1705
+ return {
1706
+ exactness: {
1707
+ clarity: exactnessClarity,
1708
+ clearElements,
1709
+ unclearElements,
1710
+ score: exactnessScore
1711
+ },
1712
+ intuitive: {
1713
+ followsConventions,
1714
+ intuitivePatterns,
1715
+ culturalIssues,
1716
+ score: intuitiveScore
1717
+ },
1718
+ counterIntuitive: {
1719
+ deviations,
1720
+ innovativeJustification,
1721
+ freshEyesPerspective,
1722
+ issuesCount
1723
+ },
1724
+ overallDesignScore
349
1725
  };
350
- return analysis;
351
1726
  }
352
1727
  /**
353
1728
  * Detect oracle problems from analysis context
@@ -370,102 +1745,219 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
370
1745
  /**
371
1746
  * Apply all enabled heuristics
372
1747
  */
373
- async applyAllHeuristics(context, problemAnalysis, userNeeds, businessNeeds) {
1748
+ async applyAllHeuristics(context, problemAnalysis, userNeeds, businessNeeds, creativityAnalysis, designAnalysis) {
374
1749
  if (!this.heuristicsEngine) {
375
1750
  throw new Error('Heuristics engine not initialized');
376
1751
  }
377
- return this.heuristicsEngine.applyAll(context, problemAnalysis, userNeeds, businessNeeds);
1752
+ // Get standard heuristics
1753
+ const standardHeuristics = await this.heuristicsEngine.applyAll(context, problemAnalysis, userNeeds, businessNeeds);
1754
+ // Add creativity and design heuristics
1755
+ const creativityHeuristic = {
1756
+ name: 'creativity-innovation',
1757
+ category: 'creativity',
1758
+ applied: true,
1759
+ score: creativityAnalysis.creativityScore,
1760
+ findings: [
1761
+ `Applied ${creativityAnalysis.innovativeApproaches.length} creative approaches`,
1762
+ `Explored ${creativityAnalysis.domainsExplored.length} diverse domains: ${creativityAnalysis.domainsExplored.join(', ')}`,
1763
+ ...creativityAnalysis.notes
1764
+ ],
1765
+ issues: creativityAnalysis.innovativeApproaches
1766
+ .filter(a => a.applicability === 'low')
1767
+ .map(a => ({
1768
+ description: `Low applicability approach: ${a.description}`,
1769
+ severity: 'low'
1770
+ })),
1771
+ recommendations: creativityAnalysis.innovativeApproaches
1772
+ .filter(a => a.applicability === 'high')
1773
+ .map(a => `Consider: ${a.description} (${a.inspirationSource})`)
1774
+ };
1775
+ const designHeuristic = {
1776
+ name: 'design-quality',
1777
+ category: 'design',
1778
+ applied: true,
1779
+ score: designAnalysis.overallDesignScore,
1780
+ findings: [
1781
+ `Exactness: ${designAnalysis.exactness.clarity} (${designAnalysis.exactness.score}/100)`,
1782
+ `Intuitive Design: ${designAnalysis.intuitive.followsConventions ? 'Follows' : 'Deviates from'} conventions (${designAnalysis.intuitive.score}/100)`,
1783
+ `Counter-intuitive issues: ${designAnalysis.counterIntuitive.issuesCount}`,
1784
+ ...designAnalysis.exactness.clearElements.map(e => `✓ ${e}`),
1785
+ ...designAnalysis.intuitive.intuitivePatterns.map(p => `✓ ${p}`)
1786
+ ],
1787
+ issues: [
1788
+ ...designAnalysis.exactness.unclearElements.map(e => ({
1789
+ description: e,
1790
+ severity: 'medium'
1791
+ })),
1792
+ ...designAnalysis.counterIntuitive.deviations
1793
+ .filter(d => d.impact === 'negative')
1794
+ .map(d => ({
1795
+ description: `${d.element}: ${d.expectedBehavior} vs ${d.actualBehavior}`,
1796
+ severity: 'medium'
1797
+ }))
1798
+ ],
1799
+ recommendations: [
1800
+ ...designAnalysis.exactness.unclearElements.map(e => `Improve clarity: ${e}`),
1801
+ ...designAnalysis.intuitive.culturalIssues.map(i => `Address: ${i}`)
1802
+ ]
1803
+ };
1804
+ return [...standardHeuristics, creativityHeuristic, designHeuristic];
378
1805
  }
379
1806
  /**
380
1807
  * Generate QX recommendations
381
1808
  */
382
1809
  async generateRecommendations(problemAnalysis, userNeeds, businessNeeds, oracleProblems, impactAnalysis, heuristics, _testabilityIntegration) {
383
1810
  const recommendations = [];
384
- // Generate recommendations from problem analysis
385
- if (problemAnalysis.clarityScore < (this.config.thresholds?.minProblemClarity || 60)) {
1811
+ let priorityCounter = 1;
1812
+ // Oracle problems (highest priority - match manual report structure)
1813
+ for (const problem of oracleProblems) {
1814
+ const impactScore = problem.severity === 'critical' ? 90 : problem.severity === 'high' ? 80 : problem.severity === 'medium' ? 60 : 40;
1815
+ const impactPct = Math.round((impactScore / 100) * 30); // Up to 30% impact
386
1816
  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'
1817
+ principle: 'Oracle Problem',
1818
+ recommendation: `Resolve: ${problem.description}`,
1819
+ severity: problem.severity,
1820
+ impact: impactScore,
1821
+ effort: problem.severity === 'critical' || problem.severity === 'high' ? 'high' : 'medium',
1822
+ priority: priorityCounter++,
1823
+ category: 'qa',
1824
+ impactPercentage: impactPct,
1825
+ estimatedEffort: problem.severity === 'critical' ? 'High - Critical issue' : problem.severity === 'high' ? 'High' : 'Medium'
394
1826
  });
395
1827
  }
396
- // Generate recommendations from user needs
397
- if (userNeeds.alignmentScore < (this.config.thresholds?.minUserNeedsAlignment || 70)) {
1828
+ // Problem clarity
1829
+ if (problemAnalysis.clarityScore < (this.config.thresholds?.minProblemClarity || 70)) {
1830
+ const gap = 70 - problemAnalysis.clarityScore;
398
1831
  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'
1832
+ principle: 'Problem Understanding',
1833
+ recommendation: 'Improve problem statement clarity with detailed breakdown of failure modes and user scenarios',
1834
+ severity: gap > 25 ? 'high' : 'medium',
1835
+ impact: Math.round(gap * 1.2),
1836
+ effort: 'medium',
1837
+ priority: priorityCounter++,
1838
+ category: 'qx',
1839
+ impactPercentage: Math.round((gap / 70) * 20),
1840
+ estimatedEffort: 'Medium - Requires stakeholder workshops'
406
1841
  });
407
1842
  }
408
- // Generate recommendations from business needs
409
- if (businessNeeds.alignmentScore < (this.config.thresholds?.minBusinessAlignment || 70)) {
1843
+ // User needs alignment (match manual report priority)
1844
+ if (userNeeds.alignmentScore < (this.config.thresholds?.minUserNeedsAlignment || 75)) {
1845
+ const gap = 75 - userNeeds.alignmentScore;
1846
+ const impactPct = Math.min(35, Math.round((gap / 75) * 100));
410
1847
  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'
1848
+ principle: 'User Needs Alignment',
1849
+ recommendation: `Improve user needs coverage from ${userNeeds.alignmentScore}/100 to at least 75/100`,
1850
+ severity: gap > 20 ? 'high' : 'medium',
1851
+ impact: Math.round(gap * 0.9),
1852
+ effort: gap > 25 ? 'high' : 'medium',
1853
+ priority: priorityCounter++,
1854
+ category: 'ux',
1855
+ impactPercentage: impactPct,
1856
+ estimatedEffort: gap > 25 ? 'High - Major UX redesign' : 'Medium - UX improvements'
418
1857
  });
419
1858
  }
420
- // Generate recommendations from oracle problems
421
- for (const problem of oracleProblems) {
422
- if (problem.severity === 'high' || problem.severity === 'critical') {
1859
+ // Heuristic-specific high-impact recommendations
1860
+ const lowScoringHeuristics = heuristics
1861
+ .filter(h => h.score < 70)
1862
+ .sort((a, b) => a.score - b.score)
1863
+ .slice(0, 5); // Top 5 worst
1864
+ lowScoringHeuristics.forEach(heuristic => {
1865
+ const impactScore = 75 - heuristic.score;
1866
+ const impactPct = Math.round((impactScore / 75) * 25);
1867
+ if (heuristic.recommendations.length > 0 && heuristic.heuristicType) {
423
1868
  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'
1869
+ principle: this.formatHeuristicName(heuristic.heuristicType),
1870
+ recommendation: heuristic.recommendations[0],
1871
+ severity: heuristic.score < 50 ? 'high' : 'medium',
1872
+ impact: impactScore,
1873
+ effort: heuristic.score < 40 ? 'high' : 'medium',
1874
+ priority: priorityCounter++,
1875
+ category: heuristic.category,
1876
+ impactPercentage: impactPct,
1877
+ estimatedEffort: heuristic.score < 40 ? 'High - Significant work required' : 'Medium'
431
1878
  });
432
1879
  }
1880
+ });
1881
+ // High-impact issues from heuristics
1882
+ heuristics.forEach(heuristic => {
1883
+ if (!heuristic.heuristicType)
1884
+ return;
1885
+ heuristic.issues
1886
+ .filter(issue => issue.severity === 'critical' || issue.severity === 'high')
1887
+ .slice(0, 1) // One per heuristic
1888
+ .forEach(issue => {
1889
+ const impactScore = Math.min(85, 100 - heuristic.score);
1890
+ const impactPct = Math.round((impactScore / 100) * 22);
1891
+ recommendations.push({
1892
+ principle: this.formatHeuristicName(heuristic.heuristicType),
1893
+ recommendation: issue.description,
1894
+ severity: issue.severity,
1895
+ impact: impactScore,
1896
+ effort: issue.severity === 'critical' ? 'high' : 'medium',
1897
+ priority: priorityCounter++,
1898
+ category: heuristic.category,
1899
+ impactPercentage: impactPct,
1900
+ estimatedEffort: issue.severity === 'critical' ? 'High - Critical fix' : 'Medium'
1901
+ });
1902
+ });
1903
+ });
1904
+ // Business-user balance
1905
+ const balanceDiff = Math.abs(userNeeds.alignmentScore - businessNeeds.alignmentScore);
1906
+ if (balanceDiff > 15) {
1907
+ const impactPct = Math.round((balanceDiff / 100) * 20);
1908
+ const favorsUser = userNeeds.alignmentScore > businessNeeds.alignmentScore;
1909
+ recommendations.push({
1910
+ principle: 'User-Business Balance',
1911
+ recommendation: favorsUser
1912
+ ? 'Strengthen business value metrics while maintaining user experience quality'
1913
+ : 'Enhance user experience focus to balance business-centric approach',
1914
+ severity: balanceDiff > 30 ? 'high' : 'medium',
1915
+ impact: Math.round(balanceDiff * 0.75),
1916
+ effort: 'medium',
1917
+ priority: priorityCounter++,
1918
+ category: 'qx',
1919
+ impactPercentage: impactPct,
1920
+ estimatedEffort: 'Medium - Requires stakeholder alignment'
1921
+ });
433
1922
  }
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
- }
1923
+ // Business needs (if misaligned)
1924
+ if (businessNeeds.alignmentScore < (this.config.thresholds?.minBusinessAlignment || 70)) {
1925
+ const gap = 70 - businessNeeds.alignmentScore;
1926
+ recommendations.push({
1927
+ principle: 'Business Alignment',
1928
+ recommendation: 'Improve alignment with business KPIs and objectives',
1929
+ severity: gap > 25 ? 'high' : 'medium',
1930
+ impact: Math.round(gap * 0.7),
1931
+ effort: 'medium',
1932
+ priority: priorityCounter++,
1933
+ category: 'qx',
1934
+ impactPercentage: Math.round((gap / 70) * 18),
1935
+ estimatedEffort: 'Medium - Business stakeholder review'
1936
+ });
449
1937
  }
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;
1938
+ // Sort by impact percentage and limit to top 10
1939
+ const sorted = recommendations
1940
+ .sort((a, b) => (b.impactPercentage || 0) - (a.impactPercentage || 0))
1941
+ .slice(0, 10);
1942
+ // Reassign priorities
1943
+ sorted.forEach((rec, idx) => {
1944
+ rec.priority = idx + 1;
455
1945
  });
456
- return recommendations;
1946
+ return sorted;
457
1947
  }
458
1948
  /**
459
1949
  * Calculate overall QX score
460
1950
  */
461
- calculateOverallQXScore(problemAnalysis, userNeeds, businessNeeds, impactAnalysis, heuristics) {
1951
+ calculateOverallQXScore(problemAnalysis, userNeeds, businessNeeds, creativityAnalysis, designAnalysis, impactAnalysis, heuristics) {
462
1952
  // Weighted average of all components
463
1953
  const weights = {
464
- problem: 0.20,
465
- userNeeds: 0.25,
466
- businessNeeds: 0.20,
467
- impact: 0.15,
468
- heuristics: 0.20
1954
+ problem: 0.15, // Reduced from 0.20
1955
+ userNeeds: 0.20, // Reduced from 0.25
1956
+ businessNeeds: 0.15, // Reduced from 0.20
1957
+ creativity: 0.15, // NEW
1958
+ design: 0.15, // NEW
1959
+ impact: 0.10, // Reduced from 0.15
1960
+ heuristics: 0.10 // Reduced from 0.20
469
1961
  };
470
1962
  const heuristicsAvg = heuristics.length > 0
471
1963
  ? heuristics.reduce((sum, h) => sum + h.score, 0) / heuristics.length
@@ -474,6 +1966,8 @@ class QXPartnerAgent extends BaseAgent_1.BaseAgent {
474
1966
  const score = problemAnalysis.clarityScore * weights.problem +
475
1967
  userNeeds.alignmentScore * weights.userNeeds +
476
1968
  businessNeeds.alignmentScore * weights.businessNeeds +
1969
+ creativityAnalysis.creativityScore * weights.creativity +
1970
+ designAnalysis.overallDesignScore * weights.design +
477
1971
  impactScore * weights.impact +
478
1972
  heuristicsAvg * weights.heuristics;
479
1973
  return Math.round(score);
@@ -668,17 +2162,357 @@ class QXHeuristicsEngine {
668
2162
  }
669
2163
  return results;
670
2164
  }
671
- async apply(heuristic, _context, _problemAnalysis, _userNeeds, _businessNeeds) {
672
- // Simplified heuristic application
673
- // In real implementation, each heuristic would have specific logic
2165
+ async apply(heuristic, context, problemAnalysis, userNeeds, businessNeeds) {
2166
+ const category = this.getHeuristicCategory(heuristic);
2167
+ const findings = [];
2168
+ const issues = [];
2169
+ const recommendations = [];
2170
+ let score = 75; // Base score
2171
+ // Apply specific heuristic logic based on type
2172
+ switch (heuristic) {
2173
+ case qx_1.QXHeuristic.CONSISTENCY_ANALYSIS:
2174
+ if (context.domMetrics?.semanticStructure?.hasHeader && context.domMetrics?.semanticStructure?.hasFooter) {
2175
+ score = 85;
2176
+ findings.push('Consistent page structure with header and footer');
2177
+ }
2178
+ else {
2179
+ score = 60;
2180
+ recommendations.push('Add consistent header/footer structure');
2181
+ }
2182
+ break;
2183
+ case qx_1.QXHeuristic.INTUITIVE_DESIGN:
2184
+ const hasNav = context.domMetrics?.semanticStructure?.hasNav;
2185
+ const focusable = context.accessibility?.focusableElementsCount || 0;
2186
+ if (hasNav && focusable > 10) {
2187
+ score = 82;
2188
+ findings.push('Intuitive navigation and interaction design');
2189
+ }
2190
+ else {
2191
+ score = 55;
2192
+ issues.push({ description: 'Navigation or interaction patterns unclear', severity: 'medium' });
2193
+ }
2194
+ break;
2195
+ case qx_1.QXHeuristic.EXACTNESS_AND_CLARITY:
2196
+ // Visual hierarchy and clarity (Design category)
2197
+ score = 70;
2198
+ const hasSemanticStructure = context.domMetrics?.semanticStructure;
2199
+ const structureScore = [
2200
+ hasSemanticStructure?.hasHeader,
2201
+ hasSemanticStructure?.hasMain,
2202
+ hasSemanticStructure?.hasNav,
2203
+ hasSemanticStructure?.hasFooter
2204
+ ].filter(Boolean).length;
2205
+ score = 50 + (structureScore * 10);
2206
+ if (structureScore >= 3) {
2207
+ findings.push('Strong visual hierarchy with semantic HTML elements');
2208
+ }
2209
+ else if (structureScore >= 2) {
2210
+ findings.push('Moderate visual hierarchy - some semantic elements present');
2211
+ recommendations.push('Add more semantic HTML5 elements for clarity');
2212
+ }
2213
+ else {
2214
+ issues.push({ description: 'Weak visual hierarchy - missing semantic structure', severity: 'high' });
2215
+ recommendations.push('Implement semantic HTML5: header, nav, main, footer');
2216
+ }
2217
+ // Title and description clarity
2218
+ if (context.metadata?.description && context.metadata.description.length > 20) {
2219
+ score += 10;
2220
+ findings.push('Page has descriptive metadata');
2221
+ }
2222
+ break;
2223
+ case qx_1.QXHeuristic.USER_FEELINGS_IMPACT:
2224
+ // Comprehensive user feelings analysis (Interaction + Accessibility)
2225
+ const altCoverage = context.accessibility?.altTextsCoverage || 0;
2226
+ const loadTime = context.performance?.loadTime || 0;
2227
+ const ariaLabels = context.accessibility?.ariaLabelsCount || 0;
2228
+ const focusableElements = context.accessibility?.focusableElementsCount || 0;
2229
+ score = 60; // Base
2230
+ // Accessibility impact on feelings (35% weight)
2231
+ if (altCoverage >= 90) {
2232
+ score += 20;
2233
+ findings.push('Excellent accessibility (90%+ alt coverage) creates inclusive, positive experience');
2234
+ }
2235
+ else if (altCoverage >= 70) {
2236
+ score += 12;
2237
+ findings.push('Good accessibility creates generally positive user feelings');
2238
+ }
2239
+ else if (altCoverage < 50) {
2240
+ score -= 15;
2241
+ issues.push({ description: 'Poor accessibility (<50% alt coverage) frustrates users with disabilities', severity: 'high' });
2242
+ recommendations.push('Improve alt text coverage to at least 80% for better accessibility');
2243
+ }
2244
+ // ARIA support impact
2245
+ if (ariaLabels > 5) {
2246
+ score += 8;
2247
+ findings.push('Strong ARIA labeling enhances screen reader experience');
2248
+ }
2249
+ // Performance impact on feelings (35% weight)
2250
+ if (loadTime < 1500) {
2251
+ score += 15;
2252
+ findings.push('Very fast load time (<1.5s) delights users');
2253
+ }
2254
+ else if (loadTime < 2500) {
2255
+ score += 8;
2256
+ findings.push('Fast load time enhances user satisfaction');
2257
+ }
2258
+ else if (loadTime > 4000) {
2259
+ score -= 20;
2260
+ issues.push({ description: 'Very slow load time (>4s) causes significant frustration', severity: 'critical' });
2261
+ recommendations.push('Optimize page load time - target under 2.5 seconds');
2262
+ }
2263
+ else if (loadTime > 3000) {
2264
+ score -= 12;
2265
+ issues.push({ description: 'Slow load time causes user frustration', severity: 'high' });
2266
+ }
2267
+ // Error visibility impact (15% weight)
2268
+ if (context.errorIndicators?.hasErrorMessages) {
2269
+ score -= 12;
2270
+ issues.push({ description: 'Visible errors reduce user confidence and satisfaction', severity: 'high' });
2271
+ recommendations.push('Review and fix visible error messages');
2272
+ }
2273
+ // Interaction capability (15% weight)
2274
+ if (focusableElements > 20) {
2275
+ score += 5;
2276
+ findings.push('Rich interactive elements provide user control and engagement');
2277
+ }
2278
+ else if (focusableElements < 5) {
2279
+ score -= 8;
2280
+ issues.push({ description: 'Limited interactivity may feel restrictive', severity: 'medium' });
2281
+ }
2282
+ score = Math.max(20, Math.min(100, score));
2283
+ break;
2284
+ case qx_1.QXHeuristic.GUI_FLOW_IMPACT:
2285
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
2286
+ const forms = context.domMetrics?.forms || 0;
2287
+ if (interactiveElements > 20) {
2288
+ score = 75;
2289
+ findings.push(`${interactiveElements} interactive elements provide user control`);
2290
+ }
2291
+ if (forms > 0) {
2292
+ findings.push(`${forms} forms impact user input flows`);
2293
+ score = Math.min(100, score + 10);
2294
+ }
2295
+ if (interactiveElements === 0) {
2296
+ score = 30;
2297
+ issues.push({ description: 'Limited user interaction capability', severity: 'high' });
2298
+ }
2299
+ break;
2300
+ case qx_1.QXHeuristic.CROSS_FUNCTIONAL_IMPACT:
2301
+ score = 70;
2302
+ if (context.accessibility && (context.accessibility.altTextsCoverage || 0) < 100) {
2303
+ findings.push('Content team needed for alt text creation');
2304
+ }
2305
+ if (context.performance && (context.performance.loadTime || 0) > 2000) {
2306
+ findings.push('Engineering team needed for performance optimization');
2307
+ }
2308
+ if (problemAnalysis.complexity === 'complex') {
2309
+ findings.push('QA team needed for comprehensive testing');
2310
+ }
2311
+ score = 70 + (findings.length * 5);
2312
+ break;
2313
+ case qx_1.QXHeuristic.DATA_DEPENDENT_IMPACT:
2314
+ if (context.domMetrics?.forms && context.domMetrics.forms > 0) {
2315
+ score = 75;
2316
+ findings.push(`${context.domMetrics.forms} forms depend on backend data processing`);
2317
+ }
2318
+ else {
2319
+ score = 50;
2320
+ findings.push('Limited data-dependent features');
2321
+ }
2322
+ break;
2323
+ case qx_1.QXHeuristic.PROBLEM_UNDERSTANDING:
2324
+ score = problemAnalysis.clarityScore;
2325
+ if (problemAnalysis.clarityScore > 80) {
2326
+ findings.push('Problem is well-defined');
2327
+ }
2328
+ else {
2329
+ issues.push({ description: 'Problem clarity needs improvement', severity: 'medium' });
2330
+ }
2331
+ findings.push(...problemAnalysis.breakdown);
2332
+ break;
2333
+ case qx_1.QXHeuristic.RULE_OF_THREE:
2334
+ score = problemAnalysis.potentialFailures.length >= 3 ? 85 : 60;
2335
+ findings.push(`${problemAnalysis.potentialFailures.length} potential failure modes identified`);
2336
+ if (problemAnalysis.potentialFailures.length < 3) {
2337
+ recommendations.push('Identify at least 3 potential failure modes');
2338
+ }
2339
+ break;
2340
+ case qx_1.QXHeuristic.PROBLEM_COMPLEXITY:
2341
+ score = problemAnalysis.complexity === 'simple' ? 90 :
2342
+ problemAnalysis.complexity === 'moderate' ? 75 : 60;
2343
+ findings.push(`Problem complexity: ${problemAnalysis.complexity}`);
2344
+ break;
2345
+ case qx_1.QXHeuristic.USER_NEEDS_IDENTIFICATION:
2346
+ score = userNeeds.alignmentScore;
2347
+ findings.push(`${userNeeds.needs.length} user needs identified`);
2348
+ const mustHave = userNeeds.needs.filter(n => n.priority === 'must-have').length;
2349
+ findings.push(`${mustHave} must-have features`);
2350
+ if (userNeeds.challenges.length > 0) {
2351
+ issues.push({ description: `${userNeeds.challenges.length} user need challenges found`, severity: 'medium' });
2352
+ }
2353
+ break;
2354
+ case qx_1.QXHeuristic.USER_NEEDS_SUITABILITY:
2355
+ score = userNeeds.suitability === 'excellent' ? 95 :
2356
+ userNeeds.suitability === 'good' ? 80 :
2357
+ userNeeds.suitability === 'adequate' ? 65 : 45;
2358
+ findings.push(`User needs suitability: ${userNeeds.suitability}`);
2359
+ break;
2360
+ case qx_1.QXHeuristic.USER_NEEDS_VALIDATION:
2361
+ const addressedNeeds = userNeeds.needs.filter(n => n.addressed).length;
2362
+ score = userNeeds.needs.length > 0 ? (addressedNeeds / userNeeds.needs.length) * 100 : 50;
2363
+ findings.push(`${addressedNeeds}/${userNeeds.needs.length} needs validated and addressed`);
2364
+ break;
2365
+ case qx_1.QXHeuristic.BUSINESS_NEEDS_IDENTIFICATION:
2366
+ score = businessNeeds.alignmentScore;
2367
+ findings.push(`Primary goal: ${businessNeeds.primaryGoal}`);
2368
+ findings.push(`${businessNeeds.kpisAffected.length} KPIs affected`);
2369
+ findings.push(`${businessNeeds.crossTeamImpact.length} cross-team impacts`);
2370
+ break;
2371
+ case qx_1.QXHeuristic.USER_VS_BUSINESS_BALANCE:
2372
+ const balanceScore = 100 - Math.abs(userNeeds.alignmentScore - businessNeeds.alignmentScore);
2373
+ score = balanceScore;
2374
+ if (balanceScore > 80) {
2375
+ findings.push('Good balance between user and business needs');
2376
+ }
2377
+ else {
2378
+ issues.push({ description: 'Imbalance between user and business priorities', severity: 'medium' });
2379
+ recommendations.push('Align user and business objectives more closely');
2380
+ }
2381
+ break;
2382
+ case qx_1.QXHeuristic.KPI_IMPACT_ANALYSIS:
2383
+ score = businessNeeds.impactsKPIs ? 85 : 50;
2384
+ findings.push(`KPIs impacted: ${businessNeeds.kpisAffected.join(', ')}`);
2385
+ if (businessNeeds.compromisesUX) {
2386
+ issues.push({ description: 'Business ease compromises user experience', severity: 'high' });
2387
+ score -= 20;
2388
+ }
2389
+ break;
2390
+ case qx_1.QXHeuristic.ORACLE_PROBLEM_DETECTION:
2391
+ // This is handled separately, score based on whether we can detect issues
2392
+ score = 75;
2393
+ findings.push('Oracle problem detection capability active');
2394
+ break;
2395
+ case qx_1.QXHeuristic.WHAT_MUST_NOT_CHANGE:
2396
+ score = 80;
2397
+ if (context.domMetrics?.semanticStructure?.hasMain) {
2398
+ findings.push('Main content structure is immutable');
2399
+ }
2400
+ if (context.accessibility && (context.accessibility.focusableElementsCount || 0) > 0) {
2401
+ findings.push('Keyboard navigation support must be maintained');
2402
+ }
2403
+ break;
2404
+ case qx_1.QXHeuristic.SUPPORTING_DATA_ANALYSIS:
2405
+ score = 75;
2406
+ const hasData = (context.domMetrics?.forms || 0) > 0 || (context.domMetrics?.interactiveElements || 0) > 20;
2407
+ if (hasData) {
2408
+ score = 82;
2409
+ findings.push('Sufficient data points for informed decision-making');
2410
+ }
2411
+ else {
2412
+ score = 60;
2413
+ issues.push({ description: 'Limited data for comprehensive analysis', severity: 'medium' });
2414
+ recommendations.push('Collect more user interaction data');
2415
+ }
2416
+ break;
2417
+ case qx_1.QXHeuristic.COMPETITIVE_ANALYSIS:
2418
+ score = 70; // Baseline - actual comparison would need competitor data
2419
+ findings.push('Competitive analysis capability available');
2420
+ if (context.domMetrics?.semanticStructure?.hasNav && context.domMetrics?.interactiveElements && context.domMetrics.interactiveElements > 15) {
2421
+ score = 78;
2422
+ findings.push('Navigation and interaction patterns follow industry standards');
2423
+ }
2424
+ else {
2425
+ recommendations.push('Compare interaction patterns with leading competitors');
2426
+ }
2427
+ break;
2428
+ case qx_1.QXHeuristic.DOMAIN_INSPIRATION:
2429
+ score = 72;
2430
+ const hasModernElements = context.accessibility && (context.accessibility.ariaLabelsCount || 0) > 0;
2431
+ if (hasModernElements) {
2432
+ score = 80;
2433
+ findings.push('Modern accessibility patterns show domain inspiration');
2434
+ }
2435
+ else {
2436
+ recommendations.push('Research domain-specific design patterns and best practices');
2437
+ }
2438
+ break;
2439
+ case qx_1.QXHeuristic.INNOVATIVE_SOLUTIONS:
2440
+ score = 65; // Most sites are conventional
2441
+ const hasAdvancedFeatures = (context.accessibility?.landmarkRoles || 0) > 3;
2442
+ if (hasAdvancedFeatures) {
2443
+ score = 75;
2444
+ findings.push('Advanced accessibility features show innovative thinking');
2445
+ }
2446
+ else {
2447
+ recommendations.push('Explore innovative UX patterns to differentiate experience');
2448
+ }
2449
+ break;
2450
+ case qx_1.QXHeuristic.COUNTER_INTUITIVE_DESIGN:
2451
+ score = 85; // High score means few counter-intuitive elements (good)
2452
+ const confusingNav = !context.domMetrics?.semanticStructure?.hasNav && (context.domMetrics?.interactiveElements || 0) > 10;
2453
+ const poorStructure = !context.domMetrics?.semanticStructure?.hasHeader && !context.domMetrics?.semanticStructure?.hasFooter;
2454
+ if (confusingNav) {
2455
+ score = 45;
2456
+ issues.push({ description: 'Navigation structure may be counter-intuitive', severity: 'high' });
2457
+ recommendations.push('Add semantic navigation elements');
2458
+ }
2459
+ if (poorStructure) {
2460
+ score -= 15;
2461
+ issues.push({ description: 'Page structure lacks expected header/footer', severity: 'medium' });
2462
+ }
2463
+ if (score > 75) {
2464
+ findings.push('No counter-intuitive design patterns detected');
2465
+ }
2466
+ break;
2467
+ case qx_1.QXHeuristic.SUPPORTING_DATA_ANALYSIS:
2468
+ score = 70;
2469
+ if (context.performance)
2470
+ findings.push('Performance data available');
2471
+ if (context.accessibility)
2472
+ findings.push('Accessibility metrics available');
2473
+ if (context.domMetrics)
2474
+ findings.push('DOM structure data available');
2475
+ score = 60 + (findings.length * 10);
2476
+ break;
2477
+ case qx_1.QXHeuristic.COMPETITIVE_ANALYSIS:
2478
+ score = 65;
2479
+ findings.push('Competitive analysis capability available');
2480
+ recommendations.push('Compare with competitor sites for benchmarking');
2481
+ break;
2482
+ case qx_1.QXHeuristic.DOMAIN_INSPIRATION:
2483
+ score = 70;
2484
+ findings.push('Consider best practices from similar domains');
2485
+ break;
2486
+ case qx_1.QXHeuristic.INNOVATIVE_SOLUTIONS:
2487
+ score = 68;
2488
+ findings.push('Opportunity for innovative UX solutions');
2489
+ break;
2490
+ case qx_1.QXHeuristic.COUNTER_INTUITIVE_DESIGN:
2491
+ score = 75;
2492
+ findings.push('No counter-intuitive design patterns detected');
2493
+ break;
2494
+ default:
2495
+ // Generic heuristic evaluation based on category
2496
+ if (category === 'user-needs') {
2497
+ score = userNeeds.alignmentScore;
2498
+ }
2499
+ else if (category === 'business-needs') {
2500
+ score = businessNeeds.alignmentScore;
2501
+ }
2502
+ else if (category === 'problem') {
2503
+ score = problemAnalysis.clarityScore;
2504
+ }
2505
+ break;
2506
+ }
674
2507
  return {
675
2508
  name: heuristic,
676
- category: this.getHeuristicCategory(heuristic),
2509
+ heuristicType: heuristic,
2510
+ category,
677
2511
  applied: true,
678
- score: 75, // Placeholder
679
- findings: [],
680
- issues: [],
681
- recommendations: []
2512
+ score: Math.min(100, Math.max(0, score)),
2513
+ findings,
2514
+ issues,
2515
+ recommendations
682
2516
  };
683
2517
  }
684
2518
  getHeuristicCategory(heuristic) {
@@ -733,6 +2567,67 @@ class OracleDetector {
733
2567
  ]
734
2568
  });
735
2569
  }
2570
+ // ENHANCED: Detect contextual oracle problems even for well-built sites
2571
+ const titleLower = (context.title || '').toLowerCase();
2572
+ const descLower = (context.metadata?.description || '').toLowerCase();
2573
+ // E-commerce/Travel booking: Conversion vs UX quality
2574
+ if (titleLower.includes('hotel') || titleLower.includes('booking') || titleLower.includes('travel') ||
2575
+ titleLower.includes('shop') || titleLower.includes('store') || descLower.includes('book')) {
2576
+ if (businessNeeds.kpisAffected.some(k => k.toLowerCase().includes('conversion') || k.toLowerCase().includes('engagement'))) {
2577
+ problems.push({
2578
+ type: 'user-vs-business',
2579
+ description: 'Potential conflict between conversion optimization (business) and user experience quality (user trust)',
2580
+ severity: 'medium',
2581
+ stakeholders: ['Marketing', 'Product', 'Users'],
2582
+ resolutionApproach: [
2583
+ 'A/B test aggressive vs. subtle conversion tactics',
2584
+ 'Measure both conversion rate and user satisfaction metrics',
2585
+ 'Balance urgency messaging with transparent communication'
2586
+ ]
2587
+ });
2588
+ }
2589
+ // Price transparency oracle
2590
+ problems.push({
2591
+ type: 'unclear-criteria',
2592
+ description: 'Unclear criteria for price display timing - when to show fees, taxes, and final price',
2593
+ severity: 'medium',
2594
+ stakeholders: ['Users', 'Legal', 'Business'],
2595
+ resolutionApproach: [
2596
+ 'Define regulatory compliance requirements for price display',
2597
+ 'Balance business desire for competitive base pricing vs user need for full price transparency',
2598
+ 'Establish clear standards for fee disclosure timing'
2599
+ ]
2600
+ });
2601
+ }
2602
+ // Content sites: Quality vs. Quantity
2603
+ if (titleLower.includes('blog') || titleLower.includes('article') || titleLower.includes('news') ||
2604
+ titleLower.includes('magazine') || titleLower.includes('testing')) {
2605
+ problems.push({
2606
+ type: 'user-vs-business',
2607
+ description: 'Content depth (user need) vs. publication frequency (business engagement goals) trade-off',
2608
+ severity: 'low',
2609
+ stakeholders: ['Readers', 'Content Team', 'Editorial'],
2610
+ resolutionApproach: [
2611
+ 'Define content quality standards and acceptance criteria',
2612
+ 'Balance editorial calendar with quality thresholds',
2613
+ 'Consider mix of in-depth and quick-read content formats'
2614
+ ]
2615
+ });
2616
+ }
2617
+ // Complex sites: Technical constraints
2618
+ if ((context.domMetrics?.totalElements || 0) > 500 || (context.domMetrics?.interactiveElements || 0) > 50) {
2619
+ problems.push({
2620
+ type: 'technical-constraint',
2621
+ description: 'Platform technical limitations may restrict advanced UX features or accessibility enhancements',
2622
+ severity: 'low',
2623
+ stakeholders: ['Development', 'Product', 'Users'],
2624
+ resolutionApproach: [
2625
+ 'Evaluate platform capabilities and constraints',
2626
+ 'Prioritize features based on user impact vs. implementation complexity',
2627
+ 'Consider gradual enhancement approach'
2628
+ ]
2629
+ });
2630
+ }
736
2631
  return problems.filter(p => this.meetsMinimumSeverity(p.severity));
737
2632
  }
738
2633
  meetsMinimumSeverity(severity) {
@@ -746,23 +2641,89 @@ class OracleDetector {
746
2641
  * Impact Analyzer
747
2642
  */
748
2643
  class ImpactAnalyzer {
749
- async analyze(_context, _problemAnalysis) {
750
- // Simplified impact analysis
751
- // In real implementation, this would perform deep analysis
2644
+ async analyze(context, problemAnalysis) {
2645
+ const guiFlowEndUser = [];
2646
+ const guiFlowInternal = [];
2647
+ const userFeelings = [];
2648
+ const performance = [];
2649
+ const security = [];
2650
+ const immutableRequirements = [];
2651
+ // Analyze visible impacts
2652
+ const interactiveElements = context.domMetrics?.interactiveElements || 0;
2653
+ const forms = context.domMetrics?.forms || 0;
2654
+ if (interactiveElements > 0) {
2655
+ guiFlowEndUser.push(`${interactiveElements} interactive elements affect user journey`);
2656
+ }
2657
+ if (forms > 0) {
2658
+ guiFlowEndUser.push(`${forms} forms impact user input flows`);
2659
+ }
2660
+ // User feelings based on quality metrics
2661
+ const altCoverage = context.accessibility?.altTextsCoverage || 0;
2662
+ if (altCoverage > 80) {
2663
+ userFeelings.push('Positive - Good accessibility creates inclusive experience');
2664
+ }
2665
+ else if (altCoverage < 50) {
2666
+ userFeelings.push('Frustrated - Poor accessibility excludes some users');
2667
+ }
2668
+ const loadTime = context.performance?.loadTime || 0;
2669
+ if (loadTime > 3000) {
2670
+ userFeelings.push('Impatient - Slow load time causes frustration');
2671
+ }
2672
+ else if (loadTime < 2000) {
2673
+ userFeelings.push('Satisfied - Fast load time enhances experience');
2674
+ }
2675
+ if (context.errorIndicators?.hasErrorMessages) {
2676
+ userFeelings.push('Confused - Visible errors reduce confidence');
2677
+ }
2678
+ // Analyze invisible impacts
2679
+ if (loadTime > 2000) {
2680
+ performance.push(`Load time ${loadTime}ms impacts user retention`);
2681
+ }
2682
+ if (!context.metadata?.viewport) {
2683
+ performance.push('Missing viewport tag affects mobile performance');
2684
+ }
2685
+ // Immutable requirements
2686
+ if (context.domMetrics?.semanticStructure?.hasMain) {
2687
+ immutableRequirements.push('Must maintain main content accessibility');
2688
+ }
2689
+ if (context.accessibility && (context.accessibility.focusableElementsCount || 0) > 0) {
2690
+ immutableRequirements.push('Must support keyboard navigation');
2691
+ }
2692
+ if (problemAnalysis.complexity === 'complex') {
2693
+ immutableRequirements.push('Must maintain system stability with complex interactions');
2694
+ }
2695
+ // Calculate impact scores
2696
+ let visibleScore = 50;
2697
+ if (guiFlowEndUser.length > 0)
2698
+ visibleScore += 15;
2699
+ if (userFeelings.some(f => f.includes('Positive') || f.includes('Satisfied')))
2700
+ visibleScore += 20;
2701
+ if (userFeelings.some(f => f.includes('Frustrated') || f.includes('Confused')))
2702
+ visibleScore -= 15;
2703
+ visibleScore = Math.max(0, Math.min(100, visibleScore));
2704
+ let invisibleScore = 50;
2705
+ if (performance.length === 0)
2706
+ invisibleScore += 20;
2707
+ if (security.length === 0)
2708
+ invisibleScore += 10;
2709
+ invisibleScore = Math.max(0, Math.min(100, invisibleScore));
2710
+ const overallImpactScore = Math.round((visibleScore + invisibleScore) / 2);
752
2711
  return {
753
2712
  visible: {
754
2713
  guiFlow: {
755
- forEndUser: [],
756
- forInternalUser: []
2714
+ forEndUser: guiFlowEndUser,
2715
+ forInternalUser: guiFlowInternal
757
2716
  },
758
- userFeelings: []
2717
+ userFeelings,
2718
+ score: visibleScore
759
2719
  },
760
2720
  invisible: {
761
- performance: [],
762
- security: []
2721
+ performance,
2722
+ security,
2723
+ score: invisibleScore
763
2724
  },
764
- immutableRequirements: [],
765
- overallImpactScore: 30 // Lower is better
2725
+ immutableRequirements,
2726
+ overallImpactScore
766
2727
  };
767
2728
  }
768
2729
  }