@kb-labs/qa-core 2.89.0 → 2.93.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.
@@ -1,7 +1,36 @@
1
1
  import { execSync } from 'child_process';
2
2
  import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
3
- import { join, dirname } from 'path';
4
- import { TRENDS_WINDOW, PATHS, HISTORY_MAX_ENTRIES, getCheckIcon, getCheckLabel } from '@kb-labs/qa-contracts';
3
+ import { dirname, join } from 'path';
4
+ import { TRENDS_WINDOW, HISTORY_MAX_ENTRIES, PATHS, getCheckIcon, getCheckLabel, QA_DATA_DIR } from '@kb-labs/qa-contracts';
5
+
6
+ // src/history/history-store.ts
7
+ function trendPath(rootDir, checkId) {
8
+ return join(rootDir, QA_DATA_DIR, "trends", `${checkId}.json`);
9
+ }
10
+ function loadTrendHistory(rootDir, checkId) {
11
+ const path = trendPath(rootDir, checkId);
12
+ if (!existsSync(path)) {
13
+ return [];
14
+ }
15
+ try {
16
+ return JSON.parse(readFileSync(path, "utf-8"));
17
+ } catch {
18
+ return [];
19
+ }
20
+ }
21
+ function appendTrendEntry(rootDir, checkId, entry) {
22
+ const path = trendPath(rootDir, checkId);
23
+ const dir = dirname(path);
24
+ if (!existsSync(dir)) {
25
+ mkdirSync(dir, { recursive: true });
26
+ }
27
+ const entries = loadTrendHistory(rootDir, checkId);
28
+ entries.push(entry);
29
+ while (entries.length > HISTORY_MAX_ENTRIES) {
30
+ entries.shift();
31
+ }
32
+ writeFileSync(path, JSON.stringify(entries, null, 2));
33
+ }
5
34
 
6
35
  // src/history/history-store.ts
7
36
  function loadHistory(rootDir) {
@@ -23,7 +52,7 @@ function saveHistory(rootDir, entries) {
23
52
  }
24
53
  writeFileSync(path, JSON.stringify(entries, null, 2));
25
54
  }
