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
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ // src/final-run.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.runFinal = runFinal;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const runner_1 = require("./runner");
11
+ const v2_intelligence_pipeline_1 = require("./v2/pipeline/v2-intelligence.pipeline");
12
+ const issue_view_1 = require("./console/issue-view");
13
+ function pickOneFailureFolder() {
14
+ const base = "playwright-results";
15
+ if (!fs_1.default.existsSync(base))
16
+ return null;
17
+ const entries = fs_1.default.readdirSync(base, { withFileTypes: true });
18
+ const folder = entries.find(e => e.isDirectory());
19
+ return folder ? path_1.default.join(base, folder.name) : null;
20
+ }
21
+ function line(char = "=", count = 70) {
22
+ console.log(char.repeat(count));
23
+ }
24
+ /**
25
+ * Parse --issues / --issue flags
26
+ *
27
+ * Supported:
28
+ * --issues
29
+ * --issues=top3
30
+ * --issues=login
31
+ * --issue
32
+ * --issue=top3
33
+ * --issue=login
34
+ */
35
+ function parseIssueFilter() {
36
+ const arg = process.argv.find(a => a === "--issues" ||
37
+ a === "--issue" ||
38
+ a.startsWith("--issues=") ||
39
+ a.startsWith("--issue="));
40
+ if (!arg)
41
+ return { enabled: false };
42
+ if (arg === "--issues" || arg === "--issue") {
43
+ return { enabled: true };
44
+ }
45
+ const [, value] = arg.split("=");
46
+ if (value?.startsWith("top")) {
47
+ const n = Number(value.replace("top", ""));
48
+ return { enabled: true, topN: n };
49
+ }
50
+ return { enabled: true, keyword: value };
51
+ }
52
+ /* ===================================================== */
53
+ /* ✅ EXPORTED FUNCTION – SAME LOGIC, NO BEHAVIOR CHANGE */
54
+ /* ===================================================== */
55
+ async function runFinal() {
56
+ const playwrightResultsDir = "playwright-results";
57
+ const failureFolder = pickOneFailureFolder();
58
+ const issueFilter = parseIssueFilter();
59
+ if (!failureFolder) {
60
+ console.error("No Playwright failure folders found.");
61
+ return;
62
+ }
63
+ /* ---------------- V1 + Phase 3 ---------------- */
64
+ const v1 = await (0, runner_1.runQualityIntelligence)(playwrightResultsDir);
65
+ line();
66
+ console.log("QUALITY INTELLIGENCE – FINAL PROJECT OUTPUT");
67
+ line();
68
+ console.log(`Primary Failure : ${v1.dominantCategory}`);
69
+ console.log(`Confidence Score : ${v1.confidence}`);
70
+ console.log(`Flaky Failures : ${v1.flakyCount}`);
71
+ console.log(`Trend Signal : ${v1.trend?.signal ?? "N/A"}`);
72
+ line("-");
73
+ console.log("Failure Breakdown:");
74
+ Object.entries(v1.groupedCounts).forEach(([k, v]) => {
75
+ console.log(` - ${k}: ${v}`);
76
+ });
77
+ line("-");
78
+ console.log("Confidence Diagnosis:");
79
+ v1.intelligence.confidence.explanation.forEach(e => console.log(` • ${e}`));
80
+ line("-");
81
+ console.log("AI Explanation:");
82
+ console.log(` Summary: ${v1.intelligence.explanation.summary}`);
83
+ v1.intelligence.explanation.reasoning.forEach(r => console.log(` - ${r}`));
84
+ /* ---------------- V2 ---------------- */
85
+ const v2 = await (0, v2_intelligence_pipeline_1.runV2Intelligence)(failureFolder, v1.dominantCategory, "Derived from Playwright failure artifacts");
86
+ line("-");
87
+ console.log("TRACE & SELF-HEALING INTELLIGENCE");
88
+ console.log("\nEvidence:");
89
+ if (v2.trace.screenshots.length) {
90
+ v2.trace.screenshots.forEach(s => console.log(` Screenshot: ${s}`));
91
+ }
92
+ else {
93
+ console.log(" No screenshots found");
94
+ }
95
+ if (v2.trace.traceFile) {
96
+ console.log(` Trace File : ${v2.trace.traceFile}`);
97
+ console.log(` Open Trace : npx playwright show-trace ${v2.trace.traceFile}`);
98
+ }
99
+ line("-");
100
+ console.log("Self-Healing Decision:");
101
+ if (v2.healing) {
102
+ console.log(` Allowed : YES`);
103
+ console.log(` Reason : ${v2.healing.reason}`);
104
+ console.log(` Fix : ${v2.healing.suggestedFix}`);
105
+ console.log(` Confidence : ${v2.healing.confidence}`);
106
+ }
107
+ else {
108
+ console.log(` Allowed : NO (manual fix required)`);
109
+ }
110
+ line("-");
111
+ console.log("LLM Advisory:");
112
+ console.log(` ${v2.llm.summary}`);
113
+ v2.llm.fixRecommendation.forEach(r => console.log(` - ${r}`));
114
+ /* ---------------- ISSUE VIEW (OPTIONAL) ---------------- */
115
+ if (issueFilter.enabled) {
116
+ line();
117
+ console.log("ISSUE ANALYSIS");
118
+ line();
119
+ const issues = (0, issue_view_1.extractIssues)(v1.groupedCounts, v1.confidence);
120
+ const filtered = (0, issue_view_1.filterIssues)(issues, issueFilter);
121
+ (0, issue_view_1.printIssues)(filtered, failureFolder);
122
+ }
123
+ line();
124
+ }
125
+ /* ===================================================== */
126
+ /* ✅ KEEP EXISTING BEHAVIOR WHEN RUN DIRECTLY */
127
+ /* ===================================================== */
128
+ if (require.main === module) {
129
+ runFinal();
130
+ }
@@ -0,0 +1,27 @@
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.runFinalOutput = runFinalOutput;
7
+ const child_process_1 = require("child_process");
8
+ const path_1 = __importDefault(require("path"));
9
+ async function runFinalOutput(options) {
10
+ const args = [];
11
+ if (options.showIssues) {
12
+ args.push("--issue");
13
+ }
14
+ // run existing final-run.ts via ts-node / node
15
+ const scriptPath = path_1.default.join(__dirname, "final-run.ts");
16
+ return new Promise((resolve, reject) => {
17
+ const proc = (0, child_process_1.spawn)("node", ["--loader", "ts-node/esm", scriptPath, ...args], {
18
+ stdio: "inherit"
19
+ });
20
+ proc.on("close", code => {
21
+ if (code === 0)
22
+ resolve();
23
+ else
24
+ reject(new Error(`final-run exited with ${code}`));
25
+ });
26
+ });
27
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.test = void 0;
4
+ const test_1 = require("@playwright/test");
5
+ exports.test = test_1.test.extend({
6
+ networkResponses: async ({ page }, use) => {
7
+ const responses = [];
8
+ page.on('response', async (response) => {
9
+ responses.push({
10
+ url: response.url(),
11
+ method: response.request().method(),
12
+ status: response.status(),
13
+ ok: response.ok()
14
+ });
15
+ });
16
+ await use(responses);
17
+ }
18
+ });
@@ -0,0 +1,25 @@
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.saveFailureHistory = saveFailureHistory;
7
+ exports.loadFailureHistory = loadFailureHistory;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const HISTORY_DIR = path_1.default.join(process.cwd(), "artifacts/failure-history");
11
+ function saveFailureHistory(entry) {
12
+ if (!fs_1.default.existsSync(HISTORY_DIR)) {
13
+ fs_1.default.mkdirSync(HISTORY_DIR, { recursive: true });
14
+ }
15
+ const filePath = path_1.default.join(HISTORY_DIR, `${entry.runId}.json`);
16
+ fs_1.default.writeFileSync(filePath, JSON.stringify(entry, null, 2));
17
+ }
18
+ function loadFailureHistory() {
19
+ if (!fs_1.default.existsSync(HISTORY_DIR))
20
+ return [];
21
+ return fs_1.default.readdirSync(HISTORY_DIR).map(file => {
22
+ const fullPath = path_1.default.join(HISTORY_DIR, file);
23
+ return JSON.parse(fs_1.default.readFileSync(fullPath, "utf-8"));
24
+ });
25
+ }
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // History layer is DECOUPLED from pipeline
3
+ // No dependency on FailureType to avoid path issues
4
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,20 @@
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.classifyFailure = classifyFailure;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const REGISTRY_PATH = path_1.default.resolve('data/failures/registry.json');
10
+ const REGRESSION_GAP = 3;
11
+ function classifyFailure(fingerprint, currentRun) {
12
+ const registry = JSON.parse(fs_1.default.readFileSync(REGISTRY_PATH, 'utf-8'));
13
+ const entry = registry[fingerprint];
14
+ if (!entry)
15
+ return 'NEW';
16
+ if (currentRun - entry.lastSeenRun > REGRESSION_GAP) {
17
+ return 'REGRESSED';
18
+ }
19
+ return 'KNOWN';
20
+ }
@@ -0,0 +1,39 @@
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.recordFailure = recordFailure;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const BASE_PATH = path_1.default.resolve('data/failures');
10
+ const REGISTRY_PATH = path_1.default.join(BASE_PATH, 'registry.json');
11
+ const FP_DIR = path_1.default.join(BASE_PATH, 'fingerprints');
12
+ function readRegistry() {
13
+ return JSON.parse(fs_1.default.readFileSync(REGISTRY_PATH, 'utf-8'));
14
+ }
15
+ function writeRegistry(data) {
16
+ fs_1.default.writeFileSync(REGISTRY_PATH, JSON.stringify(data, null, 2));
17
+ }
18
+ function recordFailure(fingerprint, runId, testName) {
19
+ const registry = readRegistry();
20
+ if (!registry[fingerprint]) {
21
+ registry[fingerprint] = {
22
+ firstSeenRun: runId,
23
+ lastSeenRun: runId,
24
+ totalOccurrences: 1
25
+ };
26
+ }
27
+ else {
28
+ registry[fingerprint].lastSeenRun = runId;
29
+ registry[fingerprint].totalOccurrences++;
30
+ }
31
+ writeRegistry(registry);
32
+ const fpPath = path_1.default.join(FP_DIR, `${fingerprint}.json`);
33
+ const history = fs_1.default.existsSync(fpPath)
34
+ ? JSON.parse(fs_1.default.readFileSync(fpPath, 'utf-8'))
35
+ : { runsSeen: [], affectedTests: [] };
36
+ history.runsSeen.push(runId);
37
+ history.affectedTests.push(testName);
38
+ fs_1.default.writeFileSync(fpPath, JSON.stringify(history, null, 2));
39
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.trackFailure = trackFailure;
4
+ const fingerprint_generator_1 = require("./fingerprint.generator");
5
+ const failure_classifier_1 = require("./failure.classifier");
6
+ const failure_store_1 = require("./failure.store");
7
+ function trackFailure(input, runId, testName) {
8
+ const fingerprint = (0, fingerprint_generator_1.generateFailureFingerprint)(input);
9
+ const classification = (0, failure_classifier_1.classifyFailure)(fingerprint, runId);
10
+ (0, failure_store_1.recordFailure)(fingerprint, runId, testName);
11
+ return {
12
+ fingerprint,
13
+ classification
14
+ };
15
+ }
@@ -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
+ }