@elench/testkit 0.1.80 → 0.1.81

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 (43) hide show
  1. package/README.md +50 -35
  2. package/lib/cli/args.mjs +2 -14
  3. package/lib/cli/args.test.mjs +1 -17
  4. package/lib/cli/command-helpers.mjs +1 -20
  5. package/lib/cli/entrypoint.mjs +0 -4
  6. package/lib/cli/presentation/colors.mjs +1 -1
  7. package/lib/cli/presentation/failure-presentation.mjs +4 -4
  8. package/lib/cli/presentation/run-reporter.mjs +23 -9
  9. package/lib/cli/presentation/run-reporter.test.mjs +12 -6
  10. package/lib/cli/presentation/summary-box.test.mjs +4 -4
  11. package/lib/cli/viewer.mjs +18 -19
  12. package/lib/config/index.mjs +6 -6
  13. package/lib/config/runtime.mjs +8 -8
  14. package/lib/config-api/index.d.ts +4 -4
  15. package/lib/package.test.mjs +4 -4
  16. package/lib/{known-failures → regressions}/github.mjs +36 -78
  17. package/lib/regressions/github.test.mjs +324 -0
  18. package/lib/regressions/index.d.ts +189 -0
  19. package/lib/{known-failures → regressions}/index.mjs +90 -93
  20. package/lib/{known-failures → regressions}/index.test.mjs +37 -48
  21. package/lib/runner/formatting.mjs +49 -34
  22. package/lib/runner/formatting.test.mjs +16 -15
  23. package/lib/runner/metadata.mjs +1 -1
  24. package/lib/runner/orchestrator.mjs +7 -9
  25. package/lib/runner/regressions.mjs +304 -0
  26. package/lib/runner/{triage.test.mjs → regressions.test.mjs} +50 -36
  27. package/lib/runner/reporting.mjs +2 -2
  28. package/lib/runner/reporting.test.mjs +2 -2
  29. package/lib/runner/run-finalization.mjs +18 -30
  30. package/lib/runner/template-steps.mjs +2 -2
  31. package/node_modules/@elench/next-analysis/package.json +1 -1
  32. package/node_modules/@elench/testkit-bridge/package.json +2 -2
  33. package/node_modules/@elench/testkit-protocol/package.json +1 -1
  34. package/node_modules/@elench/ts-analysis/package.json +1 -1
  35. package/package.json +8 -8
  36. package/lib/cli/commands/known-failures/render.mjs +0 -19
  37. package/lib/cli/commands/known-failures/validate.mjs +0 -20
  38. package/lib/cli/known-failures.mjs +0 -164
  39. package/lib/known-failures/github.test.mjs +0 -512
  40. package/lib/known-failures/index.d.ts +0 -192
  41. package/lib/runner/triage.mjs +0 -221
  42. /package/lib/{known-failures → regressions}/github-cache.mjs +0 -0
  43. /package/lib/{known-failures → regressions}/github-transport.mjs +0 -0