26
- function createHistoryEntry(results, rootDir, packages) {
55
+ function createHistoryEntry(results, rootDir, packages, runContext) {
27
56
  let commit = "unknown";
28
57
  let branch = "unknown";
29
58
  let message = "";
@@ -63,16 +92,24 @@ function createHistoryEntry(results, rootDir, packages) {
63
92
  submodules,
64
93
  status: hasFailures ? "failed" : "passed",
65
94
  summary,
66
- failedPackages
95
+ failedPackages,
96
+ ...runContext ? { runContext } : {}
67
97
  };
68
98
  }
69
- function appendEntry(rootDir, entry) {
99
+ function appendEntry(rootDir, entry, checks) {
70
100
  const history = loadHistory(rootDir);
71
101
  history.push(entry);
72
102
  while (history.length > HISTORY_MAX_ENTRIES) {
73
103
  history.shift();
74
104
  }
75
105
  saveHistory(rootDir, history);
106
+ if (checks) {
107
+ for (const check of checks) {
108
+ if (check.trending && entry.summary[check.id] !== void 0) {
109
+ appendTrendEntry(rootDir, check.id, entry);
110
+ }
111
+ }
112
+ }
76
113
  }
77
114
  function collectCheckTypes(entries) {
78
115
  const s = /* @__PURE__ */ new Set();
@@ -319,6 +356,6 @@ function getPackageTimeline(history, packageName) {
319
356
  };
320
357
  }
321
358
 
322
- export { analyzeEnrichedTrends, analyzeTrends, appendEntry, createHistoryEntry, detectRegressions, getPackageTimeline, loadHistory, saveHistory };
359
+ export { analyzeEnrichedTrends, analyzeTrends, appendEntry, appendTrendEntry, createHistoryEntry, detectRegressions, getPackageTimeline, loadHistory, loadTrendHistory, saveHistory };
323
360
  //# sourceMappingURL=index.js.map
324
361
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/history/history-store.ts","../../src/history/trend-analyzer.ts","../../src/history/regression-detector.ts","../../src/history/package-timeline.ts"],"names":["delta"],"mappings":"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","file":"index.js","sourcesContent":["import { execSync } from 'node:child_process';\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { PATHS, HISTORY_MAX_ENTRIES } from '@kb-labs/qa-contracts';\nimport type { HistoryEntry, QAResults, SubmoduleInfo, WorkspacePackage } from '@kb-labs/qa-contracts';\n\n/**\n * Load history entries from disk.\n */\nexport function loadHistory(rootDir: string): HistoryEntry[] {\n const path = join(rootDir, PATHS.HISTORY);\n if (!existsSync(path)) {return [];}\n try {\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return [];\n }\n}\n\n/**\n * Save history entries to disk.\n */\nexport function saveHistory(rootDir: string, entries: HistoryEntry[]): void {\n const path = join(rootDir, PATHS.HISTORY);\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(path, JSON.stringify(entries, null, 2));\n}\n\n/**\n * Create a history entry from QA results.\n */\nexport function createHistoryEntry(\n results: QAResults,\n rootDir: string,\n packages?: WorkspacePackage[],\n): HistoryEntry {\n let commit = 'unknown';\n let branch = 'unknown';\n let message = '';\n\n try {\n commit = execSync('git rev-parse --short HEAD', { cwd: rootDir, encoding: 'utf-8' }).trim();\n branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: rootDir, encoding: 'utf-8' }).trim();\n message = execSync('git log -1 --format=%s', { cwd: rootDir, encoding: 'utf-8' }).trim();\n } catch {\n // git not available\n }\n\n const hasFailures = Object.values(results).some(r => r.failed.length > 0);\n\n const summary = {} as HistoryEntry['summary'];\n const failedPackages = {} as HistoryEntry['failedPackages'];\n\n for (const ct of Object.keys(results)) {\n const r = results[ct]!;\n summary[ct] = {\n passed: r.passed.length,\n failed: r.failed.length,\n skipped: r.skipped.length,\n };\n failedPackages[ct] = [...r.failed];\n }\n\n // Collect submodule info from packages (deduplicated by repo)\n let submodules: Record<string, SubmoduleInfo> | undefined;\n if (packages) {\n const subs: Record<string, SubmoduleInfo> = {};\n for (const pkg of packages) {\n if (pkg.submodule && !subs[pkg.repo]) {\n subs[pkg.repo] = pkg.submodule;\n }\n }\n if (Object.keys(subs).length > 0) {\n submodules = subs;\n }\n }\n\n return {\n timestamp: new Date().toISOString(),\n git: { commit, branch, message },\n submodules,\n status: hasFailures ? 'failed' : 'passed',\n summary,\n failedPackages,\n };\n}\n\n/**\n * Append a history entry, keeping max HISTORY_MAX_ENTRIES.\n */\nexport function appendEntry(rootDir: string, entry: HistoryEntry): void {\n const history = loadHistory(rootDir);\n history.push(entry);\n\n // Trim to max entries\n while (history.length > HISTORY_MAX_ENTRIES) {\n history.shift();\n }\n\n saveHistory(rootDir, history);\n}\n","import type { HistoryEntry, TrendResult, EnrichedTrendResult, TrendChangelogEntry } from '@kb-labs/qa-contracts';\nimport { TRENDS_WINDOW, getCheckLabel, getCheckIcon } from '@kb-labs/qa-contracts';\n\n/**\n * Collect all unique check type keys across a set of history entries.\n */\nfunction collectCheckTypes(entries: HistoryEntry[]): string[] {\n const s = new Set<string>();\n for (const e of entries) {\n for (const k of Object.keys(e.summary)) {s.add(k);}\n }\n return [...s];\n}\n\n/**\n * Analyze trends over a window of history entries.\n * Compares failure counts between first and last entry in the window.\n */\nexport function analyzeTrends(\n history: HistoryEntry[],\n window: number = TRENDS_WINDOW,\n): TrendResult[] {\n if (history.length < 2) {return [];}\n\n const windowEntries = history.slice(-window);\n const first = windowEntries[0]!;\n const last = windowEntries[windowEntries.length - 1]!;\n\n const results: TrendResult[] = [];\n\n for (const ct of collectCheckTypes([first, last])) {\n const previous = first.summary[ct]?.failed ?? 0;\n const current = last.summary[ct]?.failed ?? 0;\n const delta = current - previous;\n\n let trend: TrendResult['trend'];\n if (delta > 0) {trend = 'regression';}\n else if (delta < 0) {trend = 'improvement';}\n else {trend = 'no-change';}\n\n results.push({ checkType: ct, label: getCheckLabel(ct), icon: getCheckIcon(ct), previous, current, delta, trend });\n }\n\n return results;\n}\n\n/**\n * Enriched trend analysis — full time-series, per-entry changelog, velocity.\n *\n * For each check type, provides:\n * - timeSeries: all data points in the window (for charts)\n * - changelog: what changed between consecutive entries (new failures / fixes)\n * - velocity: average delta per entry (rate of change)\n */\nexport function analyzeEnrichedTrends(\n history: HistoryEntry[],\n window: number = TRENDS_WINDOW,\n): EnrichedTrendResult[] {\n if (history.length < 2) {return [];}\n\n const windowEntries = history.slice(-window);\n const first = windowEntries[0]!;\n const last = windowEntries[windowEntries.length - 1]!;\n\n const results: EnrichedTrendResult[] = [];\n\n for (const ct of collectCheckTypes(windowEntries)) {\n // Build time-series: map each entry to a data point\n const timeSeries = windowEntries.map((entry) => ({\n timestamp: entry.timestamp,\n gitCommit: entry.git.commit,\n gitBranch: entry.git.branch,\n gitMessage: entry.git.message,\n passed: entry.summary[ct]?.passed ?? 0,\n failed: entry.summary[ct]?.failed ?? 0,\n skipped: entry.summary[ct]?.skipped ?? 0,\n }));\n\n // Build changelog: diff failedPackages between consecutive entries\n const changelog: TrendChangelogEntry[] = [];\n const deltas: number[] = [];\n\n for (let i = 1; i < windowEntries.length; i++) {\n const prev = windowEntries[i - 1]!;\n const curr = windowEntries[i]!;\n\n const prevFailed = new Set(prev.failedPackages[ct] ?? []);\n const currFailed = curr.failedPackages[ct] ?? [];\n const currFailedSet = new Set(currFailed);\n\n const newFailures = currFailed.filter((p) => !prevFailed.has(p));\n const fixed = [...prevFailed].filter((p) => !currFailedSet.has(p));\n const delta = currFailed.length - prevFailed.size;\n\n deltas.push(delta);\n\n // Only include entries that actually had changes\n if (newFailures.length > 0 || fixed.length > 0) {\n changelog.push({\n timestamp: curr.timestamp,\n gitCommit: curr.git.commit,\n gitMessage: curr.git.message,\n newFailures,\n fixed,\n delta,\n });\n }\n }\n\n // Summary metrics\n const previous = first.summary[ct]?.failed ?? 0;\n const current = last.summary[ct]?.failed ?? 0;\n const delta = current - previous;\n\n let trend: EnrichedTrendResult['trend'];\n if (delta > 0) {trend = 'regression';}\n else if (delta < 0) {trend = 'improvement';}\n else {trend = 'no-change';}\n\n // Velocity: average delta per entry transition\n const velocity = deltas.length > 0\n ? deltas.reduce((sum, d) => sum + d, 0) / deltas.length\n : 0;\n\n results.push({\n checkType: ct,\n label: getCheckLabel(ct),\n icon: getCheckIcon(ct),\n timeSeries,\n changelog,\n current,\n previous,\n delta,\n trend,\n velocity: Math.round(velocity * 100) / 100,\n });\n }\n\n return results;\n}\n","import type { HistoryEntry, RegressionResult } from '@kb-labs/qa-contracts';\n\n/**\n * Detect regressions by comparing the last 2 history entries.\n * A regression = new packages failing that weren't failing before.\n */\nexport function detectRegressions(history: HistoryEntry[]): RegressionResult {\n if (history.length < 2) {\n return { hasRegressions: false, regressions: [] };\n }\n\n const previous = history[history.length - 2]!;\n const current = history[history.length - 1]!;\n\n const regressions: RegressionResult['regressions'] = [];\n\n const checkTypes = [...new Set([...Object.keys(previous.failedPackages), ...Object.keys(current.failedPackages)])];\n\n for (const ct of checkTypes) {\n const prevFailed = new Set(previous.failedPackages[ct] ?? []);\n const currFailed = current.failedPackages[ct] ?? [];\n\n const newFailures = currFailed.filter((p) => !prevFailed.has(p));\n const delta = currFailed.length - prevFailed.size;\n\n if (newFailures.length > 0) {\n regressions.push({\n checkType: ct,\n delta,\n newFailures,\n });\n }\n }\n\n return {\n hasRegressions: regressions.length > 0,\n regressions,\n };\n}\n","import type { HistoryEntry, CheckType } from '@kb-labs/qa-contracts';\nimport type { PackageTimelineEntry, PackageTimelineResponse } from '@kb-labs/qa-contracts';\n\nfunction buildEntries(history: HistoryEntry[], packageName: string): { entries: PackageTimelineEntry[]; repo: string } {\n let repo = 'unknown';\n const entries: PackageTimelineEntry[] = [];\n\n for (let i = history.length - 1; i >= 0; i--) {\n const h = history[i]!;\n const checks: Record<string, 'passed' | 'failed' | 'skipped'> = {};\n let found = false;\n\n for (const ct of Object.keys(h.summary)) {\n const failedList = h.failedPackages[ct] ?? [];\n const summaryEntry = h.summary[ct];\n if (failedList.includes(packageName)) {\n checks[ct] = 'failed'; found = true;\n } else if (summaryEntry && (summaryEntry.passed > 0 || summaryEntry.failed > 0)) {\n checks[ct] = 'passed'; found = true;\n } else {\n checks[ct] = 'skipped';\n }\n }\n\n if (!found) { continue; }\n\n let submoduleCommit: string | undefined;\n if (h.submodules) {\n for (const [repoName, info] of Object.entries(h.submodules)) {\n if (repoName === repo || repo === 'unknown') {\n submoduleCommit = info.commit;\n if (repo === 'unknown') { repo = repoName; }\n }\n }\n }\n\n entries.push({\n timestamp: h.timestamp,\n git: h.git,\n submoduleCommit,\n checks: checks as Record<CheckType, 'passed' | 'failed' | 'skipped'>,\n });\n }\n\n return { entries, repo };\n}\n\nfunction computeFlakyScore(entries: PackageTimelineEntry[]): { flakyScore: number; flakyChecks: CheckType[] } {\n const allCheckTypes = new Set<string>();\n for (const entry of entries) {\n for (const k of Object.keys(entry.checks)) { allCheckTypes.add(k); }\n }\n\n const flakyChecks: CheckType[] = [];\n let totalFlips = 0;\n let totalTransitions = 0;\n\n for (const ct of allCheckTypes) {\n let flips = 0;\n let transitions = 0;\n for (let i = 1; i < entries.length; i++) {\n const prev = entries[i - 1]!.checks[ct];\n const curr = entries[i]!.checks[ct];\n if (prev === 'skipped' || curr === 'skipped' || !prev || !curr) { continue; }\n transitions++;\n if (prev !== curr) { flips++; }\n }\n if (transitions > 0 && flips / transitions > 0.3) { flakyChecks.push(ct as CheckType); }\n totalFlips += flips;\n totalTransitions += transitions;\n }\n\n return {\n flakyScore: totalTransitions > 0 ? Math.min(1, totalFlips / totalTransitions) : 0,\n flakyChecks,\n };\n}\n\nfunction computeStreak(entries: PackageTimelineEntry[]): { status: 'passing' | 'failing'; count: number } {\n const latest = entries[0];\n if (!latest) { return { status: 'passing', count: 0 }; }\n\n const streakStatus: 'passing' | 'failing' = Object.values(latest.checks).some((v) => v === 'failed') ? 'failing' : 'passing';\n let count = 1;\n for (let i = 1; i < entries.length; i++) {\n const eFail = Object.values(entries[i]!.checks).some((v) => v === 'failed');\n if ((eFail ? 'failing' : 'passing') !== streakStatus) { break; }\n count++;\n }\n return { status: streakStatus, count };\n}\n\n/**\n * Build a timeline for a specific package across QA history.\n * Computes flaky score, current streak, and first failure timestamp.\n */\nexport function getPackageTimeline(\n history: HistoryEntry[],\n packageName: string,\n): PackageTimelineResponse {\n const { entries, repo } = buildEntries(history, packageName);\n const { flakyScore, flakyChecks } = computeFlakyScore(entries);\n\n let firstFailure: string | undefined;\n for (let i = entries.length - 1; i >= 0; i--) {\n if (Object.values(entries[i]!.checks).some((v) => v === 'failed')) {\n firstFailure = entries[i]!.timestamp;\n }\n }\n\n return {\n packageName,\n repo,\n entries,\n flakyScore: Math.round(flakyScore * 100) / 100,\n flakyChecks,\n firstFailure,\n currentStreak: computeStreak(entries),\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/history/trend-store.ts","../../src/history/history-store.ts","../../src/history/trend-analyzer.ts","../../src/history/regression-detector.ts","../../src/history/package-timeline.ts"],"names":["join","existsSync","readFileSync","dirname","mkdirSync","writeFileSync","HISTORY_MAX_ENTRIES","delta"],"mappings":"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","file":"index.js","sourcesContent":["import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { QA_DATA_DIR, HISTORY_MAX_ENTRIES } from '@kb-labs/qa-contracts';\nimport type { HistoryEntry } from '@kb-labs/qa-contracts';\n\nfunction trendPath(rootDir: string, checkId: string): string {\n return join(rootDir, QA_DATA_DIR, 'trends', `${checkId}.json`);\n}\n\n/**\n * Load per-check trend history from disk.\n */\nexport function loadTrendHistory(rootDir: string, checkId: string): HistoryEntry[] {\n const path = trendPath(rootDir, checkId);\n if (!existsSync(path)) { return []; }\n try {\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return [];\n }\n}\n\n/**\n * Append a history entry to the per-check trend store.\n * Keeps max HISTORY_MAX_ENTRIES entries per check.\n */\nexport function appendTrendEntry(rootDir: string, checkId: string, entry: HistoryEntry): void {\n const path = trendPath(rootDir, checkId);\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const entries = loadTrendHistory(rootDir, checkId);\n entries.push(entry);\n while (entries.length > HISTORY_MAX_ENTRIES) {\n entries.shift();\n }\n writeFileSync(path, JSON.stringify(entries, null, 2));\n}\n","import { execSync } from 'node:child_process';\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { PATHS, HISTORY_MAX_ENTRIES } from '@kb-labs/qa-contracts';\nimport type { HistoryEntry, QAResults, SubmoduleInfo, WorkspacePackage, QACheckConfig } from '@kb-labs/qa-contracts';\nimport { appendTrendEntry } from './trend-store.js';\n\n/**\n * Load history entries from disk.\n */\nexport function loadHistory(rootDir: string): HistoryEntry[] {\n const path = join(rootDir, PATHS.HISTORY);\n if (!existsSync(path)) {return [];}\n try {\n return JSON.parse(readFileSync(path, 'utf-8'));\n } catch {\n return [];\n }\n}\n\n/**\n * Save history entries to disk.\n */\nexport function saveHistory(rootDir: string, entries: HistoryEntry[]): void {\n const path = join(rootDir, PATHS.HISTORY);\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(path, JSON.stringify(entries, null, 2));\n}\n\n/**\n * Create a history entry from QA results.\n */\nexport function createHistoryEntry(\n results: QAResults,\n rootDir: string,\n packages?: WorkspacePackage[],\n runContext?: HistoryEntry['runContext'],\n): HistoryEntry {\n let commit = 'unknown';\n let branch = 'unknown';\n let message = '';\n\n try {\n commit = execSync('git rev-parse --short HEAD', { cwd: rootDir, encoding: 'utf-8' }).trim();\n branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: rootDir, encoding: 'utf-8' }).trim();\n message = execSync('git log -1 --format=%s', { cwd: rootDir, encoding: 'utf-8' }).trim();\n } catch {\n // git not available\n }\n\n const hasFailures = Object.values(results).some(r => r.failed.length > 0);\n\n const summary = {} as HistoryEntry['summary'];\n const failedPackages = {} as HistoryEntry['failedPackages'];\n\n for (const ct of Object.keys(results)) {\n const r = results[ct]!;\n summary[ct] = {\n passed: r.passed.length,\n failed: r.failed.length,\n skipped: r.skipped.length,\n };\n failedPackages[ct] = [...r.failed];\n }\n\n // Collect submodule info from packages (deduplicated by repo)\n let submodules: Record<string, SubmoduleInfo> | undefined;\n if (packages) {\n const subs: Record<string, SubmoduleInfo> = {};\n for (const pkg of packages) {\n if (pkg.submodule && !subs[pkg.repo]) {\n subs[pkg.repo] = pkg.submodule;\n }\n }\n if (Object.keys(subs).length > 0) {\n submodules = subs;\n }\n }\n\n return {\n timestamp: new Date().toISOString(),\n git: { commit, branch, message },\n submodules,\n status: hasFailures ? 'failed' : 'passed',\n summary,\n failedPackages,\n ...(runContext ? { runContext } : {}),\n };\n}\n\n/**\n * Append a history entry, keeping max HISTORY_MAX_ENTRIES.\n * If checks config is provided, also writes to per-check trend store for checks with trending:true.\n */\nexport function appendEntry(rootDir: string, entry: HistoryEntry, checks?: QACheckConfig[]): void {\n const history = loadHistory(rootDir);\n history.push(entry);\n\n // Trim to max entries\n while (history.length > HISTORY_MAX_ENTRIES) {\n history.shift();\n }\n\n saveHistory(rootDir, history);\n\n // Write to per-check trend store for trending checks\n if (checks) {\n for (const check of checks) {\n if (check.trending && entry.summary[check.id] !== undefined) {\n appendTrendEntry(rootDir, check.id, entry);\n }\n }\n }\n}\n","import type { HistoryEntry, TrendResult, EnrichedTrendResult, TrendChangelogEntry } from '@kb-labs/qa-contracts';\nimport { TRENDS_WINDOW, getCheckLabel, getCheckIcon } from '@kb-labs/qa-contracts';\n\n/**\n * Collect all unique check type keys across a set of history entries.\n */\nfunction collectCheckTypes(entries: HistoryEntry[]): string[] {\n const s = new Set<string>();\n for (const e of entries) {\n for (const k of Object.keys(e.summary)) {s.add(k);}\n }\n return [...s];\n}\n\n/**\n * Analyze trends over a window of history entries.\n * Compares failure counts between first and last entry in the window.\n */\nexport function analyzeTrends(\n history: HistoryEntry[],\n window: number = TRENDS_WINDOW,\n): TrendResult[] {\n if (history.length < 2) {return [];}\n\n const windowEntries = history.slice(-window);\n const first = windowEntries[0]!;\n const last = windowEntries[windowEntries.length - 1]!;\n\n const results: TrendResult[] = [];\n\n for (const ct of collectCheckTypes([first, last])) {\n const previous = first.summary[ct]?.failed ?? 0;\n const current = last.summary[ct]?.failed ?? 0;\n const delta = current - previous;\n\n let trend: TrendResult['trend'];\n if (delta > 0) {trend = 'regression';}\n else if (delta < 0) {trend = 'improvement';}\n else {trend = 'no-change';}\n\n results.push({ checkType: ct, label: getCheckLabel(ct), icon: getCheckIcon(ct), previous, current, delta, trend });\n }\n\n return results;\n}\n\n/**\n * Enriched trend analysis — full time-series, per-entry changelog, velocity.\n *\n * For each check type, provides:\n * - timeSeries: all data points in the window (for charts)\n * - changelog: what changed between consecutive entries (new failures / fixes)\n * - velocity: average delta per entry (rate of change)\n */\nexport function analyzeEnrichedTrends(\n history: HistoryEntry[],\n window: number = TRENDS_WINDOW,\n): EnrichedTrendResult[] {\n if (history.length < 2) {return [];}\n\n const windowEntries = history.slice(-window);\n const first = windowEntries[0]!;\n const last = windowEntries[windowEntries.length - 1]!;\n\n const results: EnrichedTrendResult[] = [];\n\n for (const ct of collectCheckTypes(windowEntries)) {\n // Build time-series: map each entry to a data point\n const timeSeries = windowEntries.map((entry) => ({\n timestamp: entry.timestamp,\n gitCommit: entry.git.commit,\n gitBranch: entry.git.branch,\n gitMessage: entry.git.message,\n passed: entry.summary[ct]?.passed ?? 0,\n failed: entry.summary[ct]?.failed ?? 0,\n skipped: entry.summary[ct]?.skipped ?? 0,\n }));\n\n // Build changelog: diff failedPackages between consecutive entries\n const changelog: TrendChangelogEntry[] = [];\n const deltas: number[] = [];\n\n for (let i = 1; i < windowEntries.length; i++) {\n const prev = windowEntries[i - 1]!;\n const curr = windowEntries[i]!;\n\n const prevFailed = new Set(prev.failedPackages[ct] ?? []);\n const currFailed = curr.failedPackages[ct] ?? [];\n const currFailedSet = new Set(currFailed);\n\n const newFailures = currFailed.filter((p) => !prevFailed.has(p));\n const fixed = [...prevFailed].filter((p) => !currFailedSet.has(p));\n const delta = currFailed.length - prevFailed.size;\n\n deltas.push(delta);\n\n // Only include entries that actually had changes\n if (newFailures.length > 0 || fixed.length > 0) {\n changelog.push({\n timestamp: curr.timestamp,\n gitCommit: curr.git.commit,\n gitMessage: curr.git.message,\n newFailures,\n fixed,\n delta,\n });\n }\n }\n\n // Summary metrics\n const previous = first.summary[ct]?.failed ?? 0;\n const current = last.summary[ct]?.failed ?? 0;\n const delta = current - previous;\n\n let trend: EnrichedTrendResult['trend'];\n if (delta > 0) {trend = 'regression';}\n else if (delta < 0) {trend = 'improvement';}\n else {trend = 'no-change';}\n\n // Velocity: average delta per entry transition\n const velocity = deltas.length > 0\n ? deltas.reduce((sum, d) => sum + d, 0) / deltas.length\n : 0;\n\n results.push({\n checkType: ct,\n label: getCheckLabel(ct),\n icon: getCheckIcon(ct),\n timeSeries,\n changelog,\n current,\n previous,\n delta,\n trend,\n velocity: Math.round(velocity * 100) / 100,\n });\n }\n\n return results;\n}\n","import type { HistoryEntry, RegressionResult } from '@kb-labs/qa-contracts';\n\n/**\n * Detect regressions by comparing the last 2 history entries.\n * A regression = new packages failing that weren't failing before.\n */\nexport function detectRegressions(history: HistoryEntry[]): RegressionResult {\n if (history.length < 2) {\n return { hasRegressions: false, regressions: [] };\n }\n\n const previous = history[history.length - 2]!;\n const current = history[history.length - 1]!;\n\n const regressions: RegressionResult['regressions'] = [];\n\n const checkTypes = [...new Set([...Object.keys(previous.failedPackages), ...Object.keys(current.failedPackages)])];\n\n for (const ct of checkTypes) {\n const prevFailed = new Set(previous.failedPackages[ct] ?? []);\n const currFailed = current.failedPackages[ct] ?? [];\n\n const newFailures = currFailed.filter((p) => !prevFailed.has(p));\n const delta = currFailed.length - prevFailed.size;\n\n if (newFailures.length > 0) {\n regressions.push({\n checkType: ct,\n delta,\n newFailures,\n });\n }\n }\n\n return {\n hasRegressions: regressions.length > 0,\n regressions,\n };\n}\n","import type { HistoryEntry, CheckType } from '@kb-labs/qa-contracts';\nimport type { PackageTimelineEntry, PackageTimelineResponse } from '@kb-labs/qa-contracts';\n\nfunction buildEntries(history: HistoryEntry[], packageName: string): { entries: PackageTimelineEntry[]; repo: string } {\n let repo = 'unknown';\n const entries: PackageTimelineEntry[] = [];\n\n for (let i = history.length - 1; i >= 0; i--) {\n const h = history[i]!;\n const checks: Record<string, 'passed' | 'failed' | 'skipped'> = {};\n let found = false;\n\n for (const ct of Object.keys(h.summary)) {\n const failedList = h.failedPackages[ct] ?? [];\n const summaryEntry = h.summary[ct];\n if (failedList.includes(packageName)) {\n checks[ct] = 'failed'; found = true;\n } else if (summaryEntry && (summaryEntry.passed > 0 || summaryEntry.failed > 0)) {\n checks[ct] = 'passed'; found = true;\n } else {\n checks[ct] = 'skipped';\n }\n }\n\n if (!found) { continue; }\n\n let submoduleCommit: string | undefined;\n if (h.submodules) {\n for (const [repoName, info] of Object.entries(h.submodules)) {\n if (repoName === repo || repo === 'unknown') {\n submoduleCommit = info.commit;\n if (repo === 'unknown') { repo = repoName; }\n }\n }\n }\n\n entries.push({\n timestamp: h.timestamp,\n git: h.git,\n submoduleCommit,\n checks: checks as Record<CheckType, 'passed' | 'failed' | 'skipped'>,\n });\n }\n\n return { entries, repo };\n}\n\nfunction computeFlakyScore(entries: PackageTimelineEntry[]): { flakyScore: number; flakyChecks: CheckType[] } {\n const allCheckTypes = new Set<string>();\n for (const entry of entries) {\n for (const k of Object.keys(entry.checks)) { allCheckTypes.add(k); }\n }\n\n const flakyChecks: CheckType[] = [];\n let totalFlips = 0;\n let totalTransitions = 0;\n\n for (const ct of allCheckTypes) {\n let flips = 0;\n let transitions = 0;\n for (let i = 1; i < entries.length; i++) {\n const prev = entries[i - 1]!.checks[ct];\n const curr = entries[i]!.checks[ct];\n if (prev === 'skipped' || curr === 'skipped' || !prev || !curr) { continue; }\n transitions++;\n if (prev !== curr) { flips++; }\n }\n if (transitions > 0 && flips / transitions > 0.3) { flakyChecks.push(ct as CheckType); }\n totalFlips += flips;\n totalTransitions += transitions;\n }\n\n return {\n flakyScore: totalTransitions > 0 ? Math.min(1, totalFlips / totalTransitions) : 0,\n flakyChecks,\n };\n}\n\nfunction computeStreak(entries: PackageTimelineEntry[]): { status: 'passing' | 'failing'; count: number } {\n const latest = entries[0];\n if (!latest) { return { status: 'passing', count: 0 }; }\n\n const streakStatus: 'passing' | 'failing' = Object.values(latest.checks).some((v) => v === 'failed') ? 'failing' : 'passing';\n let count = 1;\n for (let i = 1; i < entries.length; i++) {\n const eFail = Object.values(entries[i]!.checks).some((v) => v === 'failed');\n if ((eFail ? 'failing' : 'passing') !== streakStatus) { break; }\n count++;\n }\n return { status: streakStatus, count };\n}\n\n/**\n * Build a timeline for a specific package across QA history.\n * Computes flaky score, current streak, and first failure timestamp.\n */\nexport function getPackageTimeline(\n history: HistoryEntry[],\n packageName: string,\n): PackageTimelineResponse {\n const { entries, repo } = buildEntries(history, packageName);\n const { flakyScore, flakyChecks } = computeFlakyScore(entries);\n\n let firstFailure: string | undefined;\n for (let i = entries.length - 1; i >= 0; i--) {\n if (Object.values(entries[i]!.checks).some((v) => v === 'failed')) {\n firstFailure = entries[i]!.timestamp;\n }\n }\n\n return {\n packageName,\n repo,\n entries,\n flakyScore: Math.round(flakyScore * 100) / 100,\n flakyChecks,\n firstFailure,\n currentStreak: computeStreak(entries),\n };\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { L as LastRunData, c as collectSubmoduleInfo, g as getSubmoduleInfo, a as getWorkspacePackages, l as loadLastRun, r as runLintCheck, b as runQA, d as runTestCheck, e as runTypeCheck, s as saveLastRun } from './last-run-store-CVsUqnU0.js';
1
+ export { L as LastRunData, c as collectSubmoduleInfo, g as getSubmoduleInfo, a as getWorkspacePackages, l as loadLastRun, r as runCustomChecks, b as runLintCheck, d as runQA, e as runTestCheck, f as runTypeCheck, s as saveLastRun } from './last-run-store-C1AkskWI.js';
2
2
  export { captureBaseline, compareWithBaseline, createBaselineFromResults, loadBaseline, saveBaseline } from './baseline/index.js';
3
- export { analyzeEnrichedTrends, analyzeTrends, appendEntry, createHistoryEntry, detectRegressions, getPackageTimeline, loadHistory, saveHistory } from './history/index.js';
3
+ export { analyzeEnrichedTrends, analyzeTrends, appendEntry, appendTrendEntry, createHistoryEntry, detectRegressions, getPackageTimeline, loadHistory, loadTrendHistory, saveHistory } from './history/index.js';
4
4
  export { ReportSection, buildBaselineReport, buildDetailedJsonReport, buildDetailedRunReport, buildHistoryTable, buildJsonReport, buildRegressionsReport, buildRunReport, buildTrendsReport, groupErrors, groupResults } from './report/index.js';
5
5
  export { resolveCategories } from './categories/index.js';
6
6
  import '@kb-labs/qa-contracts';
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync, statSy
2
2
  import { join, relative, dirname, resolve } from 'path';
3
3
  import { execSync, spawnSync } from 'child_process';
4
4
  import { createHash } from 'crypto';
5
- import { TRENDS_WINDOW, PATHS, HISTORY_MAX_ENTRIES, getCheckIcon, getCheckLabel } from '@kb-labs/qa-contracts';
5
+ import { TRENDS_WINDOW, PATHS, HISTORY_MAX_ENTRIES, getCheckIcon, getCheckLabel, QA_DATA_DIR } from '@kb-labs/qa-contracts';
6
6
 
7
7
  // src/runner/workspace.ts
8
8
  function getSubmoduleInfo(repoDir, repoName) {
@@ -279,21 +279,39 @@ var ID_MAP = {
279
279
  tests: "test"
280
280
  };
281
281
  function emptyResult() {
282
- return { passed: [], failed: [], skipped: [], errors: {} };
282
+ return { passed: [], failed: [], skipped: [], errors: {}, details: {} };
283
283
  }
284
- function runCommand(command, args, cwd, timeoutMs) {
284
+ function runCommand(command, args, cwd, timeoutMs, extraEnv) {
285
285
  try {
286
- const result = spawnSync(command, args, { cwd, timeout: timeoutMs, encoding: "utf-8", shell: false });
286
+ const env = extraEnv ? { ...process.env, ...extraEnv } : void 0;
287
+ const result = spawnSync(command, args, { cwd, timeout: timeoutMs, encoding: "utf-8", shell: false, env });
287
288
  const stdout = result.stdout ?? "";
288
289
  const stderr = result.stderr ?? "";
289
290
  const exitCode = result.status ?? 1;
290
291
  return { ok: exitCode === 0, stdout, stderr, exitCode };
291
292
  } catch (e) {
292
- return { ok: false, stdout: "", stderr: e.message ?? String(e), exitCode: 1 };
293
+ return { ok: false, stdout: "", stderr: e instanceof Error ? e.message : String(e), exitCode: 1 };
293
294
  }
294
295
  }
296
+ function parseTypedOutput(stdout) {
297
+ try {
298
+ const parsed = JSON.parse(stdout);
299
+ if (typeof parsed === "object" && parsed !== null && "ok" in parsed) {
300
+ return {
301
+ ok: parsed.ok === true,
302
+ items: Array.isArray(parsed.items) ? parsed.items : []
303
+ };
304
+ }
305
+ } catch {
306
+ }
307
+ return null;
308
+ }
295
309
  function evaluate(check, stdout, stderr, exitCode) {
296
310
  if ((check.parser ?? "exitcode") === "json") {
311
+ const typed = parseTypedOutput(stdout);
312
+ if (typed !== null) {
313
+ return typed.ok;
314
+ }
297
315
  try {
298
316
  const parsed = JSON.parse(stdout);
299
317
  return parsed.ok === true || parsed.success === true || parsed.status === "ok";
@@ -303,23 +321,54 @@ function evaluate(check, stdout, stderr, exitCode) {
303
321
  }
304
322
  return exitCode === 0;
305
323
  }
306
- function recordResult(bucket, key, passed, stdout, stderr, exitCode, canonicalId, onProgress, durationMs) {
324
+ function recordResult(bucket, key, passed, stdout, stderr, exitCode, check, canonicalId, onProgress, durationMs) {
307
325
  if (passed) {
308
326
  bucket.passed.push(key);
309
327
  onProgress?.(canonicalId, key, "pass", durationMs);
310
328
  } else {
311
329
  bucket.failed.push(key);
312
330
  bucket.errors[key] = stderr || stdout || `Exit code ${exitCode}`;
331
+ if ((check.parser ?? "exitcode") === "json") {
332
+ const typed = parseTypedOutput(stdout);
333
+ if (typed && typed.items.length > 0) {
334
+ bucket.details[key] = typed.items;
335
+ }
336
+ }
313
337
  onProgress?.(canonicalId, key, "fail", durationMs);
314
338
  }
315
339
  }
316
- function runInRepoRoot(check, canonicalId, resolvedArgs, rootDir, bucket, onProgress) {
340
+ function getDiffPackageDirs(rootDir, base, diffFilter) {
341
+ try {
342
+ const filter = diffFilter ? `--diff-filter=${diffFilter}` : "";
343
+ const args = ["diff", filter, `${base}...HEAD`, "--name-only"].filter(Boolean);
344
+ const output = execSync(`git ${args.join(" ")}`, { cwd: rootDir, encoding: "utf-8" }).trim();
345
+ if (!output) {
346
+ return /* @__PURE__ */ new Set();
347
+ }
348
+ return new Set(output.split("\n").map((f) => f.split("/")[0]).filter((s) => Boolean(s)));
349
+ } catch {
350
+ return /* @__PURE__ */ new Set();
351
+ }
352
+ }
353
+ function filterPackagesByDiff(packages, rootDir, diffFilter, base) {
354
+ const touchedDirs = getDiffPackageDirs(rootDir, base, diffFilter);
355
+ if (touchedDirs.size === 0) {
356
+ return [];
357
+ }
358
+ return packages.filter((pkg) => {
359
+ const relParts = pkg.relativePath.split("/");
360
+ return relParts.some((_, i) => touchedDirs.has(relParts.slice(0, i + 1).join("/")));
361
+ });
362
+ }
363
+ function runInRepoRoot(check, canonicalId, resolvedArgs, rootDir, bucket, onProgress, diffBase) {
317
364
  const startMs = Date.now();
318
- const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, rootDir, check.timeoutMs ?? 12e4);
319
- recordResult(bucket, rootDir, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, canonicalId, onProgress, Date.now() - startMs);
365
+ const env = diffBase ? { KB_QA_BASE: diffBase } : void 0;
366
+ const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, rootDir, check.timeoutMs ?? 12e4, env);
367
+ recordResult(bucket, rootDir, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, check, canonicalId, onProgress, Date.now() - startMs);
320
368
  }
321
- function runInScopePath(check, canonicalId, resolvedArgs, packages, rootDir, bucket, onProgress) {
369
+ function runInScopePath(check, canonicalId, resolvedArgs, packages, rootDir, bucket, onProgress, diffBase) {
322
370
  const seen = /* @__PURE__ */ new Set();
371
+ const env = diffBase ? { KB_QA_BASE: diffBase } : void 0;
323
372
  for (const pkg of packages) {
324
373
  if (seen.has(pkg.repo)) {
325
374
  continue;
@@ -330,8 +379,8 @@ function runInScopePath(check, canonicalId, resolvedArgs, packages, rootDir, buc
330
379
  continue;
331
380
  }
332
381
  const startMs = Date.now();
333
- const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, scopeDir, check.timeoutMs ?? 12e4);
334
- recordResult(bucket, pkg.repo, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, canonicalId, onProgress, Date.now() - startMs);
382
+ const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, scopeDir, check.timeoutMs ?? 12e4, env);
383
+ recordResult(bucket, pkg.repo, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, check, canonicalId, onProgress, Date.now() - startMs);
335
384
  }
336
385
  }
337
386
  function getPnpmScriptName(check) {
@@ -352,9 +401,10 @@ function hasNpmScript(pkgDir, scriptName) {
352
401
  return false;
353
402
  }
354
403
  }
355
- function runPerPackage(check, canonicalId, resolvedArgs, packages, bucket, onProgress) {
404
+ function runPerPackage(check, canonicalId, resolvedArgs, packages, bucket, onProgress, diffBase) {
356
405
  const sortedPackages = check.ordered ? sortByBuildLayers(packages) : packages;
357
406
  const scriptName = getPnpmScriptName(check);
407
+ const env = diffBase ? { KB_QA_BASE: diffBase } : void 0;
358
408
  for (const pkg of sortedPackages) {
359
409
  if (scriptName && !hasNpmScript(pkg.dir, scriptName)) {
360
410
  bucket.skipped.push(pkg.name);
@@ -362,11 +412,11 @@ function runPerPackage(check, canonicalId, resolvedArgs, packages, bucket, onPro
362
412
  continue;
363
413
  }
364
414
  const startMs = Date.now();
365
- const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, pkg.dir, check.timeoutMs ?? 12e4);
366
- recordResult(bucket, pkg.name, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, canonicalId, onProgress, Date.now() - startMs);
415
+ const { stderr, exitCode, stdout } = runCommand(check.command, resolvedArgs, pkg.dir, check.timeoutMs ?? 12e4, env);
416
+ recordResult(bucket, pkg.name, evaluate(check, stdout, stderr, exitCode), stdout, stderr, exitCode, check, canonicalId, onProgress, Date.now() - startMs);
367
417
  }
368
418
  }
369
- function runCustomChecks(checks, packages, rootDir, onProgress) {
419
+ function runCustomChecks(checks, packages, rootDir, onProgress, diffBase) {
370
420
  const results = {};
371
421
  for (const check of checks) {
372
422
  const canonicalId = ID_MAP[check.id.toLowerCase()] ?? check.id;
@@ -380,12 +430,34 @@ function runCustomChecks(checks, packages, rootDir, onProgress) {
380
430
  );
381
431
  const runIn = check.runIn ?? "perPackage";
382
432
  if (runIn === "repoRoot") {
383
- runInRepoRoot(check, canonicalId, resolvedArgs, rootDir, bucket, onProgress);
433
+ runInRepoRoot(check, canonicalId, resolvedArgs, rootDir, bucket, onProgress, diffBase);
384
434
  } else if (runIn === "scopePath") {
385
- runInScopePath(check, canonicalId, resolvedArgs, packages, rootDir, bucket, onProgress);
435
+ runInScopePath(check, canonicalId, resolvedArgs, packages, rootDir, bucket, onProgress, diffBase);
436
+ } else if (runIn === "diffOnly" || runIn === "newFiles") {
437
+ const base = diffBase ?? "main";
438
+ const diffFilter = runIn === "newFiles" ? "A" : "";
439
+ const filteredPackages = filterPackagesByDiff(packages, rootDir, diffFilter, base);
440
+ if (filteredPackages.length === 0) {
441
+ bucket.skipped.push(...packages.map((p) => p.name));
442
+ for (const pkg of packages) {
443
+ onProgress?.(canonicalId, pkg.name, "skip");
444
+ }
445
+ } else {
446
+ const filteredNames = new Set(filteredPackages.map((p) => p.name));
447
+ for (const pkg of packages) {
448
+ if (!filteredNames.has(pkg.name)) {
449
+ bucket.skipped.push(pkg.name);
450
+ onProgress?.(canonicalId, pkg.name, "skip");
451
+ }
452
+ }
453
+ runPerPackage(check, canonicalId, resolvedArgs, filteredPackages, bucket, onProgress, base);
454
+ }
386
455
  } else {
387
456
  runPerPackage(check, canonicalId, resolvedArgs, packages, bucket, onProgress);
388
457
  }
458
+ if (Object.keys(bucket.details).length > 0) {
459
+ results[canonicalId].details = bucket.details;
460
+ }
389
461
  }
390
462
  return results;
391
463
  }
@@ -493,8 +565,9 @@ function runBuildCheck(options) {
493
565
  } catch (err) {
494
566
  const durationMs = Date.now() - startMs;
495
567
  result.failed.push(pkg.name);
496
- const rawErr = (err.stderr || err.stdout || err.message || "").trim();
497
- result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Build failed (exit code ${err.status ?? 1})`;
568
+ const spawnErr = err;
569
+ const rawErr = (spawnErr.stderr || spawnErr.stdout || spawnErr.message || "").trim();
570
+ result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Build failed (exit code ${spawnErr.status ?? 1})`;
498
571
  onProgress?.(pkg.name, "fail", durationMs);
499
572
  }
500
573
  }
@@ -522,8 +595,9 @@ function runLintCheck(options) {
522
595
  onProgress?.(pkg.name, "pass", Date.now() - startMs);
523
596
  } catch (err) {
524
597
  result.failed.push(pkg.name);
525
- const rawErr = (err.stdout || err.stderr || err.message || "").trim();
526
- result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Lint failed (exit code ${err.status ?? 1})`;
598
+ const spawnErr = err;
599
+ const rawErr = (spawnErr.stdout || spawnErr.stderr || spawnErr.message || "").trim();
600
+ result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Lint failed (exit code ${spawnErr.status ?? 1})`;
527
601
  onProgress?.(pkg.name, "fail", Date.now() - startMs);
528
602
  }
529
603
  }
@@ -551,8 +625,9 @@ function runTypeCheck(options) {
551
625
  onProgress?.(pkg.name, "pass", Date.now() - startMs);
552
626
  } catch (err) {
553
627
  result.failed.push(pkg.name);
554
- const rawErr = (err.stdout || err.stderr || err.message || "").trim();
555
- result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Type check failed (exit code ${err.status ?? 1})`;
628
+ const spawnErr = err;
629
+ const rawErr = (spawnErr.stdout || spawnErr.stderr || spawnErr.message || "").trim();
630
+ result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Type check failed (exit code ${spawnErr.status ?? 1})`;
556
631
  onProgress?.(pkg.name, "fail", Date.now() - startMs);
557
632
  }
558
633
  }
@@ -589,8 +664,9 @@ function runTestCheck(options) {
589
664
  onProgress?.(pkg.name, "pass", Date.now() - startMs);
590
665
  } catch (err) {
591
666
  result.failed.push(pkg.name);
592
- const rawErr = (err.stdout || err.stderr || err.message || "").trim();
593
- result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Test failed (exit code ${err.status ?? 1})`;
667
+ const spawnErr = err;
668
+ const rawErr = (spawnErr.stdout || spawnErr.stderr || spawnErr.message || "").trim();
669
+ result.errors[pkg.name] = rawErr.slice(0, 2e3) || `Test failed (exit code ${spawnErr.status ?? 1})`;
594
670
  onProgress?.(pkg.name, "fail", Date.now() - startMs);
595
671
  }
596
672
  }
@@ -776,6 +852,35 @@ function compareWithBaseline(results, baseline) {
776
852
  }
777
853
  return diff;
778
854
  }
855
+ function trendPath(rootDir, checkId) {
856
+ return join(rootDir, QA_DATA_DIR, "trends", `${checkId}.json`);
857
+ }
858
+ function loadTrendHistory(rootDir, checkId) {
859
+ const path = trendPath(rootDir, checkId);
860
+ if (!existsSync(path)) {
861
+ return [];
862
+ }
863
+ try {
864
+ return JSON.parse(readFileSync(path, "utf-8"));
865
+ } catch {
866
+ return [];
867
+ }
868
+ }
869
+ function appendTrendEntry(rootDir, checkId, entry) {
870
+ const path = trendPath(rootDir, checkId);
871
+ const dir = dirname(path);
872
+ if (!existsSync(dir)) {
873
+ mkdirSync(dir, { recursive: true });
874
+ }
875
+ const entries = loadTrendHistory(rootDir, checkId);
876
+ entries.push(entry);
877
+ while (entries.length > HISTORY_MAX_ENTRIES) {
878
+ entries.shift();
879
+ }
880
+ writeFileSync(path, JSON.stringify(entries, null, 2));
881
+ }
882
+
883
+ // src/history/history-store.ts
779
884
  function loadHistory(rootDir) {
780
885
  const path = join(rootDir, PATHS.HISTORY);
781
886
  if (!existsSync(path)) {
@@ -795,7 +900,7 @@ function saveHistory(rootDir, entries) {
795
900
  }
796
901
  writeFileSync(path, JSON.stringify(entries, null, 2));
797
902
  }
798
- function createHistoryEntry(results, rootDir, packages) {
903
+ function createHistoryEntry(results, rootDir, packages, runContext) {
799
904
  let commit = "unknown";
800
905
  let branch = "unknown";
801
906
  let message = "";
@@ -835,16 +940,24 @@ function createHistoryEntry(results, rootDir, packages) {
835
940
  submodules,
836
941
  status: hasFailures ? "failed" : "passed",
837
942
  summary,
838
- failedPackages
943
+ failedPackages,
944
+ ...runContext ? { runContext } : {}
839
945
  };
840
946
  }
841
- function appendEntry(rootDir, entry) {
947
+ function appendEntry(rootDir, entry, checks) {
842
948
  const history = loadHistory(rootDir);
843
949
  history.push(entry);
844
950
  while (history.length > HISTORY_MAX_ENTRIES) {
845
951
  history.shift();
846
952
  }
847
953
  saveHistory(rootDir, history);
954
+ if (checks) {
955
+ for (const check of checks) {
956
+ if (check.trending && entry.summary[check.id] !== void 0) {
957
+ appendTrendEntry(rootDir, check.id, entry);
958
+ }
959
+ }
960
+ }
848
961
  }
849
962
  function collectCheckTypes(entries) {
850
963
  const s = /* @__PURE__ */ new Set();
@@ -1092,11 +1205,22 @@ function getPackageTimeline(history, packageName) {
1092
1205
  }
1093
1206
 
1094
1207
  // src/report/json-reporter.ts
1095
- function buildJsonReport(results, diff) {
1096
- const hasFailures = Object.values(results).some((r) => r.failed.length > 0);
1208
+ function buildJsonReport(results, diff, checks) {
1209
+ const severityMap = {};
1210
+ if (checks) {
1211
+ for (const c of checks) {
1212
+ severityMap[c.id] = c.severity ?? "blocker";
1213
+ }
1214
+ }
1215
+ const hasBlockerFailures = Object.entries(results).some(([ct, r]) => {
1216
+ const sev = severityMap[ct] ?? "blocker";
1217
+ return sev !== "info" && sev !== "warning" && r.failed.length > 0;
1218
+ });
1097
1219
  const summary = {};
1098
1220
  const failures = {};
1099
1221
  const errors = {};
1222
+ const blockers = [];
1223
+ const warnings = [];
1100
1224
  for (const ct of Object.keys(results)) {
1101
1225
  const r = results[ct];
1102
1226
  const total = r.passed.length + r.failed.length + r.skipped.length;
@@ -1108,18 +1232,36 @@ function buildJsonReport(results, diff) {
1108
1232
  };
1109
1233
  failures[ct] = [...r.failed];
1110
1234
  errors[ct] = { ...r.errors };
1235
+ if (r.failed.length > 0) {
1236
+ const sev = severityMap[ct] ?? "blocker";
1237
+ const items = r.failed.flatMap((target) => {
1238
+ const detailItems = r.details?.[target];
1239
+ if (detailItems && detailItems.length > 0) {
1240
+ return detailItems;
1241
+ }
1242
+ const msg = r.errors[target];
1243
+ return msg ? [{ target, message: msg }] : [{ target, message: `check ${ct} failed` }];
1244
+ });
1245
+ if (sev === "blocker") {
1246
+ blockers.push({ check: ct, items });
1247
+ } else if (sev === "warning") {
1248
+ warnings.push({ check: ct, items });
1249
+ }
1250
+ }
1111
1251
  }
1112
1252
  return {
1113
- status: hasFailures ? "failed" : "passed",
1253
+ status: hasBlockerFailures ? "failed" : "passed",
1114
1254
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1115
1255
  summary,
1116
1256
  failures,
1117
1257
  errors,
1118
- baseline: diff ?? null
1258
+ baseline: diff ?? null,
1259
+ blockers,
1260
+ warnings
1119
1261
  };
1120
1262
  }
1121
- function buildDetailedJsonReport(results, grouped, diff) {
1122
- const base = buildJsonReport(results, diff);
1263
+ function buildDetailedJsonReport(results, grouped, diff, checks) {
1264
+ const base = buildJsonReport(results, diff, checks);
1123
1265
  return { ...base, grouped };
1124
1266
  }
1125
1267
  function icon(ct) {
@@ -1545,6 +1687,6 @@ function resolveCategories(packages, config) {
1545
1687
  return map;
1546
1688
  }
1547
1689
 
1548
- export { analyzeEnrichedTrends, analyzeTrends, appendEntry, buildBaselineReport, buildDetailedJsonReport, buildDetailedRunReport, buildHistoryTable, buildJsonReport, buildRegressionsReport, buildRunReport, buildTrendsReport, captureBaseline, collectSubmoduleInfo, compareWithBaseline, createBaselineFromResults, createHistoryEntry, detectRegressions, getPackageTimeline, getSubmoduleInfo, getWorkspacePackages, groupErrors, groupResults, loadBaseline, loadHistory, loadLastRun, resolveCategories, runLintCheck, runQA, runTestCheck, runTypeCheck, saveBaseline, saveHistory, saveLastRun };
1690
+ export { analyzeEnrichedTrends, analyzeTrends, appendEntry, appendTrendEntry, buildBaselineReport, buildDetailedJsonReport, buildDetailedRunReport, buildHistoryTable, buildJsonReport, buildRegressionsReport, buildRunReport, buildTrendsReport, captureBaseline, collectSubmoduleInfo, compareWithBaseline, createBaselineFromResults, createHistoryEntry, detectRegressions, getPackageTimeline, getSubmoduleInfo, getWorkspacePackages, groupErrors, groupResults, loadBaseline, loadHistory, loadLastRun, loadTrendHistory, resolveCategories, runCustomChecks, runLintCheck, runQA, runTestCheck, runTypeCheck, saveBaseline, saveHistory, saveLastRun };
1549
1691
  //# sourceMappingURL=index.js.map
1550
1692
  //# sourceMappingURL=index.js.map