quality-intelligence-engine 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +123 -0
  2. package/dist/bin/qi.js +27 -0
  3. package/dist/src/adapters/playwright-folder.adapter.js +68 -0
  4. package/dist/src/adapters/playwright-json.adapter.js +46 -0
  5. package/dist/src/adapters/playwright.adapter.js +36 -0
  6. package/dist/src/cli/qi-test.js +20 -0
  7. package/dist/src/cli/qi.js +59 -0
  8. package/dist/src/cli/ui/divider.js +10 -0
  9. package/dist/src/cli/ui/failureLogger.js +24 -0
  10. package/dist/src/configLoader.js +60 -0
  11. package/dist/src/console/issue-view.js +122 -0
  12. package/dist/src/failure-analysis/failure-analyzer.js +38 -0
  13. package/dist/src/final-run.js +130 -0
  14. package/dist/src/final-runner.js +27 -0
  15. package/dist/src/fixtures/networkCollector.js +18 -0
  16. package/dist/src/history/failure-history.store.js +25 -0
  17. package/dist/src/history/failure-history.types.js +4 -0
  18. package/dist/src/history/failure-trend.analyzer.js +31 -0
  19. package/dist/src/index.js +10 -0
  20. package/dist/src/integrations/ci/ci-annotator.js +26 -0
  21. package/dist/src/intelligence/confidence-calibration.engine.js +21 -0
  22. package/dist/src/intelligence/decision-intelligence/confidence.engine.js +16 -0
  23. package/dist/src/intelligence/decision-intelligence/decision.config.js +21 -0
  24. package/dist/src/intelligence/decision-intelligence/decision.engine.js +23 -0
  25. package/dist/src/intelligence/decision-intelligence/decision.types.js +2 -0
  26. package/dist/src/intelligence/failure-fingerprinting/failure.classifier.js +20 -0
  27. package/dist/src/intelligence/failure-fingerprinting/failure.store.js +39 -0
  28. package/dist/src/intelligence/failure-fingerprinting/failure.tracker.js +15 -0
  29. package/dist/src/intelligence/failure-fingerprinting/fingerprint.generator.js +28 -0
  30. package/dist/src/intelligence/failure-fingerprinting/fingerprint.types.js +2 -0
  31. package/dist/src/intelligence/flakiness-intelligence/flakiness.analyzer.js +21 -0
  32. package/dist/src/intelligence/flakiness-intelligence/flakiness.fingerprint.js +16 -0
  33. package/dist/src/intelligence/flakiness-intelligence/flakiness.store.js +24 -0
  34. package/dist/src/intelligence/flakiness-intelligence/flakiness.tracker.js +28 -0
  35. package/dist/src/intelligence/flakiness-intelligence/flakiness.types.js +2 -0
  36. package/dist/src/intelligence/intelligence.pipeline.js +17 -0
  37. package/dist/src/intelligence/llm-explainer.js +19 -0
  38. package/dist/src/intelligence/root-cause/rootcause.engine.js +25 -0
  39. package/dist/src/intelligence/trend-intelligence/trend.engine.js +25 -0
  40. package/dist/src/markdownWriter.js +64 -0
  41. package/dist/src/normalizer.js +17 -0
  42. package/dist/src/passAnalyzer.js +38 -0
  43. package/dist/src/pipeline/ai.summarizer.js +32 -0
  44. package/dist/src/pipeline/failure-analysis.pipeline.js +11 -0
  45. package/dist/src/pipeline/failure-grouping.pipeline.js +47 -0
  46. package/dist/src/reporter.js +46 -0
  47. package/dist/src/rules.js +32 -0
  48. package/dist/src/runManager.js +19 -0
  49. package/dist/src/runner.js +13 -0
  50. package/dist/src/runtime/networkCollector.js +34 -0
  51. package/dist/src/stackParser.js +17 -0
  52. package/dist/src/test-run.js +48 -0
  53. package/dist/src/types/analysis-result.js +2 -0
  54. package/dist/src/types/failure.types.js +3 -0
  55. package/dist/src/types/playwright-failure.js +2 -0
  56. package/dist/src/types.js +5 -0
  57. package/dist/src/utils/artifact.locator.js +35 -0
  58. package/dist/src/utils/confidence-constants.js +90 -0
  59. package/dist/src/utils/file-utils.js +121 -0
  60. package/dist/src/v2/llm/llm-advisor.js +17 -0
  61. package/dist/src/v2/pipeline/v2-intelligence.pipeline.js +17 -0
  62. package/dist/src/v2/self-healing/self-healer.js +44 -0
  63. package/dist/src/v2/trace-intelligence/trace-analyzer.js +33 -0
  64. package/dist/src/v2-test-run.js +59 -0
  65. package/package.json +23 -0