@@ -0,0 +1,189 @@
1
+ export type RegressionClassification =
2
+ | "expected_failure"
3
+ | "infra"
4
+ | "product_bug"
5
+ | "stale_test"
6
+ | "test_bug";
7
+
8
+ export interface RegressionIssueRef {
9
+ repo: string;
10
+ number: number;
11
+ }
12
+
13
+ export interface RegressionFingerprint {
14
+ service?: string;
15
+ type?: string;
16
+ path: string;
17
+ failureKey?: string;
18
+ errorIncludes?: string;
19
+ }
20
+
21
+ export interface RegressionCatalogEntry {
22
+ id: string;
23
+ classification: RegressionClassification;
24
+ issue: RegressionIssueRef;
25
+ summary: string;
26
+ cause: string;
27
+ lastReviewedAt: string;
28
+ fingerprints: RegressionFingerprint[];
29
+ }
30
+
31
+ export interface RegressionCatalogDocument {
32
+ schemaVersion: 1;
33
+ issueRepo?: string | null;
34
+ entries: RegressionCatalogEntry[];
35
+ }
36
+
37
+ export interface RegressionCatalogValidationResult {
38
+ errors: string[];
39
+ warnings: string[];
40
+ stats: {
41
+ entries: number;
42
+ fingerprints: number;
43
+ failedTests: number;
44
+ diagnosedFailedTests: number;
45
+ newFailedTests: number;
46
+ };
47
+ }
48
+
49
+ export interface RegressionSyncConfig {
50
+ provider?: "github";
51
+ mode?: "off" | "warn" | "error";
52
+ cacheTtlSeconds?: number;
53
+ }
54
+
55
+ export interface RegressionSyncFinding {
56
+ code: string;
57
+ severity: "warning" | "error";
58
+ message: string;
59
+ }
60
+
61
+ export interface RegressionSyncEntry {
62
+ id: string;
63
+ summary: string;
64
+ issue: RegressionIssueRef;
65
+ observed: {
66
+ matchedTests: number;
67
+ executedTests: number;
68
+ passedTests: number;
69
+ failedTests: number;
70
+ skippedTests: number;
71
+ notRunTests: number;
72
+ reproduced: boolean;
73
+ };
74
+ github: {
75
+ exists: boolean | null;
76
+ title: string | null;
77
+ state: string | null;
78
+ url: string | null;
79
+ checkedAt: string | null;
80
+ cached: boolean;
81
+ };
82
+ findings: RegressionSyncFinding[];
83
+ status: string;
84
+ }
85
+
86
+ export interface RegressionSyncResult {
87
+ schemaVersion: 2;
88
+ provider: "github";
89
+ mode: "off" | "warn" | "error";
90
+ checkedAt: string;
91
+ repo: {
92
+ detected: string | null;
93
+ remoteUrl: string | null;
94
+ configured: string | null;
95
+ };
96
+ availability: {
97
+ hasFreshData: boolean;
98
+ usedCachedFallback: boolean;
99
+ };
100
+ findings: RegressionSyncFinding[];
101
+ summary: {
102
+ entries: number;
103
+ observedEntries: number;
104
+ errors: number;
105
+ warnings: number;
106
+ byCode: Record<string, number>;
107
+ byStatus: Record<string, number>;
108
+ };
109
+ entries: RegressionSyncEntry[];
110
+ }
111
+
112
+ export declare function loadRegressionCatalogConfig(
113
+ productDir: string,
114
+ config: { file?: string | null } | null
115
+ ): RegressionCatalogDocument | null;
116
+ export declare function loadRegressionCatalogDocument(
117
+ filePath: string,
118
+ relativePath?: string
119
+ ): RegressionCatalogDocument;
120
+ export declare function normalizeRegressionCatalogDocument(
121
+ document: unknown,
122
+ relativePath?: string
123
+ ): RegressionCatalogDocument;
124
+ export declare function validateRegressionCatalogDocument(
125
+ document: RegressionCatalogDocument,
126
+ options?: {
127
+ productDir?: string;
128
+ statusArtifactPath?: string;
129
+ statusArtifact?: unknown;
130
+ }
131
+ ): RegressionCatalogValidationResult;
132
+ export declare function renderRegressionCatalogMarkdown(
133
+ document: RegressionCatalogDocument
134
+ ): string;
135
+ export declare function findMatchingRegressionEntries(
136
+ document: RegressionCatalogDocument,
137
+ fileSummary: {
138
+ service?: string;
139
+ type?: string;
140
+ path: string;
141
+ status?: string;
142
+ error?: string | null;
143
+ failureDetails?: Array<{ key?: string; title?: string }>;
144
+ }
145
+ ): RegressionCatalogEntry[];
146
+ export declare function matchesRegressionEntry(
147
+ entry: RegressionCatalogEntry,
148
+ fileSummary: {
149
+ service?: string;
150
+ type?: string;
151
+ path: string;
152
+ error?: string | null;
153
+ failureDetails?: Array<{ key?: string; title?: string }>;
154
+ }
155
+ ): boolean;
156
+ export declare function buildRegressionFileIdentity(
157
+ service: string,
158
+ type: string,
159
+ filePath: string
160
+ ): string;
161
+
162
+ export declare function normalizeRegressionSyncConfig(
163
+ value: unknown
164
+ ): {
165
+ provider: "github";
166
+ mode: "off" | "warn" | "error";
167
+ cacheTtlSeconds: number;
168
+ } | null;
169
+ export declare function validateRegressionIssues(options: {
170
+ productDir: string;
171
+ document: RegressionCatalogDocument | null;
172
+ runArtifact?: unknown;
173
+ statusArtifact?: unknown;
174
+ config?: RegressionSyncConfig | null;
175
+ gitMetadata?: {
176
+ repoSlug?: string | null;
177
+ remoteUrl?: string | null;
178
+ } | null;
179
+ }): Promise<RegressionSyncResult | null>;
180
+ export declare function shouldFailRegressionSync(
181
+ result: RegressionSyncResult | null
182
+ ): boolean;
183
+ export declare function buildRegressionSyncSummaryLines(
184
+ result: RegressionSyncResult | null
185
+ ): string[];
186
+ export declare function buildRegressionSyncSummaryParts(
187
+ result: RegressionSyncResult | null
188
+ ): string[];
189
+ export declare function parseGitHubRepoSlug(remoteUrl: string | null | undefined): string | null;
@@ -8,34 +8,33 @@ const CLASSIFICATIONS = new Set([
8
8
  "stale_test",
9
9
  "test_bug",
10
10
  ]);
