quality-intelligence-engine 2.2.18 → 2.2.22

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/dist/bin/qi.js +3 -3
  2. package/dist/src/adapters/playwright-folder.adapter.js +68 -0
  3. package/dist/src/adapters/playwright-json.adapter.js +46 -0
  4. package/dist/src/adapters/playwright.adapter.js +36 -0
  5. package/dist/src/cli/qi-test.js +20 -0
  6. package/dist/src/cli/qi.js +59 -0
  7. package/dist/src/cli/ui/divider.js +10 -0
  8. package/dist/src/cli/ui/failureLogger.js +24 -0
  9. package/dist/src/configLoader.js +60 -0
  10. package/dist/src/console/issue-view.js +81 -0
  11. package/dist/src/failure-analysis/failure-analyzer.js +38 -0
  12. package/dist/src/final-run.js +131 -0
  13. package/dist/src/final-runner.js +27 -0
  14. package/dist/src/fixtures/networkCollector.js +18 -0
  15. package/dist/src/history/failure-history.store.js +25 -0
  16. package/dist/src/history/failure-history.types.js +4 -0
  17. package/dist/src/history/failure-trend.analyzer.js +31 -0
  18. package/dist/src/index.js +10 -0
  19. package/dist/src/integrations/ci/ci-annotator.js +26 -0
  20. package/dist/src/intelligence/confidence-calibration.engine.js +21 -0
  21. package/dist/src/intelligence/decision-intelligence/confidence.engine.js +16 -0
  22. package/dist/src/intelligence/decision-intelligence/decision.config.js +21 -0
  23. package/dist/src/intelligence/decision-intelligence/decision.engine.js +23 -0
  24. package/dist/src/intelligence/decision-intelligence/decision.types.js +2 -0
  25. package/dist/src/intelligence/failure-fingerprinting/failure.classifier.js +32 -0
  26. package/dist/src/intelligence/failure-fingerprinting/failure.store.js +17 -0
  27. package/dist/src/intelligence/failure-fingerprinting/failure.tracker.js +19 -0
  28. package/dist/src/intelligence/failure-fingerprinting/fingerprint.generator.js +28 -0
  29. package/dist/src/intelligence/failure-fingerprinting/fingerprint.types.js +2 -0
  30. package/dist/src/intelligence/flakiness-intelligence/flakiness.analyzer.js +21 -0
  31. package/dist/src/intelligence/flakiness-intelligence/flakiness.fingerprint.js +16 -0
  32. package/dist/src/intelligence/flakiness-intelligence/flakiness.store.js +24 -0
  33. package/dist/src/intelligence/flakiness-intelligence/flakiness.tracker.js +28 -0
  34. package/dist/src/intelligence/flakiness-intelligence/flakiness.types.js +2 -0
  35. package/dist/src/intelligence/intelligence.pipeline.js +17 -0
  36. package/dist/src/intelligence/llm-explainer.js +19 -0
  37. package/dist/src/intelligence/root-cause/rootcause.engine.js +25 -0
  38. package/dist/src/intelligence/trend-intelligence/trend.engine.js +25 -0
  39. package/dist/src/markdownWriter.js +64 -0
  40. package/dist/src/normalizer.js +17 -0
  41. package/dist/src/passAnalyzer.js +38 -0
  42. package/dist/src/pipeline/ai.summarizer.js +32 -0
  43. package/dist/src/pipeline/failure-analysis.pipeline.js +11 -0
  44. package/dist/src/pipeline/failure-grouping.pipeline.js +59 -0
  45. package/dist/src/playwright/qi.reporter.js +17 -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 +118 -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 +1 -1
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeFailureTrends = analyzeFailureTrends;
4
+ function analyzeFailureTrends(history) {
5
+ if (history.length < 2)
6
+ return null;
7
+ const recent = history.slice(-5);
8
+ const categoryCount = {};
9
+ for (const h of recent) {
10
+ categoryCount[h.dominantCategory] =
11
+ (categoryCount[h.dominantCategory] || 0) + 1;
12
+ }
13
+ const [dominantCategory, occurrences] = Object.entries(categoryCount).sort((a, b) => b[1] - a[1])[0];
14
+ const first = recent[0].confidence;
15
+ const last = recent[recent.length - 1].confidence;
16
+ const confidenceTrend = last > first ? "increasing" :
17
+ last < first ? "decreasing" :
18
+ "stable";
19
+ const signal = occurrences >= 3 && confidenceTrend === "increasing"
20
+ ? "ESCALATING"
21
+ : occurrences >= 3
22
+ ? "STABLE"
23
+ : "NOISY";
24
+ return {
25
+ recurring: occurrences >= 2,
26
+ dominantCategory,
27
+ occurrences,
28
+ confidenceTrend,
29
+ signal
30
+ };
31
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const failure_grouping_pipeline_1 = require("./pipeline/failure-grouping.pipeline");
4
+ const fakeRun = [
5
+ { classification: "NUMERIC_MISMATCH", message: "price mismatch" },
6
+ { classification: "NUMERIC_MISMATCH", message: "tax mismatch" },
7
+ { classification: "API_BUG", message: "500 error" }
8
+ ];
9
+ const result = (0, failure_grouping_pipeline_1.groupFailures)(fakeRun);
10
+ console.log(JSON.stringify(result, null, 2));
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.emitCIAnnotation = emitCIAnnotation;
4
+ function emitCIAnnotation(input) {
5
+ // -----------------------------
6
+ // GitHub Actions annotation
7
+ // -----------------------------
8
+ if (process.env.GITHUB_ACTIONS) {
9
+ console.log(`::error file=${input.file},line=${input.line},title=${sanitize(input.testName)}::${sanitize(`${input.rootCause} (confidence ${(input.confidence / 100).toFixed(2)})`)}`);
10
+ }
11
+ // -----------------------------
12
+ // GitLab CI annotation (job log)
13
+ // -----------------------------
14
+ if (process.env.GITLAB_CI) {
15
+ console.log(`
16
+ ❌ CI Failure Annotation
17
+ Test : ${input.testName}
18
+ File : ${input.file}:${input.line}
19
+ Root Cause : ${input.rootCause}
20
+ Confidence : ${(input.confidence / 100).toFixed(2)}
21
+ `);
22
+ }
23
+ }
24
+ function sanitize(value) {
25
+ return value.replace(/[\n\r]/g, ' ');
26
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ // src/intelligence/confidence-calibration.engine.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.explainConfidence = explainConfidence;
5
+ function explainConfidence(summary) {
6
+ const explanation = [];
7
+ explanation.push("Base confidence calculated from dominant failure frequency");
8
+ if (summary.flakyCount > 0) {
9
+ explanation.push(`${summary.flakyCount} flaky (retry-based) failures detected, reducing confidence`);
10
+ }
11
+ if (summary.trend?.signal === "NOISY") {
12
+ explanation.push("Multiple failure categories reduce certainty");
13
+ }
14
+ if (summary.trend?.signal === "STABLE") {
15
+ explanation.push("Failure pattern is consistent but not escalating");
16
+ }
17
+ return {
18
+ score: summary.confidence,
19
+ explanation
20
+ };
21
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeConfidence = computeConfidence;
4
+ const decision_config_1 = require("./decision.config");
5
+ function computeConfidence(ctx) {
6
+ let confidence = ctx.baseConfidence;
7
+ for (const f of ctx.failures) {
8
+ confidence -= decision_config_1.DECISION_CONFIG.impacts.failure[f.type];
9
+ }
10
+ for (const fl of ctx.flakiness) {
11
+ if (fl.severity) {
12
+ confidence -= decision_config_1.DECISION_CONFIG.impacts.flakiness[fl.severity];
13
+ }
14
+ }
15
+ return Math.max(0, Math.min(100, confidence));
16
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DECISION_CONFIG = void 0;
4
+ exports.DECISION_CONFIG = {
5
+ impacts: {
6
+ failure: {
7
+ NEW: 15,
8
+ KNOWN: 5,
9
+ REGRESSED: 25
10
+ },
11
+ flakiness: {
12
+ LOW: 5,
13
+ MEDIUM: 10,
14
+ HIGH: 20
15
+ }
16
+ },
17
+ thresholds: {
18
+ SHIP: 80,
19
+ INVESTIGATE: 60
20
+ }
21
+ };
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decide = decide;
4
+ const decision_config_1 = require("./decision.config");
5
+ const confidence_engine_1 = require("./confidence.engine");
6
+ function decide(ctx) {
7
+ const confidence = (0, confidence_engine_1.computeConfidence)(ctx);
8
+ const reasoning = [];
9
+ if (confidence < decision_config_1.DECISION_CONFIG.thresholds.INVESTIGATE) {
10
+ reasoning.push('Confidence below investigate threshold');
11
+ }
12
+ if (confidence < decision_config_1.DECISION_CONFIG.thresholds.SHIP) {
13
+ reasoning.push('Confidence below ship threshold');
14
+ }
15
+ let decision = 'SHIP';
16
+ if (confidence < decision_config_1.DECISION_CONFIG.thresholds.INVESTIGATE) {
17
+ decision = 'HOLD';
18
+ }
19
+ else if (confidence < decision_config_1.DECISION_CONFIG.thresholds.SHIP) {
20
+ decision = 'INVESTIGATE';
21
+ }
22
+ return { confidence, decision, reasoning };
23
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.classifyFailure = classifyFailure;
4
+ /* --------- IMPLEMENTATION --------- */
5
+ function classifyFailure(input, _runId, _history) {
6
+ const haystack = JSON.stringify(input).toLowerCase();
7
+ /* ---------------- NUMERIC ---------------- */
8
+ if (haystack.includes("numeric") ||
9
+ haystack.includes("count") ||
10
+ haystack.includes("mismatch") ||
11
+ haystack.includes("total")) {
12
+ return "NUMERIC_MISMATCH";
13
+ }
14
+ /* ---------------- AUTH / LOGIN ---------------- */
15
+ if (haystack.includes("login") ||
16
+ haystack.includes("username") ||
17
+ haystack.includes("password") ||
18
+ haystack.includes("credential") ||
19
+ haystack.includes("locked") ||
20
+ haystack.includes("required") ||
21
+ haystack.includes("do not match")) {
22
+ return "AUTH_FAILURE";
23
+ }
24
+ /* ---------------- SECURITY ---------------- */
25
+ if (haystack.includes("sql") ||
26
+ haystack.includes("injection") ||
27
+ haystack.includes("unauthorized") ||
28
+ haystack.includes("forbidden")) {
29
+ return "SECURITY";
30
+ }
31
+ return "UNKNOWN";
32
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.recordFailure = recordFailure;
4
+ /* ---------- IMPLEMENTATION ---------- */
5
+ function recordFailure(arg1, arg2, arg3) {
6
+ if (typeof arg1 === "object") {
7
+ const { classification, runId, testName } = arg1;
8
+ // 🔒 internal canonical call
9
+ recordFailure(classification, Number(runId), testName ?? "unknown");
10
+ return;
11
+ }
12
+ const fingerprint = arg1;
13
+ const runId = arg2;
14
+ const testName = arg3;
15
+ // 👉 existing storage logic stays EXACTLY as-is below
16
+ // saveFailure(fingerprint, runId, testName);
17
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.trackFailure = trackFailure;
4
+ const failure_classifier_1 = require("./failure.classifier");
5
+ const failure_store_1 = require("./failure.store");
6
+ function trackFailure(rawFailure, runId) {
7
+ /**
8
+ * Treat fingerprint input as OPAQUE.
9
+ * Do NOT assume fields. This avoids regressions.
10
+ */
11
+ const fingerprintInput = rawFailure;
12
+ // Backward + forward compatible call
13
+ const classification = (0, failure_classifier_1.classifyFailure)(fingerprintInput, runId, undefined);
14
+ (0, failure_store_1.recordFailure)({
15
+ runId,
16
+ classification
17
+ });
18
+ return classification;
19
+ }
@@ -0,0 +1,28 @@
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.generateFailureFingerprint = generateFailureFingerprint;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ function normalize(value) {
9
+ return value
10
+ .toLowerCase()
11
+ .replace(/\d+/g, '') // remove dynamic numbers
12
+ .replace(/\s+/g, ' ') // normalize whitespace
13
+ .trim();
14
+ }
15
+ function generateFailureFingerprint(input) {
16
+ const canonical = JSON.stringify({
17
+ failureType: input.failureType,
18
+ pageRoute: normalize(input.pageRoute),
19
+ primaryLocator: normalize(input.primaryLocator),
20
+ assertionIntent: normalize(input.assertionIntent),
21
+ errorClass: normalize(input.errorClass)
22
+ });
23
+ return crypto_1.default
24
+ .createHash('sha1')
25
+ .update(canonical)
26
+ .digest('hex')
27
+ .slice(0, 8);
28
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeFlakiness = analyzeFlakiness;
4
+ function analyzeFlakiness(outcomes) {
5
+ if (outcomes.length < 4)
6
+ return null;
7
+ let oscillations = 0;
8
+ for (let i = 1; i < outcomes.length; i++) {
9
+ if (outcomes[i] !== outcomes[i - 1]) {
10
+ oscillations++;
11
+ }
12
+ }
13
+ const rate = oscillations / (outcomes.length - 1);
14
+ if (rate > 0.6)
15
+ return 'HIGH';
16
+ if (rate > 0.3)
17
+ return 'MEDIUM';
18
+ if (rate > 0.1)
19
+ return 'LOW';
20
+ return null;
21
+ }
@@ -0,0 +1,16 @@
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.generateFlakinessFingerprint = generateFlakinessFingerprint;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ function normalize(value) {
9
+ if (!value)
10
+ return 'unknown';
11
+ return value.toLowerCase().trim();
12
+ }
13
+ function generateFlakinessFingerprint(testName, pageRoute) {
14
+ const key = `${normalize(testName)}::${normalize(pageRoute)}`;
15
+ return crypto_1.default.createHash('md5').update(key).digest('hex');
16
+ }
@@ -0,0 +1,24 @@
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.readFlakinessHistory = readFlakinessHistory;
7
+ exports.writeFlakinessHistory = writeFlakinessHistory;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const HISTORY_PATH = path_1.default.resolve('data/flakiness/history.json');
11
+ function readFlakinessHistory() {
12
+ if (!fs_1.default.existsSync(HISTORY_PATH)) {
13
+ return {};
14
+ }
15
+ const raw = fs_1.default.readFileSync(HISTORY_PATH, 'utf-8').trim();
16
+ if (!raw) {
17
+ return {};
18
+ }
19
+ return JSON.parse(raw);
20
+ }
21
+ function writeFlakinessHistory(history) {
22
+ fs_1.default.mkdirSync(path_1.default.dirname(HISTORY_PATH), { recursive: true });
23
+ fs_1.default.writeFileSync(HISTORY_PATH, JSON.stringify(history, null, 2));
24
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.trackFlakiness = trackFlakiness;
4
+ const flakiness_fingerprint_1 = require("./flakiness.fingerprint");
5
+ const flakiness_store_1 = require("./flakiness.store");
6
+ function trackFlakiness(testName, pageRoute, outcome) {
7
+ const fingerprint = (0, flakiness_fingerprint_1.generateFlakinessFingerprint)(testName, pageRoute);
8
+ const history = (0, flakiness_store_1.readFlakinessHistory)();
9
+ if (!history[fingerprint]) {
10
+ history[fingerprint] = [];
11
+ }
12
+ history[fingerprint].push(outcome);
13
+ (0, flakiness_store_1.writeFlakinessHistory)(history);
14
+ const recent = history[fingerprint].slice(-6);
15
+ const oscillations = recent.filter((value, index, arr) => index > 0 && value !== arr[index - 1]).length;
16
+ let severity = null;
17
+ if (oscillations >= 3)
18
+ severity = 'HIGH';
19
+ else if (oscillations === 2)
20
+ severity = 'MEDIUM';
21
+ else if (oscillations === 1)
22
+ severity = 'LOW';
23
+ return {
24
+ fingerprint,
25
+ history: recent,
26
+ severity
27
+ };
28
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ // src/intelligence/intelligence.pipeline.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.enrichWithIntelligence = enrichWithIntelligence;
5
+ const llm_explainer_1 = require("./llm-explainer");
6
+ const confidence_calibration_engine_1 = require("./confidence-calibration.engine");
7
+ async function enrichWithIntelligence(summary) {
8
+ const confidence = (0, confidence_calibration_engine_1.explainConfidence)(summary);
9
+ const explanation = await (0, llm_explainer_1.explainFailureWithLLM)(summary);
10
+ return {
11
+ ...summary,
12
+ intelligence: {
13
+ confidence,
14
+ explanation
15
+ }
16
+ };
17
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ // src/intelligence/llm-explainer.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.explainFailureWithLLM = explainFailureWithLLM;
5
+ async function explainFailureWithLLM(summary) {
6
+ const dominantCount = summary.groupedCounts[summary.dominantCategory] ?? 0;
7
+ return {
8
+ summary: `Primary failure detected: ${summary.dominantCategory}`,
9
+ reasoning: [
10
+ `Failure occurred ${dominantCount} times`,
11
+ `Confidence is ${summary.confidence}`
12
+ ],
13
+ recommendedAction: [
14
+ "Inspect backend API response",
15
+ "Verify numeric calculations",
16
+ "Check recent code changes"
17
+ ]
18
+ };
19
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeRootCause = analyzeRootCause;
4
+ function analyzeRootCause(input) {
5
+ const { retryCount, isAssertionError, hasHistory } = input;
6
+ // 🔴 App bug: not assertion + no retries
7
+ if (!isAssertionError && retryCount === 0) {
8
+ return {
9
+ type: 'APPLICATION_BUG',
10
+ reason: 'Failure without retries → application issue likely'
11
+ };
12
+ }
13
+ // 🟡 Flaky: retries + history
14
+ if (retryCount > 0 && hasHistory) {
15
+ return {
16
+ type: 'FLAKY',
17
+ reason: 'Failure resolved on retry → flaky behavior detected'
18
+ };
19
+ }
20
+ // 🟢 Default: test bug
21
+ return {
22
+ type: 'TEST_BUG',
23
+ reason: 'Assertion failure with stable history → test logic issue'
24
+ };
25
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeTrend = analyzeTrend;
4
+ function analyzeTrend(history, windowSize = 10) {
5
+ const recent = history.slice(-windowSize);
6
+ const totalRuns = recent.length;
7
+ const passCount = recent.filter(r => r === 'PASS').length;
8
+ const failCount = recent.filter(r => r === 'FAIL').length;
9
+ const passRate = totalRuns ? passCount / totalRuns : 0;
10
+ const failRate = totalRuns ? failCount / totalRuns : 0;
11
+ const oscillations = recent.filter((v, i, arr) => i > 0 && v !== arr[i - 1]).length;
12
+ let flakiness = 'LOW';
13
+ if (oscillations >= 4)
14
+ flakiness = 'HIGH';
15
+ else if (oscillations >= 2)
16
+ flakiness = 'MEDIUM';
17
+ const stability = flakiness === 'HIGH' || failRate > 0.4 ? 'UNSTABLE' : 'STABLE';
18
+ return {
19
+ totalRuns,
20
+ passRate,
21
+ failRate,
22
+ flakiness,
23
+ stability
24
+ };
25
+ }
@@ -0,0 +1,64 @@
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.writeAnalysisMarkdown = writeAnalysisMarkdown;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ function writeAnalysisMarkdown(baseDir, issueId, result, occurrences) {
10
+ const issueDir = path_1.default.join(baseDir, issueId);
11
+ fs_1.default.mkdirSync(issueDir, { recursive: true });
12
+ const mdPath = path_1.default.join(issueDir, 'analysis.md');
13
+ const content = `
14
+ # ❌ ${result.category}
15
+
16
+ **Occurrences** : ${occurrences}
17
+ **Confidence** : ${result.confidence}
18
+
19
+ ---
20
+
21
+ ## 📍 Failure Location
22
+ - **File** : ${result.location.file}
23
+ - **Line** : ${result.location.line}
24
+
25
+ ---
26
+
27
+ ## 🌐 Failed API Call
28
+ - **Method** : ${result.failedApi.method}
29
+ - **Endpoint** : ${result.failedApi.endpoint}
30
+ - **Status** : ${result.failedApi.status}
31
+ - **Response** : ${result.failedApi.response ?? '-'}
32
+
33
+ ---
34
+
35
+ ## 🧠 Root Cause
36
+ ${result.rootCause}
37
+
38
+ ---
39
+
40
+ ## 🔁 Retry Guidance
41
+ ${result.retryGuidance}
42
+
43
+ ---
44
+
45
+ ## ▶️ Reproduction Steps
46
+ ${result.reproductionSteps.map((s, i) => `${i + 1}. ${s}`).join('\n')}
47
+
48
+ ---
49
+
50
+ ## ✅ Expected Result
51
+ ${result.expected}
52
+
53
+ ---
54
+
55
+ ## ❌ Actual Result
56
+ ${result.actual}
57
+
58
+ ---
59
+
60
+ ## 🛠 Suggested Actions
61
+ ${result.suggestedActions.map(a => `- ${a}`).join('\n')}
62
+ `.trim();
63
+ fs_1.default.writeFileSync(mdPath, content, 'utf-8');
64
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ /* src/normalizer.ts */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.normalizeRawInput = normalizeRawInput;
5
+ /**
6
+ * Public normalizer API
7
+ */
8
+ function normalizeRawInput(items) {
9
+ return items.map((item) => {
10
+ const safeItem = item;
11
+ return {
12
+ rootCause: safeItem.message,
13
+ actual: safeItem.message,
14
+ response: safeItem.api?.response
15
+ };
16
+ });
17
+ }
@@ -0,0 +1,38 @@
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.analyzePasses = analyzePasses;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ function analyzePasses() {
10
+ const passPath = path_1.default.join(process.cwd(), 'input/raw-pass.json');
11
+ const items = JSON.parse(fs_1.default.readFileSync(passPath, 'utf-8'));
12
+ const risks = [];
13
+ items.forEach((item) => {
14
+ // Flaky pass
15
+ if (item.retries > 0) {
16
+ risks.push({
17
+ test: item.test,
18
+ riskType: 'FLAKY_PASS',
19
+ location: { file: item.test, line: -1 },
20
+ reason: 'Test passed after retry',
21
+ fix: 'Stabilize locator or add explicit waits'
22
+ });
23
+ }
24
+ // API warnings during pass
25
+ item.apiEvents?.forEach((api) => {
26
+ if (api.status >= 400 && api.recovered) {
27
+ risks.push({
28
+ test: item.test,
29
+ riskType: 'API_WARNING',
30
+ location: { file: item.test, line: -1 },
31
+ reason: `${api.method} ${api.endpoint} failed with ${api.status} but recovered`,
32
+ fix: 'Investigate backend instability or add retry with backoff'
33
+ });
34
+ }
35
+ });
36
+ });
37
+ return risks;
38
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.summarizeWithAI = summarizeWithAI;
4
+ async function summarizeWithAI(input) {
5
+ // 🔒 Safety first — AI is OPTIONAL
6
+ if (!process.env.AI_ENABLED) {
7
+ return deterministicFallback(input);
8
+ }
9
+ try {
10
+ // 🔹 Placeholder for real LLM call
11
+ // 🔹 Intentionally simple & replaceable
12
+ return `
13
+ This failure occurred in the test "${input.testName}".
14
+ Based on the observed error and system heuristics, the root cause is classified as:
15
+ "${input.rootCause}"
16
+
17
+ The confidence score (${(input.confidence / 100).toFixed(2)}) indicates a stable and repeatable issue.
18
+ Recommended action is to follow the suggested remediation rather than retrying the test.
19
+ `.trim();
20
+ }
21
+ catch {
22
+ // 🔒 Never crash pipeline because of AI
23
+ return deterministicFallback(input);
24
+ }
25
+ }
26
+ function deterministicFallback(input) {
27
+ return `
28
+ Failure in "${input.testName}" was analyzed deterministically.
29
+ Root cause identified as: ${input.rootCause}.
30
+ Confidence level: ${(input.confidence / 100).toFixed(2)}.
31
+ `.trim();
32
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ // src/pipeline/failure-analysis.pipeline.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.runFailureAnalysisFromPlaywright = runFailureAnalysisFromPlaywright;
5
+ const playwright_folder_adapter_1 = require("../adapters/playwright-folder.adapter");
6
+ /**
7
+ * Phase 1 – Playwright Failure Intake (Folder-based)
8
+ */
9
+ async function runFailureAnalysisFromPlaywright(playwrightResultsDir) {
10
+ return (0, playwright_folder_adapter_1.parsePlaywrightFailureFolders)(playwrightResultsDir);
11
+ }