package/README.md ADDED
@@ -0,0 +1,123 @@
1
+ # Quality Intelligence Engine
2
+
3
+ **Deterministic Test Failure Classification & Analysis for Playwright**
4
+
5
+ A rule-based intelligent system that automatically classifies test failures, provides root cause analysis, and delivers actionable insights—without relying on AI black boxes.
6
+
7
+ ---
8
+
9
+ ## 🎯 Overview
10
+
11
+ The Quality Intelligence Engine analyzes Playwright test failures and classifies them into:
12
+
13
+ - **API_BUG**: Backend/API failures (authentication errors, data mismatches, server errors)
14
+ - **UI_BUG**: Frontend/UI issues (visibility problems, rendering failures, element issues)
15
+
16
+ Each classification comes with:
17
+ - ✅ **Confidence score** (0-100%) - How certain we are about the classification
18
+ - ✅ **Root cause analysis** - What went wrong and why
19
+ - ✅ **Evidence** - Signals that led to the classification
20
+ - ✅ **Diagnosis** - Step-by-step breakdown of the failure
21
+ - ✅ **Suggested actions** - Concrete next steps to fix the issue
22
+
23
+ ---
24
+
25
+ ## 🚀 Quick Start
26
+
27
+ ### Installation
28
+
29
+ ```bash
30
+ npm install
31
+ ```
32
+
33
+ ### Run Analysis
34
+
35
+ ```bash
36
+ # Run Playwright tests
37
+ npx playwright test
38
+
39
+ # Run Quality Intelligence Engine
40
+ npm start
41
+ ```
42
+
43
+ The engine will:
44
+ 1. Read test results from `playwright-report/report.json`
45
+ 2. Classify failures using the taxonomy
46
+ 3. Generate detailed analysis reports
47
+ 4. Output results to `output/` directory
48
+
49
+ ---
50
+
51
+ ## 📁 Project Structure
52
+
53
+ ```
54
+ quality-intelligence-engine/
55
+ ├── src/
56
+ │ ├── intelligence/ # Classification algorithms
57
+ │ ├── pipeline/ # Data processing
58
+ │ ├── utils/ # Utilities
59
+ │ ├── types.ts # Type definitions
60
+ │ ├── normalizer.ts # Input normalization
61
+ │ ├── configLoader.ts # Configuration
62
+ │ └── reporter.ts # Output generation
63
+ ├── tests/ # Example test cases
64
+ ├── config/ # Configuration files
65
+ └── playwright.config.ts # Playwright configuration
66
+ ```
67
+
68
+ ---
69
+
70
+ ## ⚙️ Configuration
71
+
72
+ Edit `config/agent.config.json`:
73
+
74
+ ```json
75
+ {
76
+ "engine": {
77
+ "mode": "standard",
78
+ "confidenceThresholds": {
79
+ "fail": 0.85,
80
+ "passRisk": 0.6
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ ---
87
+
88
+ ## 🏷️ Taxonomy System
89
+
90
+ Tests are tagged with their expected failure type:
91
+
92
+ ```typescript
93
+ test('Data | NUMERIC_MISMATCH | inventory count wrong', async ({ page }) => {
94
+ // Test code...
95
+ });
96
+ ```
97
+
98
+ ---
99
+
100
+ ## 📊 Confidence Scoring
101
+
102
+ Deterministic, explainable confidence calculations:
103
+
104
+ - **Base Confidence**: 0.6 - 0.95
105
+ - **Adjustments**: +0.05 - +0.1 based on signals
106
+ - **Maximum**: 0.95 (never 100% certain)
107
+
108
+ ---
109
+
110
+ ## 🔧 Development
111
+
112
+ ```bash
113
+ # Run tests
114
+ npx playwright test
115
+
116
+ # Type check
117
+ npx tsc --noEmit
118
+ ```
119
+
120
+ ---
121
+
122
+ **Version**: 1.0.0 (Refactored)
123
+ **Last Updated**: 2026-01-29
package/dist/bin/qi.js ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const path_1 = __importDefault(require("path"));
8
+ /**
9
+ * CLI entry point for Quality Intelligence
10
+ * Compiled and executed as CommonJS
11
+ */
12
+ async function main() {
13
+ const args = process.argv.slice(2);
14
+ const playwrightResultsDir = args.find(a => !a.startsWith("--")) ?? "playwright-results";
15
+ const showIssues = args.includes("--issue") || args.includes("--issues");
16
+ // Resolve project root (works after npm install)
17
+ const projectRoot = path_1.default.resolve(__dirname, "..");
18
+ // IMPORTANT: call compiled JS, never TS
19
+ const finalRunPath = path_1.default.join(projectRoot, "src", "final-run.js");
20
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
21
+ const { runFinal } = require(finalRunPath);
22
+ runFinal(playwrightResultsDir, showIssues);
23
+ }
24
+ main().catch(err => {
25
+ console.error("QI failed:", err);
26
+ process.exit(1);
27
+ });
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ // src/adapters/playwright-folder.adapter.ts
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.parsePlaywrightFailureFolders = parsePlaywrightFailureFolders;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ /**
11
+ * Improved Playwright failure folder parser
12
+ * Focus: reduce UNKNOWN classifications
13
+ */
14
+ function parsePlaywrightFailureFolders(resultsDir) {
15
+ const fullDir = path_1.default.resolve(resultsDir);
16
+ if (!fs_1.default.existsSync(fullDir)) {
17
+ throw new Error(`Playwright results directory not found: ${fullDir}`);
18
+ }
19
+ const entries = fs_1.default.readdirSync(fullDir, { withFileTypes: true });
20
+ const failures = [];
21
+ for (const entry of entries) {
22
+ if (!entry.isDirectory())
23
+ continue;
24
+ const name = entry.name;
25
+ if (!name.includes("FAIL"))
26
+ continue;
27
+ const lower = name.toLowerCase();
28
+ let classification = "UNKNOWN";
29
+ // 🔢 Numeric / data mismatches
30
+ if (lower.includes("numeric-mismatch") ||
31
+ lower.includes("count") ||
32
+ lower.includes("total") ||
33
+ lower.includes("sum") ||
34
+ lower.includes("price")) {
35
+ classification = "NUMERIC_MISMATCH";
36
+ }
37
+ // 🔐 Auth / permission issues
38
+ else if (lower.includes("unauthorized") ||
39
+ lower.includes("forbidden") ||
40
+ lower.includes("permission")) {
41
+ classification = "AUTH_BUG";
42
+ }
43
+ // ⏱ Timeouts / waits
44
+ else if (lower.includes("timeout") ||
45
+ lower.includes("timed-out") ||
46
+ lower.includes("wait")) {
47
+ classification = "TIMEOUT";
48
+ }
49
+ // 🧭 UI / locator issues
50
+ else if (lower.includes("locator") ||
51
+ lower.includes("not-visible") ||
52
+ lower.includes("hidden") ||
53
+ lower.includes("detached")) {
54
+ classification = "UI_STATE";
55
+ }
56
+ // 🔁 Retry / flaky signal
57
+ else if (lower.includes("retry")) {
58
+ classification = "FLAKY";
59
+ }
60
+ failures.push({
61
+ testName: name,
62
+ classification,
63
+ failureType: classification,
64
+ message: `Derived from Playwright folder name: ${name}`
65
+ });
66
+ }
67
+ return failures;
68
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ // src/adapters/playwright-json.adapter.ts
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.parsePlaywrightJsonReport = parsePlaywrightJsonReport;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ /**
11
+ * Converts Playwright JSON report → FailureAnalysisResult[]
12
+ */
13
+ function parsePlaywrightJsonReport(reportPath) {
14
+ const fullPath = path_1.default.resolve(reportPath);
15
+ const raw = fs_1.default.readFileSync(fullPath, "utf-8");
16
+ const report = JSON.parse(raw);
17
+ const failures = [];
18
+ for (const suite of report.suites ?? []) {
19
+ for (const spec of suite.specs ?? []) {
20
+ for (const test of spec.tests ?? []) {
21
+ for (const result of test.results ?? []) {
22
+ if (result.status === "failed") {
23
+ const error = result.error?.message ?? "";
24
+ let classification = "UNKNOWN";
25
+ if (error.includes("toHaveCount")) {
26
+ classification = "NUMERIC_MISMATCH";
27
+ }
28
+ else if (error.includes("timeout")) {
29
+ classification = "TIMEOUT";
30
+ }
31
+ else if (error.includes("locator")) {
32
+ classification = "LOCATOR_CHANGED";
33
+ }
34
+ failures.push({
35
+ testName: test.title,
36
+ classification,
37
+ failureType: classification,
38
+ message: error
39
+ });
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ return failures;
46
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readPlaywrightFailures = readPlaywrightFailures;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ function readPlaywrightFailures(reportDir) {
10
+ const reportFile = path_1.default.join(reportDir, 'report.json');
11
+ if (!fs_1.default.existsSync(reportFile)) {
12
+ throw new Error(`Playwright JSON report not found.\nExpected at: ${reportFile}`);
13
+ }
14
+ const raw = JSON.parse(fs_1.default.readFileSync(reportFile, 'utf-8'));
15
+ const failures = [];
16
+ for (const suite of raw.suites ?? []) {
17
+ for (const spec of suite.specs ?? []) {
18
+ for (const test of spec.tests ?? []) {
19
+ const failedResults = test.results?.filter((r) => r.status === 'failed');
20
+ if (!failedResults || failedResults.length === 0)
21
+ continue;
22
+ const firstFailure = failedResults[0];
23
+ const error = firstFailure.error;
24
+ failures.push({
25
+ testName: spec.title,
26
+ errorMessage: error?.message ?? 'Unknown error',
27
+ stack: error?.stack,
28
+ filePath: error?.location?.file ?? spec.file,
29
+ lineNumber: error?.location?.line ?? spec.line ?? 0,
30
+ retryCount: failedResults.length - 1
31
+ });
32
+ }
33
+ }
34
+ }
35
+ return failures;
36
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runQiTest = runQiTest;
4
+ const child_process_1 = require("child_process");
5
+ function runQiTest(args) {
6
+ const playwrightArgs = [
7
+ 'playwright',
8
+ 'test',
9
+ '--config',
10
+ 'playwright.config.ts',
11
+ ...args
12
+ ];
13
+ const child = (0, child_process_1.spawn)('npx', playwrightArgs, {
14
+ stdio: 'inherit',
15
+ shell: true
16
+ });
17
+ child.on('exit', code => {
18
+ process.exit(code ?? 0);
19
+ });
20
+ }
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ // src/cli/qi.ts
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.runQiCli = runQiCli;
6
+ const runner_1 = require("../runner");
7
+ /**
8
+ * Core CLI runner (reusable)
9
+ */
10
+ async function runQiCli(input) {
11
+ const results = await (0, runner_1.runQualityIntelligence)(input);
12
+ if (!Array.isArray(results) || results.length === 0) {
13
+ console.log("No failures detected.");
14
+ return;
15
+ }
16
+ for (const f of results) {
17
+ const testName = f.testName ?? "UNKNOWN_TEST";
18
+ const isExpected = testName.includes("EXPECTED");
19
+ console.log("=================================");
20
+ console.log(`Test Name : ${testName}${isExpected ? " (EXPECTED)" : ""}`);
21
+ console.log(`Failure Type : ${f.failureType ?? f.classification}`);
22
+ console.log(`Confidence : ${f.confidence ?? "N/A"}\n`);
23
+ if (f.rootCause?.length) {
24
+ console.log("Root Cause:");
25
+ for (const line of f.rootCause) {
26
+ console.log(` - ${line}`);
27
+ }
28
+ console.log();
29
+ }
30
+ if (f.evidence?.length) {
31
+ console.log("Evidence:");
32
+ for (const line of f.evidence) {
33
+ console.log(` - ${line}`);
34
+ }
35
+ console.log();
36
+ }
37
+ if (f.diagnosis?.length) {
38
+ console.log("Diagnosis:");
39
+ for (const line of f.diagnosis) {
40
+ console.log(` - ${line}`);
41
+ }
42
+ console.log();
43
+ }
44
+ }
45
+ }
46
+ /**
47
+ * CLI ENTRY POINT
48
+ * Allows: npx qi <path>
49
+ */
50
+ async function main() {
51
+ const input = process.argv[2] ?? "playwright-results";
52
+ await runQiCli(input);
53
+ }
54
+ if (process.argv[1]?.includes("qi")) {
55
+ main().catch(err => {
56
+ console.error("QI CLI failed:", err);
57
+ process.exit(1);
58
+ });
59
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.divider = divider;
4
+ function divider(title, width = 60) {
5
+ const line = '━'.repeat(width);
6
+ if (!title) {
7
+ return `\n${line}\n`;
8
+ }
9
+ return `\n${line}\n${title}\n${line}\n`;
10
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logFailure = logFailure;
4
+ function logFailure(input) {
5
+ const confidencePercent = Math.round(input.confidence * 100);
6
+ console.log(`
7
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
8
+ ❌ Automation Test Failure
9
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
10
+
11
+ Test Name : ${input.testName}
12
+ Failure Type : ${input.failureType}
13
+ Severity : ${input.severity}
14
+ Priority : ${input.priority}
15
+ Confidence : ${confidencePercent}%
16
+
17
+ Failure Location:
18
+ - Test File : ${input.file}
19
+ - Line Number : ${input.line}
20
+
21
+ Root Cause:
22
+ - ${input.rootCause}
23
+ `);
24
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadConfig = loadConfig;
4
+ const file_utils_1 = require("./utils/file-utils");
5
+ /**
6
+ * Validate configuration object structure
7
+ */
8
+ function validateConfig(config) {
9
+ if (typeof config !== 'object' || config === null) {
10
+ return false;
11
+ }
12
+ const cfg = config;
13
+ // Validate engine section
14
+ if (typeof cfg.engine !== 'object' ||
15
+ cfg.engine === null) {
16
+ return false;
17
+ }
18
+ const engine = cfg.engine;
19
+ if (typeof engine.mode !== 'string' ||
20
+ typeof engine.confidenceThresholds !== 'object') {
21
+ return false;
22
+ }
23
+ return true;
24
+ }
25
+ /**
26
+ * Load and validate configuration
27
+ */
28
+ function loadConfig() {
29
+ try {
30
+ const configPath = (0, file_utils_1.resolvePath)('config', 'agent.config.json');
31
+ const config = (0, file_utils_1.readJsonFile)(configPath);
32
+ if (!validateConfig(config)) {
33
+ throw new Error('Invalid configuration structure');
34
+ }
35
+ return config;
36
+ }
37
+ catch (error) {
38
+ console.error('❌ Failed to load configuration:', error);
39
+ console.error('\n💡 Using default configuration');
40
+ // Return safe defaults
41
+ return {
42
+ engine: {
43
+ mode: 'standard',
44
+ confidenceThresholds: {
45
+ fail: 0.85,
46
+ passRisk: 0.6
47
+ }
48
+ },
49
+ output: {
50
+ writeMarkdown: true,
51
+ maxHistoryRuns: 10
52
+ },
53
+ passAnalysis: {
54
+ enable: true,
55
+ reportFlakyPass: true,
56
+ reportApiWarnings: true
57
+ }
58
+ };
59
+ }
60
+ }
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ // src/console/issue-view.ts
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.extractIssues = extractIssues;
8
+ exports.filterIssues = filterIssues;
9
+ exports.printIssues = printIssues;
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ /* ---------------- Issue Core ---------------- */
13
+ function extractIssues(groupedCounts, confidence) {
14
+ return Object.entries(groupedCounts).map(([type, count]) => ({
15
+ type,
16
+ occurrences: count,
17
+ confidence
18
+ }));
19
+ }
20
+ /* ---------------- Decision Logic ---------------- */
21
+ function computeIssueDecision(issue) {
22
+ switch (issue.type) {
23
+ case "FLAKY":
24
+ return {
25
+ autoHeal: true,
26
+ reason: "Retry-based flaky behavior detected; stabilization is safe"
27
+ };
28
+ case "NUMERIC_MISMATCH":
29
+ return {
30
+ autoHeal: false,
31
+ reason: "Numeric mismatch likely backend or data-related"
32
+ };
33
+ default:
34
+ return {
35
+ autoHeal: false,
36
+ reason: "Insufficient signal to safely auto-heal"
37
+ };
38
+ }
39
+ }
40
+ /* ---------------- Owner Suggestion ---------------- */
41
+ function computeIssueOwner(issue) {
42
+ switch (issue.type) {
43
+ case "NUMERIC_MISMATCH":
44
+ return {
45
+ team: "Backend",
46
+ reason: "Mismatch usually originates from API data or backend calculations"
47
+ };
48
+ case "FLAKY":
49
+ return {
50
+ team: "QA",
51
+ reason: "Flaky behavior typically caused by timing, waits, or test stability"
52
+ };
53
+ case "UNKNOWN":
54
+ default:
55
+ return {
56
+ team: "Frontend",
57
+ reason: "Failure source unclear; frontend investigation needed first"
58
+ };
59
+ }
60
+ }
61
+ /* ---------------- Evidence Picker ---------------- */
62
+ function pickIssueEvidence(failureFolder) {
63
+ if (!fs_1.default.existsSync(failureFolder))
64
+ return {};
65
+ const files = fs_1.default.readdirSync(failureFolder);
66
+ const screenshot = files.find(f => f.startsWith("test-failed") && f.endsWith(".png"));
67
+ const trace = files.includes("trace.zip") ? "trace.zip" : undefined;
68
+ return {
69
+ screenshot: screenshot
70
+ ? path_1.default.join(failureFolder, screenshot)
71
+ : undefined,
72
+ trace: trace
73
+ ? path_1.default.join(failureFolder, trace)
74
+ : undefined
75
+ };
76
+ }
77
+ /* ---------------- Filtering ---------------- */
78
+ function filterIssues(issues, filter) {
79
+ let result = [...issues];
80
+ if (filter.keyword) {
81
+ const k = filter.keyword.toLowerCase();
82
+ result = result.filter(i => i.type.toLowerCase().includes(k));
83
+ }
84
+ result.sort((a, b) => b.occurrences * b.confidence -
85
+ a.occurrences * a.confidence);
86
+ if (filter.topN !== undefined) {
87
+ result = result.slice(0, filter.topN);
88
+ }
89
+ return result;
90
+ }
91
+ /* ---------------- Console Output ---------------- */
92
+ function printIssues(issues, failureFolder) {
93
+ if (issues.length === 0) {
94
+ console.log("No matching issues found.");
95
+ return;
96
+ }
97
+ const evidence = pickIssueEvidence(failureFolder);
98
+ issues.forEach((issue, index) => {
99
+ const decision = computeIssueDecision(issue);
100
+ const owner = computeIssueOwner(issue);
101
+ console.log(`================ ISSUE #${index + 1} =================`);
102
+ console.log(`Type : ${issue.type}`);
103
+ console.log(`Occurrences : ${issue.occurrences}`);
104
+ console.log(`Confidence : ${issue.confidence}`);
105
+ console.log("Decision:");
106
+ console.log(` - Auto-heal: ${decision.autoHeal ? "YES" : "NO"}`);
107
+ console.log(` - Reason : ${decision.reason}`);
108
+ console.log("Owner:");
109
+ console.log(` - Team : ${owner.team}`);
110
+ console.log(` - Why : ${owner.reason}`);
111
+ if (evidence.screenshot || evidence.trace) {
112
+ console.log("\nEvidence:");
113
+ if (evidence.screenshot) {
114
+ console.log(` Screenshot : ${evidence.screenshot}`);
115
+ }
116
+ if (evidence.trace) {
117
+ console.log(` Trace : npx playwright show-trace ${evidence.trace}`);
118
+ }
119
+ }
120
+ console.log("======================================================");
121
+ });
122
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ // src/failure-analysis/failure-analyzer.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.FailureAnalyzer = void 0;
5
+ /**
6
+ * FailureAnalyzer
7
+ * ----------------
8
+ * Phase 1 core analyzer.
9
+ * This class is intentionally simple and stable.
10
+ * Do NOT add history, trends, or intelligence here.
11
+ */
12
+ class FailureAnalyzer {
13
+ analyze(input) {
14
+ if (!input || !Array.isArray(input)) {
15
+ return [];
16
+ }
17
+ return input.map((item) => {
18
+ // Minimal safe classification logic
19
+ if (item?.type === "NUMERIC_MISMATCH") {
20
+ return {
21
+ classification: "NUMERIC_MISMATCH",
22
+ message: item.message
23
+ };
24
+ }
25
+ if (item?.type === "API_BUG") {
26
+ return {
27
+ classification: "API_BUG",
28
+ message: item.message
29
+ };
30
+ }
31
+ return {
32
+ classification: "UNKNOWN",
33
+ message: item?.message
34
+ };
35
+ });
36
+ }
37
+ }
38
+ exports.FailureAnalyzer = FailureAnalyzer;