11
- const STATES = new Set(["closed", "open"]);
12
11
 
13
- export function loadKnownFailuresConfig(productDir, config) {
14
- const relativePath = config?.knownFailuresFile;
12
+ export function loadRegressionCatalogConfig(productDir, config) {
13
+ const relativePath = config?.file;
15
14
  if (!relativePath) return null;
16
15
 
17
16
  const absolutePath = path.resolve(productDir, relativePath);
18
17
  if (!fs.existsSync(absolutePath)) {
19
- throw new Error(`Known failures file not found: ${relativePath}`);
18
+ throw new Error(`Regression catalog not found: ${relativePath}`);
20
19
  }
21
20
 
22
- return loadKnownFailuresDocument(absolutePath, relativePath);
21
+ return loadRegressionCatalogDocument(absolutePath, relativePath);
23
22
  }
24
23
 
25
- export function loadKnownFailuresDocument(filePath, relativePath = filePath) {
24
+ export function loadRegressionCatalogDocument(filePath, relativePath = filePath) {
26
25
  let parsed;
27
26
  try {
28
27
  parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
29
28
  } catch (error) {
30
29
  throw new Error(
31
- `Could not parse known failures file ${relativePath}: ${formatErrorMessage(error)}`
30
+ `Could not parse regression catalog ${relativePath}: ${formatErrorMessage(error)}`
32
31
  );
33
32
  }
34
33
 
35
- return normalizeKnownFailuresDocument(parsed, relativePath);
34
+ return normalizeRegressionCatalogDocument(parsed, relativePath);
36
35
  }
37
36
 
38
- export function normalizeKnownFailuresDocument(document, relativePath = "known failures file") {
37
+ export function normalizeRegressionCatalogDocument(document, relativePath = "regression catalog") {
39
38
  if (!document || typeof document !== "object") {
40
39
  throw new Error(`${relativePath} must contain a JSON object`);
41
40
  }
@@ -48,7 +47,7 @@ export function normalizeKnownFailuresDocument(document, relativePath = "known f
48
47
 
49
48
  const ids = new Set();
50
49
  const entries = document.entries.map((entry, index) => {
51
- const normalized = normalizeKnownFailureEntry(entry, `${relativePath} entries[${index}]`);
50
+ const normalized = normalizeRegressionEntry(entry, `${relativePath} entries[${index}]`);
52
51
  if (ids.has(normalized.id)) {
53
52
  throw new Error(`${relativePath} has duplicate entry id "${normalized.id}"`);
54
53
  }
@@ -63,40 +62,45 @@ export function normalizeKnownFailuresDocument(document, relativePath = "known f
63
62
  };
64
63
  }
65
64
 
66
- export function validateKnownFailuresDocument(
65
+ export function validateRegressionCatalogDocument(
67
66
  document,
68
67
  { productDir, statusArtifactPath, statusArtifact } = {}
69
68
  ) {
70
69
  const errors = [];
71
70
  const warnings = [];
72
- const matchKeys = new Set();
73
- let matchCount = 0;
71
+ const fingerprintKeys = new Set();
72
+ let fingerprintCount = 0;
74
73
 
75
74
  for (const entry of document.entries) {
76
- for (const match of entry.matches) {
77
- matchCount += 1;
75
+ for (const fingerprint of entry.fingerprints) {
76
+ fingerprintCount += 1;
78
77
  if (productDir) {
79
- const absolutePath = path.join(productDir, match.path);
78
+ const absolutePath = path.join(productDir, fingerprint.path);
80
79
  if (!fs.existsSync(absolutePath)) {
81
- errors.push(`Missing matched file: ${match.path} (${entry.id})`);
80
+ errors.push(`Missing matched file: ${fingerprint.path} (${entry.id})`);
82
81
  }
83
82
  }
84
83
 
85
- const matchKey = [
84
+ const fingerprintKey = [
86
85
  entry.id,
87
- match.service || "",
88
- match.type || "",
89
- match.path,
90
- match.failureKey || "",
91
- match.errorIncludes || "",
86
+ fingerprint.service || "",
87
+ fingerprint.type || "",
88
+ fingerprint.path,
89
+ fingerprint.failureKey || "",
90
+ fingerprint.errorIncludes || "",
92
91
  ].join("::");
93
- if (matchKeys.has(matchKey)) {
94
- errors.push(`Duplicate match selector in ${entry.id}: ${match.path}`);
92
+ if (fingerprintKeys.has(fingerprintKey)) {
93
+ errors.push(`Duplicate fingerprint selector in ${entry.id}: ${fingerprint.path}`);
95
94
  }
96
- matchKeys.add(matchKey);
97
-
98
- if (!match.path.includes("__testkit__") || !match.path.endsWith(".testkit.ts")) {
99
- warnings.push(`Match path does not look like a testkit file: ${match.path} (${entry.id})`);
95
+ fingerprintKeys.add(fingerprintKey);
96
+
97
+ if (
98
+ !fingerprint.path.includes("__testkit__") ||
99
+ !fingerprint.path.endsWith(".testkit.ts")
100
+ ) {
101
+ warnings.push(
102
+ `Fingerprint path does not look like a testkit file: ${fingerprint.path} (${entry.id})`
103
+ );
100
104
  }
101
105
  }
102
106
 
@@ -105,14 +109,11 @@ export function validateKnownFailuresDocument(
105
109
  `Entry ${entry.id} uses issue repo ${entry.issue.repo} instead of document issueRepo ${document.issueRepo}`
106
110
  );
107
111
  }
108
- if (!entry.issue.url.endsWith(`/issues/${entry.issue.number}`)) {
109
- errors.push(`Issue URL does not match issue number for ${entry.id}`);
110
- }
111
112
  }
112
113
 
113
114
  let failedTests = 0;
114
- let triagedFailedTests = 0;
115
- let untriagedFailedTests = 0;
115
+ let diagnosedFailedTests = 0;
116
+ let newFailedTests = 0;
116
117
  const parsedStatusArtifact = statusArtifact
117
118
  ? parseStatusArtifact(statusArtifact)
118
119
  : statusArtifactPath && fs.existsSync(statusArtifactPath)
@@ -122,11 +123,11 @@ export function validateKnownFailuresDocument(
122
123
  const failed = parsedStatusArtifact.tests.filter((test) => test.status === "failed");
123
124
  failedTests = failed.length;
124
125
  for (const test of failed) {
125
- if (findMatchingKnownFailureEntries(document, test).length > 0) {
126
- triagedFailedTests += 1;
126
+ if (findMatchingRegressionEntries(document, test).length > 0) {
127
+ diagnosedFailedTests += 1;
127
128
  } else {
128
- untriagedFailedTests += 1;
129
- warnings.push(`Untriaged failed test: ${test.path}`);
129
+ newFailedTests += 1;
130
+ warnings.push(`New failing test not yet in regression catalog: ${test.path}`);
130
131
  }
131
132
  }
132
133
 
@@ -135,41 +136,39 @@ export function validateKnownFailuresDocument(
135
136
  warnings,
136
137
  stats: {
137
138
  entries: document.entries.length,
138
- matches: matchCount,
139
+ fingerprints: fingerprintCount,
139
140
  failedTests,
140
- triagedFailedTests,
141
- untriagedFailedTests,
141
+ diagnosedFailedTests,
142
+ newFailedTests,
142
143
  },
143
144
  };
144
145
  }
145
146
 
146
- export function renderKnownFailuresMarkdown(document) {
147
+ export function renderRegressionCatalogMarkdown(document) {
147
148
  const lines = [
148
- "# Bourne Bugs",
149
+ "# Regression Catalog",
149
150
  "",
150
- "Canonical source: `testkit.known-failures.json`",
151
+ "Canonical source: `testkit.regressions.json`",
151
152
  "",
152
- `Tracked bug classes: ${document.entries.length}`,
153
+ `Tracked regressions: ${document.entries.length}`,
153
154
  `Tracked issue references: ${new Set(document.entries.map((entry) => entry.issue.number)).size}`,
154
155
  "",
155
156
  ];
156
157
 
157
158
  document.entries.forEach((entry, index) => {
158
159
  lines.push(
159
- `## ${index + 1}. ${entry.title} — [#${entry.issue.number}](${entry.issue.url})`,
160
+ `## ${index + 1}. ${entry.summary} — #${entry.issue.number}`,
160
161
  "",
161
162
  `- Classification: \`${entry.classification}\``,
162
- `- State: \`${entry.state}\``,
163
- `- Description: ${entry.description}`,
164
- `- Why failing: ${entry.whyFailing}`,
163
+ `- Cause: ${entry.cause}`,
165
164
  `- Last reviewed: ${entry.lastReviewedAt}`,
166
- "- Matches:"
165
+ "- Fingerprints:"
167
166
  );
168
167
 
169
- for (const match of entry.matches) {
168
+ for (const fingerprint of entry.fingerprints) {
170
169
  lines.push(
171
- ` - \`${match.path}\`${match.failureKey ? ` — \`${match.failureKey}\`` : ""}${
172
- match.errorIncludes ? ` — error contains \`${match.errorIncludes}\`` : ""
170
+ ` - \`${fingerprint.path}\`${fingerprint.failureKey ? ` — \`${fingerprint.failureKey}\`` : ""}${
171
+ fingerprint.errorIncludes ? ` — error contains \`${fingerprint.errorIncludes}\`` : ""
173
172
  }`
174
173
  );
175
174
  }
@@ -180,70 +179,68 @@ export function renderKnownFailuresMarkdown(document) {
180
179
  return `${lines.join("\n").trimEnd()}\n`;
181
180
  }
182
181
 
183
- export function findMatchingKnownFailureEntries(document, fileSummary) {
184
- return document.entries.filter((entry) => matchesKnownFailureEntry(entry, fileSummary));
182
+ export function findMatchingRegressionEntries(document, fileSummary) {
183
+ return document.entries.filter((entry) => matchesRegressionEntry(entry, fileSummary));
185
184
  }
186
185
 
187
- export function matchesKnownFailureEntry(entry, fileSummary) {
188
- return entry.matches.some((match) => matchesKnownFailureMatch(match, fileSummary));
186
+ export function matchesRegressionEntry(entry, fileSummary) {
187
+ return entry.fingerprints.some((fingerprint) =>
188
+ matchesRegressionFingerprint(fingerprint, fileSummary)
189
+ );
189
190
  }
190
191
 
191
- export function matchesKnownFailureMatch(match, fileSummary) {
192
- if (match.service && match.service !== fileSummary.service) return false;
193
- if (match.type && match.type !== fileSummary.type) return false;
194
- if (match.path !== fileSummary.path) return false;
195
- if (match.failureKey) {
192
+ export function matchesRegressionFingerprint(fingerprint, fileSummary) {
193
+ if (fingerprint.service && fingerprint.service !== fileSummary.service) return false;
194
+ if (fingerprint.type && fingerprint.type !== fileSummary.type) return false;
195
+ if (fingerprint.path !== fileSummary.path) return false;
196
+ if (fingerprint.failureKey) {
196
197
  const failureKeys = Array.isArray(fileSummary.failureDetails)
197
198
  ? fileSummary.failureDetails.flatMap((detail) => [detail?.key, detail?.title].filter(Boolean))
198
199
  : [];
199
- if (!failureKeys.includes(match.failureKey)) return false;
200
+ if (!failureKeys.includes(fingerprint.failureKey)) return false;
200
201
  }
201
- if (match.errorIncludes && !String(fileSummary.error || "").includes(match.errorIncludes)) {
202
+ if (fingerprint.errorIncludes && !String(fileSummary.error || "").includes(fingerprint.errorIncludes)) {
202
203
  return false;
203
204
  }
204
205
  return true;
205
206
  }
206
207
 
207
- export function buildKnownFailureFileIdentity(service, type, filePath) {
208
+ export function buildRegressionFileIdentity(service, type, filePath) {
208
209
  return `${service}::${type}::${filePath}`;
209
210
  }
210
211
 
211
- function normalizeKnownFailureEntry(entry, label) {
212
+ function normalizeRegressionEntry(entry, label) {
212
213
  if (!entry || typeof entry !== "object") {
213
214
  throw new Error(`${label} must be an object`);
214
215
  }
215
216
 
216
217
  const id = requireNonEmptyString(entry.id, `${label}.id`);
217
- const title = requireNonEmptyString(entry.title, `${label}.title`);
218
218
  const classification = requireEnumValue(
219
219
  entry.classification,
220
220
  CLASSIFICATIONS,
221
221
  `${label}.classification`
222
222
  );
223
- const state = requireEnumValue(entry.state, STATES, `${label}.state`);
224
- const description = requireNonEmptyString(entry.description, `${label}.description`);
225
- const whyFailing = requireNonEmptyString(entry.whyFailing, `${label}.whyFailing`);
223
+ const summary = requireNonEmptyString(entry.summary, `${label}.summary`);
224
+ const cause = requireNonEmptyString(entry.cause, `${label}.cause`);
226
225
  const lastReviewedAt = requireNonEmptyString(entry.lastReviewedAt, `${label}.lastReviewedAt`);
227
- if (!Array.isArray(entry.matches) || entry.matches.length === 0) {
228
- throw new Error(`${label}.matches must be a non-empty array`);
226
+ if (!Array.isArray(entry.fingerprints) || entry.fingerprints.length === 0) {
227
+ throw new Error(`${label}.fingerprints must be a non-empty array`);
229
228
  }
230
229
 
231
230
  return {
232
231
  id,
233
- title,
234
232
  classification,
235
- state,
236
- issue: normalizeKnownFailureIssue(entry.issue, `${label}.issue`),
237
- description,
238
- whyFailing,
233
+ issue: normalizeRegressionIssue(entry.issue, `${label}.issue`),
234
+ summary,
235
+ cause,
239
236
  lastReviewedAt,
240
- matches: entry.matches.map((match, index) =>
241
- normalizeKnownFailureMatch(match, `${label}.matches[${index}]`)
237
+ fingerprints: entry.fingerprints.map((fingerprint, index) =>
238
+ normalizeRegressionFingerprint(fingerprint, `${label}.fingerprints[${index}]`)
242
239
  ),
243
240
  };
244
241
  }
245
242
 
246
- function normalizeKnownFailureIssue(issue, label) {
243
+ function normalizeRegressionIssue(issue, label) {
247
244
  if (!issue || typeof issue !== "object") {
248
245
  throw new Error(`${label} must be an object`);
249
246
  }
@@ -253,31 +250,30 @@ function normalizeKnownFailureIssue(issue, label) {
253
250
  if (!Number.isInteger(number) || number <= 0) {
254
251
  throw new Error(`${label}.number must be a positive integer`);
255
252
  }
256
- const url = requireNonEmptyString(issue.url, `${label}.url`);
257
253
 
258
- return { repo, number, url };
254
+ return { repo, number };
259
255
  }
260
256
 
261
- function normalizeKnownFailureMatch(match, label) {
262
- if (!match || typeof match !== "object") {
257
+ function normalizeRegressionFingerprint(fingerprint, label) {
258
+ if (!fingerprint || typeof fingerprint !== "object") {
263
259
  throw new Error(`${label} must be an object`);
264
260
  }
265
261
 
266
- const pathValue = requireNonEmptyString(match.path, `${label}.path`);
262
+ const pathValue = requireNonEmptyString(fingerprint.path, `${label}.path`);
267
263
  const normalized = {
268
264
  path: pathValue,
269
265
  };
270
266
 
271
- const service = normalizeOptionalString(match.service);
267
+ const service = normalizeOptionalString(fingerprint.service);
272
268
  if (service) normalized.service = service;
273
269
 
274
- const type = normalizeOptionalString(match.type);
270
+ const type = normalizeOptionalString(fingerprint.type);
275
271
  if (type) normalized.type = type;
276
272
 
277
- const failureKey = normalizeOptionalString(match.failureKey);
273
+ const failureKey = normalizeOptionalString(fingerprint.failureKey);
278
274
  if (failureKey) normalized.failureKey = failureKey;
279
275
 
280
- const errorIncludes = normalizeOptionalString(match.errorIncludes);
276
+ const errorIncludes = normalizeOptionalString(fingerprint.errorIncludes);
281
277
  if (errorIncludes) normalized.errorIncludes = errorIncludes;
282
278
 
283
279
  return normalized;
@@ -321,9 +317,10 @@ function formatErrorMessage(error) {
321
317
  }
322
318
 
323
319
  export {
324
- buildKnownFailureIssueValidationSummaryLines,
325
- normalizeKnownFailureIssueValidationConfig,
320
+ buildRegressionSyncSummaryLines,
321
+ buildRegressionSyncSummaryParts,
322
+ normalizeRegressionSyncConfig,
326
323
  parseGitHubRepoSlug,
327
- shouldFailKnownFailureIssueValidation,
328
- validateKnownFailureIssues,
324
+ shouldFailRegressionSync,
325
+ validateRegressionIssues,
329
326
  } from "./github.mjs";