@yasserkhanorg/impact-gate 2.1.1 → 2.1.3

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.
@@ -10,7 +10,7 @@ const LEGACY_TO_CATEGORY = {
10
10
  'ux-issue': 'ux',
11
11
  };
12
12
  const CANONICAL_CATEGORIES = new Set([
13
- 'visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility',
13
+ 'visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility', 'links',
14
14
  ]);
15
15
  /**
16
16
  * Normalize any FindingType (legacy or canonical) to a canonical FindingCategory.
@@ -46,6 +46,11 @@ export const SEVERITY_DEFINITIONS = {
46
46
  // Category definitions
47
47
  // ---------------------------------------------------------------------------
48
48
  export const CATEGORY_DEFINITIONS = {
49
+ links: {
50
+ label: 'Links',
51
+ description: 'Broken links (404), wrong destinations, dead anchors, external links that fail.',
52
+ examples: ['404 on nav link', 'Link goes to wrong page', 'Anchor target missing', 'External link returns 500'],
53
+ },
49
54
  visual: {
50
55
  label: 'Visual/UI',
51
56
  description: 'Layout breaks, broken images, z-index issues, font/color inconsistencies, animation glitches, alignment issues, dark mode problems.',
@@ -109,13 +109,22 @@ export async function runQAAgent(inputConfig) {
109
109
  }
110
110
  }
111
111
  // -----------------------------------------------------------------------
112
+ // Compute remaining findings (exclude verified fixes)
113
+ // -----------------------------------------------------------------------
114
+ const verifiedIds = new Set((phase25?.fixes ?? [])
115
+ .filter((f) => f.status === 'verified')
116
+ .map((f) => f.findingId));
117
+ const remainingFindings = verifiedIds.size > 0
118
+ ? phase2.findings.filter((f) => !verifiedIds.has(f.id))
119
+ : phase2.findings;
120
+ // -----------------------------------------------------------------------
112
121
  // Regression comparison (optional)
113
122
  // -----------------------------------------------------------------------
114
123
  let regressionComparison;
115
124
  if (config.regression) {
116
125
  const baseline = loadBaseline(outputDir);
117
126
  if (baseline) {
118
- regressionComparison = compareBaselines(healthScore, phase2.findings, baseline);
127
+ regressionComparison = compareBaselines(healthScore, remainingFindings, baseline);
119
128
  logger.info('Regression comparison', {
120
129
  scoreDelta: regressionComparison.scoreDelta,
121
130
  fixedIssues: regressionComparison.fixedIssues.length,
@@ -126,8 +135,8 @@ export async function runQAAgent(inputConfig) {
126
135
  logger.info('No baseline found — saving current run as baseline');
127
136
  }
128
137
  }
129
- // Always save baseline for future comparisons
130
- saveBaseline(outputDir, healthScore, phase2.findings, config.baseUrl);
138
+ // Always save baseline for future comparisons (use remaining findings, not stale originals)
139
+ saveBaseline(outputDir, healthScore, remainingFindings, config.baseUrl);
131
140
  // -----------------------------------------------------------------------
132
141
  // Phase 3: Report + Spec Generation + Verdict
133
142
  // -----------------------------------------------------------------------
@@ -49,7 +49,8 @@ Pick dimensions that matter for THIS flow. Example: for "channel settings" → p
49
49
  ## Finding Categories
50
50
  When reporting findings, use the most specific category:
51
51
  - **visual** — Layout breaks, broken images, z-index issues, alignment, animation glitches, dark mode problems
52
- - **functional** — Broken links, dead buttons, form validation failures, incorrect redirects, race conditions, state not persisting
52
+ - **links** — Broken links (404), wrong destinations, dead anchors, external links that fail
53
+ - **functional** — Dead buttons, form validation failures, incorrect redirects, race conditions, state not persisting
53
54
  - **ux** — Confusing navigation, missing loading indicators, slow interactions (>500ms), unclear error messages, no confirmation before destructive actions
54
55
  - **content** — Typos, grammar errors, placeholder/lorem ipsum left in, truncated text, wrong labels
55
56
  - **performance** — Slow page loads (>3s), janky scrolling, layout shifts (CLS), excessive network requests
@@ -99,7 +99,7 @@ export const TOOL_DEFINITIONS = [
99
99
  input_schema: {
100
100
  type: 'object',
101
101
  properties: {
102
- type: { type: 'string', enum: ['visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility', 'bug', 'visual-regression', 'ux-issue', 'gap'] },
102
+ type: { type: 'string', enum: ['visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility', 'links', 'bug', 'visual-regression', 'ux-issue', 'gap'] },
103
103
  severity: { type: 'string', enum: ['critical', 'high', 'medium', 'low', 'info'] },
104
104
  summary: { type: 'string', description: 'What you found' },
105
105
  repro_steps: {
@@ -217,7 +217,7 @@ export function executeTool(ctx, name, input) {
217
217
  }
218
218
  case 'report_finding': {
219
219
  const VALID_TYPES = new Set([
220
- 'visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility',
220
+ 'visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility', 'links',
221
221
  'bug', 'visual-regression', 'ux-issue', 'gap',
222
222
  ]);
223
223
  const VALID_SEVERITIES = new Set(['critical', 'high', 'medium', 'low', 'info']);
@@ -47,7 +47,9 @@ ${evidence.consoleErrors?.length ? `- **Console errors:** ${evidence.consoleErro
47
47
  - Make the SMALLEST change that fixes the issue. Do NOT refactor surrounding code.
48
48
  - Only modify files directly related to the bug.
49
49
  - If you can't find the source after 3 search attempts, report that the fix is not possible.
50
- - If type checking fails after your fix, revert with git_revert.
50
+ - If type checking or lint fails BEFORE you commit, use git_restore to discard your edits, then report the fix is not possible.
51
+ - If you already committed and verification fails, use git_revert to undo the commit.
52
+ - NEVER leave uncommitted edits behind. Always either commit or restore.
51
53
  - The base URL is ${baseUrl}.
52
54
  - When done, respond with text only (no tool use) explaining the result.`;
53
55
  }
@@ -91,6 +93,7 @@ export async function runFixLoop(config, findings, browser, projectRoot) {
91
93
  baseUrl: config.baseUrl,
92
94
  screenshotDir,
93
95
  screenshotCounter: 100, // Start at 100 to avoid collisions with Phase 2 screenshots
96
+ qaCommitHashes: new Set(),
94
97
  };
95
98
  for (const finding of fixable) {
96
99
  if (wtf.shouldStop()) {
@@ -111,7 +114,10 @@ export async function runFixLoop(config, findings, browser, projectRoot) {
111
114
  costUSD += result.costUSD;
112
115
  wtf.recordAttempt(result.fix.status, result.fix.filesChanged?.length || 0);
113
116
  }
114
- const healthScoreAfter = computeHealthScore(findings);
117
+ // Exclude verified fixes from the post-fix score so it reflects actual remaining issues
118
+ const verifiedIds = new Set(fixes.filter((f) => f.status === 'verified').map((f) => f.findingId));
119
+ const remainingFindings = findings.filter((f) => !verifiedIds.has(f.id));
120
+ const healthScoreAfter = computeHealthScore(remainingFindings);
115
121
  return {
116
122
  fixes,
117
123
  fixesAttempted: fixes.filter((f) => f.status !== 'skipped').length,
@@ -134,6 +140,7 @@ async function fixSingleFinding(client, model, config, finding, toolCtx) {
134
140
  let filesChanged = [];
135
141
  let beforeScreenshot;
136
142
  let afterScreenshot;
143
+ let verifiedFixed = false;
137
144
  let status = 'skipped';
138
145
  // Take "before" screenshot
139
146
  try {
@@ -176,9 +183,10 @@ async function fixSingleFinding(client, model, config, finding, toolCtx) {
176
183
  // If no tool use, the agent is done
177
184
  const toolUseBlocks = assistantContent.filter((b) => b.type === 'tool_use');
178
185
  if (toolUseBlocks.length === 0) {
179
- // Determine status from what happened
186
+ // Determine status: 'verified' requires both a commit and explicit confirmation
187
+ // from verify_in_browser that the bug is gone. A screenshot alone is not proof.
180
188
  if (commitHash) {
181
- status = afterScreenshot ? 'verified' : 'best-effort';
189
+ status = verifiedFixed ? 'verified' : 'best-effort';
182
190
  }
183
191
  break;
184
192
  }
@@ -194,8 +202,13 @@ async function fixSingleFinding(client, model, config, finding, toolCtx) {
194
202
  if (result.filesChanged) {
195
203
  filesChanged = [...filesChanged, ...result.filesChanged];
196
204
  }
197
- if (result.screenshotPath && block.name === 'verify_in_browser') {
198
- afterScreenshot = result.screenshotPath;
205
+ if (block.name === 'verify_in_browser') {
206
+ if (result.screenshotPath) {
207
+ afterScreenshot = result.screenshotPath;
208
+ }
209
+ if (result.verifiedFixed === true) {
210
+ verifiedFixed = true;
211
+ }
199
212
  }
200
213
  if (block.name === 'git_revert') {
201
214
  status = 'reverted';
@@ -80,16 +80,26 @@ export const FIX_TOOL_DEFINITIONS = [
80
80
  required: [],
81
81
  },
82
82
  },
83
+ {
84
+ name: 'git_restore',
85
+ description: 'Discard all uncommitted changes in the working tree. Use this if validation fails BEFORE you have committed, to clean up your attempted edits.',
86
+ input_schema: {
87
+ type: 'object',
88
+ properties: {},
89
+ required: [],
90
+ },
91
+ },
83
92
  {
84
93
  name: 'verify_in_browser',
85
- description: 'Navigate to a URL and take a screenshot to verify a fix. Returns the screenshot path and any console errors.',
94
+ description: 'Navigate to a URL, take a screenshot, and report whether the fix resolved the issue. You MUST set fixed=true only if the original bug is no longer present. Set fixed=false if the bug still reproduces or if you cannot confirm.',
86
95
  input_schema: {
87
96
  type: 'object',
88
97
  properties: {
89
98
  url: { type: 'string', description: 'URL to navigate to for verification' },
90
99
  label: { type: 'string', description: 'Label for the screenshot (e.g. "after-fix-001")' },
100
+ fixed: { type: 'boolean', description: 'true if the original bug is gone, false if it still reproduces or uncertain' },
91
101
  },
92
- required: ['url', 'label'],
102
+ required: ['url', 'label', 'fixed'],
93
103
  },
94
104
  },
95
105
  ];
@@ -216,6 +226,7 @@ export function executeFixTool(ctx, name, input) {
216
226
  execFileSync('git', ['add', ...files], { cwd: ctx.projectRoot, encoding: 'utf-8' });
217
227
  execFileSync('git', ['commit', '-m', message], { cwd: ctx.projectRoot, encoding: 'utf-8' });
218
228
  const hash = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
229
+ ctx.qaCommitHashes.add(hash);
219
230
  return { output: `Committed: ${hash} — ${message}`, commitHash: hash, filesChanged: files };
220
231
  }
221
232
  catch (err) {
@@ -224,16 +235,32 @@ export function executeFixTool(ctx, name, input) {
224
235
  }
225
236
  }
226
237
  case 'git_revert': {
238
+ // Safety: only revert commits created by the fix loop
239
+ const currentHead = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
240
+ if (!ctx.qaCommitHashes.has(currentHead)) {
241
+ return { output: `Blocked: HEAD (${currentHead}) was not created by the fix loop. Refusing to revert a user commit.` };
242
+ }
227
243
  try {
228
244
  execFileSync('git', ['revert', '--no-edit', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' });
229
- const hash = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
230
- return { output: `Reverted HEAD. New HEAD: ${hash}`, commitHash: hash };
245
+ ctx.qaCommitHashes.delete(currentHead);
246
+ const newHash = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
247
+ return { output: `Reverted ${currentHead}. New HEAD: ${newHash}`, commitHash: newHash };
231
248
  }
232
249
  catch (err) {
233
250
  const error = err;
234
251
  return { output: `Git revert failed: ${error.stderr || String(err)}` };
235
252
  }
236
253
  }
254
+ case 'git_restore': {
255
+ try {
256
+ execFileSync('git', ['checkout', '--', '.'], { cwd: ctx.projectRoot, encoding: 'utf-8' });
257
+ return { output: 'Restored working tree to last commit state. All uncommitted edits discarded.' };
258
+ }
259
+ catch (err) {
260
+ const error = err;
261
+ return { output: `Git restore failed: ${error.stderr || String(err)}` };
262
+ }
263
+ }
237
264
  case 'verify_in_browser': {
238
265
  const url = String(input.url);
239
266
  const label = String(input.label || 'verify').replace(/[^a-zA-Z0-9_-]/g, '_');
@@ -254,7 +281,9 @@ export function executeFixTool(ctx, name, input) {
254
281
  catch {
255
282
  // Not available
256
283
  }
257
- return { output: `Screenshot saved: ${screenshotPath}${consoleErrors}`, screenshotPath };
284
+ const fixed = input.fixed === true;
285
+ const verdict = fixed ? 'Bug appears resolved.' : 'Bug still reproduces or unconfirmed.';
286
+ return { output: `Screenshot saved: ${screenshotPath}. ${verdict}${consoleErrors}`, screenshotPath, verifiedFixed: fixed };
258
287
  }
259
288
  default:
260
289
  return { output: `Unknown fix tool: ${name}` };
@@ -55,18 +55,25 @@ export function loadBaseline(outputDir) {
55
55
  /**
56
56
  * Compare current findings against a saved baseline.
57
57
  */
58
+ /**
59
+ * Build a fingerprint for a finding that includes flow, type, and summary
60
+ * to avoid collapsing distinct issues with the same generic summary.
61
+ */
62
+ function issueFingerprint(issue) {
63
+ return `${issue.flow}|${issue.type}|${issue.summary}`.toLowerCase();
64
+ }
58
65
  export function compareBaselines(currentScore, currentFindings, baseline) {
59
- const baselineIssueIds = new Set(baseline.issues.map((i) => i.summary.toLowerCase()));
60
- const currentIssueIds = new Set(currentFindings
66
+ const baselineFingerprints = new Set(baseline.issues.map((i) => issueFingerprint(i)));
67
+ const currentFingerprints = new Set(currentFindings
61
68
  .filter((f) => f.type !== 'verified-ok')
62
- .map((f) => f.summary.toLowerCase()));
69
+ .map((f) => issueFingerprint(f)));
63
70
  // Issues in baseline but not in current = fixed
64
71
  const fixedIssues = baseline.issues
65
- .filter((i) => !currentIssueIds.has(i.summary.toLowerCase()))
72
+ .filter((i) => !currentFingerprints.has(issueFingerprint(i)))
66
73
  .map((i) => `${i.id}: ${i.summary}`);
67
74
  // Issues in current but not in baseline = new
68
75
  const newIssues = currentFindings
69
- .filter((f) => f.type !== 'verified-ok' && !baselineIssueIds.has(f.summary.toLowerCase()))
76
+ .filter((f) => f.type !== 'verified-ok' && !baselineFingerprints.has(issueFingerprint(f)))
70
77
  .map((f) => `${f.id}: ${f.summary}`);
71
78
  // Category deltas
72
79
  const categoryDeltas = {};
@@ -1 +1 @@
1
- {"version":3,"file":"finding_taxonomy.d.ts","sourceRoot":"","sources":["../../src/qa-agent/finding_taxonomy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,mBAAmB,EAAC,MAAM,YAAY,CAAC;AAiBrH;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAKjE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,WAAW,GAAG,mBAAmB,CAK/E;AAMD,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAMhE,CAAC;AAMF,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,eAAe,EAAE;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAC,CAoClH,CAAC;AAYF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAKlE"}
1
+ {"version":3,"file":"finding_taxonomy.d.ts","sourceRoot":"","sources":["../../src/qa-agent/finding_taxonomy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,mBAAmB,EAAC,MAAM,YAAY,CAAC;AAiBrH;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAKjE;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,WAAW,GAAG,mBAAmB,CAK/E;AAMD,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAMhE,CAAC;AAMF,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,eAAe,EAAE;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAC,CAyClH,CAAC;AAYF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAKlE"}
@@ -16,7 +16,7 @@ const LEGACY_TO_CATEGORY = {
16
16
  'ux-issue': 'ux',
17
17
  };
18
18
  const CANONICAL_CATEGORIES = new Set([
19
- 'visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility',
19
+ 'visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility', 'links',
20
20
  ]);
21
21
  /**
22
22
  * Normalize any FindingType (legacy or canonical) to a canonical FindingCategory.
@@ -52,6 +52,11 @@ exports.SEVERITY_DEFINITIONS = {
52
52
  // Category definitions
53
53
  // ---------------------------------------------------------------------------
54
54
  exports.CATEGORY_DEFINITIONS = {
55
+ links: {
56
+ label: 'Links',
57
+ description: 'Broken links (404), wrong destinations, dead anchors, external links that fail.',
58
+ examples: ['404 on nav link', 'Link goes to wrong page', 'Anchor target missing', 'External link returns 500'],
59
+ },
55
60
  visual: {
56
61
  label: 'Visual/UI',
57
62
  description: 'Layout breaks, broken images, z-index issues, font/color inconsistencies, animation glitches, alignment issues, dark mode problems.',
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/qa-agent/orchestrator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAA0D,QAAQ,EAAE,QAAQ,EAAoC,MAAM,YAAY,CAAC;AAgB/I,wBAAsB,UAAU,CAAC,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqJzE"}
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/qa-agent/orchestrator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAA0D,QAAQ,EAAE,QAAQ,EAAoC,MAAM,YAAY,CAAC;AAgB/I,wBAAsB,UAAU,CAAC,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAiKzE"}
@@ -112,13 +112,22 @@ async function runQAAgent(inputConfig) {
112
112
  }
113
113
  }
114
114
  // -----------------------------------------------------------------------
115
+ // Compute remaining findings (exclude verified fixes)
116
+ // -----------------------------------------------------------------------
117
+ const verifiedIds = new Set((phase25?.fixes ?? [])
118
+ .filter((f) => f.status === 'verified')
119
+ .map((f) => f.findingId));
120
+ const remainingFindings = verifiedIds.size > 0
121
+ ? phase2.findings.filter((f) => !verifiedIds.has(f.id))
122
+ : phase2.findings;
123
+ // -----------------------------------------------------------------------
115
124
  // Regression comparison (optional)
116
125
  // -----------------------------------------------------------------------
117
126
  let regressionComparison;
118
127
  if (config.regression) {
119
128
  const baseline = (0, baseline_js_1.loadBaseline)(outputDir);
120
129
  if (baseline) {
121
- regressionComparison = (0, baseline_js_1.compareBaselines)(healthScore, phase2.findings, baseline);
130
+ regressionComparison = (0, baseline_js_1.compareBaselines)(healthScore, remainingFindings, baseline);
122
131
  logger_js_1.logger.info('Regression comparison', {
123
132
  scoreDelta: regressionComparison.scoreDelta,
124
133
  fixedIssues: regressionComparison.fixedIssues.length,
@@ -129,8 +138,8 @@ async function runQAAgent(inputConfig) {
129
138
  logger_js_1.logger.info('No baseline found — saving current run as baseline');
130
139
  }
131
140
  }
132
- // Always save baseline for future comparisons
133
- (0, baseline_js_1.saveBaseline)(outputDir, healthScore, phase2.findings, config.baseUrl);
141
+ // Always save baseline for future comparisons (use remaining findings, not stale originals)
142
+ (0, baseline_js_1.saveBaseline)(outputDir, healthScore, remainingFindings, config.baseUrl);
134
143
  // -----------------------------------------------------------------------
135
144
  // Phase 3: Report + Spec Generation + Verdict
136
145
  // -----------------------------------------------------------------------
@@ -1 +1 @@
1
- {"version":3,"file":"agent_loop.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase2/agent_loop.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAA2C,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAC,MAAM,aAAa,CAAC;AAuJ9G,wBAAsB,YAAY,CAC9B,MAAM,EAAE,QAAQ,EAChB,KAAK,EAAE,UAAU,EAAE,GACpB,OAAO,CAAC,YAAY,CAAC,CAyOvB"}
1
+ {"version":3,"file":"agent_loop.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase2/agent_loop.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAA2C,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAC,MAAM,aAAa,CAAC;AAwJ9G,wBAAsB,YAAY,CAC9B,MAAM,EAAE,QAAQ,EAChB,KAAK,EAAE,UAAU,EAAE,GACpB,OAAO,CAAC,YAAY,CAAC,CAyOvB"}
@@ -55,7 +55,8 @@ Pick dimensions that matter for THIS flow. Example: for "channel settings" → p
55
55
  ## Finding Categories
56
56
  When reporting findings, use the most specific category:
57
57
  - **visual** — Layout breaks, broken images, z-index issues, alignment, animation glitches, dark mode problems
58
- - **functional** — Broken links, dead buttons, form validation failures, incorrect redirects, race conditions, state not persisting
58
+ - **links** — Broken links (404), wrong destinations, dead anchors, external links that fail
59
+ - **functional** — Dead buttons, form validation failures, incorrect redirects, race conditions, state not persisting
59
60
  - **ux** — Confusing navigation, missing loading indicators, slow interactions (>500ms), unclear error messages, no confirmation before destructive actions
60
61
  - **content** — Typos, grammar errors, placeholder/lorem ipsum left in, truncated text, wrong labels
61
62
  - **performance** — Slow page loads (>3s), janky scrolling, layout shifts (CLS), excessive network requests
@@ -103,7 +103,7 @@ exports.TOOL_DEFINITIONS = [
103
103
  input_schema: {
104
104
  type: 'object',
105
105
  properties: {
106
- type: { type: 'string', enum: ['visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility', 'bug', 'visual-regression', 'ux-issue', 'gap'] },
106
+ type: { type: 'string', enum: ['visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility', 'links', 'bug', 'visual-regression', 'ux-issue', 'gap'] },
107
107
  severity: { type: 'string', enum: ['critical', 'high', 'medium', 'low', 'info'] },
108
108
  summary: { type: 'string', description: 'What you found' },
109
109
  repro_steps: {
@@ -221,7 +221,7 @@ function executeTool(ctx, name, input) {
221
221
  }
222
222
  case 'report_finding': {
223
223
  const VALID_TYPES = new Set([
224
- 'visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility',
224
+ 'visual', 'functional', 'ux', 'content', 'performance', 'console', 'accessibility', 'links',
225
225
  'bug', 'visual-regression', 'ux-issue', 'gap',
226
226
  ]);
227
227
  const VALID_SEVERITIES = new Set(['critical', 'high', 'medium', 'low', 'info']);
@@ -1 +1 @@
1
- {"version":3,"file":"fix_loop.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase25/fix_loop.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,OAAO,EAAqC,aAAa,EAAE,QAAQ,EAAC,MAAM,aAAa,CAAC;AACrG,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAuD7D,wBAAsB,UAAU,CAC5B,MAAM,EAAE,QAAQ,EAChB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,CAAC,CAwFxB"}
1
+ {"version":3,"file":"fix_loop.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase25/fix_loop.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,OAAO,EAAqC,aAAa,EAAE,QAAQ,EAAC,MAAM,aAAa,CAAC;AACrG,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAyD7D,wBAAsB,UAAU,CAC5B,MAAM,EAAE,QAAQ,EAChB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,aAAa,CAAC,CA4FxB"}
@@ -53,7 +53,9 @@ ${evidence.consoleErrors?.length ? `- **Console errors:** ${evidence.consoleErro
53
53
  - Make the SMALLEST change that fixes the issue. Do NOT refactor surrounding code.
54
54
  - Only modify files directly related to the bug.
55
55
  - If you can't find the source after 3 search attempts, report that the fix is not possible.
56
- - If type checking fails after your fix, revert with git_revert.
56
+ - If type checking or lint fails BEFORE you commit, use git_restore to discard your edits, then report the fix is not possible.
57
+ - If you already committed and verification fails, use git_revert to undo the commit.
58
+ - NEVER leave uncommitted edits behind. Always either commit or restore.
57
59
  - The base URL is ${baseUrl}.
58
60
  - When done, respond with text only (no tool use) explaining the result.`;
59
61
  }
@@ -97,6 +99,7 @@ async function runFixLoop(config, findings, browser, projectRoot) {
97
99
  baseUrl: config.baseUrl,
98
100
  screenshotDir,
99
101
  screenshotCounter: 100, // Start at 100 to avoid collisions with Phase 2 screenshots
102
+ qaCommitHashes: new Set(),
100
103
  };
101
104
  for (const finding of fixable) {
102
105
  if (wtf.shouldStop()) {
@@ -117,7 +120,10 @@ async function runFixLoop(config, findings, browser, projectRoot) {
117
120
  costUSD += result.costUSD;
118
121
  wtf.recordAttempt(result.fix.status, result.fix.filesChanged?.length || 0);
119
122
  }
120
- const healthScoreAfter = (0, health_score_js_1.computeHealthScore)(findings);
123
+ // Exclude verified fixes from the post-fix score so it reflects actual remaining issues
124
+ const verifiedIds = new Set(fixes.filter((f) => f.status === 'verified').map((f) => f.findingId));
125
+ const remainingFindings = findings.filter((f) => !verifiedIds.has(f.id));
126
+ const healthScoreAfter = (0, health_score_js_1.computeHealthScore)(remainingFindings);
121
127
  return {
122
128
  fixes,
123
129
  fixesAttempted: fixes.filter((f) => f.status !== 'skipped').length,
@@ -140,6 +146,7 @@ async function fixSingleFinding(client, model, config, finding, toolCtx) {
140
146
  let filesChanged = [];
141
147
  let beforeScreenshot;
142
148
  let afterScreenshot;
149
+ let verifiedFixed = false;
143
150
  let status = 'skipped';
144
151
  // Take "before" screenshot
145
152
  try {
@@ -182,9 +189,10 @@ async function fixSingleFinding(client, model, config, finding, toolCtx) {
182
189
  // If no tool use, the agent is done
183
190
  const toolUseBlocks = assistantContent.filter((b) => b.type === 'tool_use');
184
191
  if (toolUseBlocks.length === 0) {
185
- // Determine status from what happened
192
+ // Determine status: 'verified' requires both a commit and explicit confirmation
193
+ // from verify_in_browser that the bug is gone. A screenshot alone is not proof.
186
194
  if (commitHash) {
187
- status = afterScreenshot ? 'verified' : 'best-effort';
195
+ status = verifiedFixed ? 'verified' : 'best-effort';
188
196
  }
189
197
  break;
190
198
  }
@@ -200,8 +208,13 @@ async function fixSingleFinding(client, model, config, finding, toolCtx) {
200
208
  if (result.filesChanged) {
201
209
  filesChanged = [...filesChanged, ...result.filesChanged];
202
210
  }
203
- if (result.screenshotPath && block.name === 'verify_in_browser') {
204
- afterScreenshot = result.screenshotPath;
211
+ if (block.name === 'verify_in_browser') {
212
+ if (result.screenshotPath) {
213
+ afterScreenshot = result.screenshotPath;
214
+ }
215
+ if (result.verifiedFixed === true) {
216
+ verifiedFixed = true;
217
+ }
205
218
  }
206
219
  if (block.name === 'git_revert') {
207
220
  status = 'reverted';
@@ -7,12 +7,16 @@ export interface FixToolContext {
7
7
  baseUrl: string;
8
8
  screenshotDir: string;
9
9
  screenshotCounter: number;
10
+ /** Commit hashes created by the fix loop. Only these can be reverted. */
11
+ qaCommitHashes: Set<string>;
10
12
  }
11
13
  export interface FixToolResult {
12
14
  output: string;
13
15
  filesChanged?: string[];
14
16
  commitHash?: string;
15
17
  screenshotPath?: string;
18
+ /** Explicit verification signal from verify_in_browser */
19
+ verifiedFixed?: boolean;
16
20
  }
17
21
  export declare function executeFixTool(ctx: FixToolContext, name: string, input: Record<string, unknown>): FixToolResult;
18
22
  //# sourceMappingURL=fix_tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fix_tools.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase25/fix_tools.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAE/C,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAM7D,eAAO,MAAM,oBAAoB,EAAE,SAAS,CAAC,IAAI,EAsFhD,CAAC;AAMF,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AA2CD,wBAAgB,cAAc,CAC1B,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,aAAa,CA2If"}
1
+ {"version":3,"file":"fix_tools.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/phase25/fix_tools.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAE/C,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAM7D,eAAO,MAAM,oBAAoB,EAAE,SAAS,CAAC,IAAI,EAgGhD,CAAC;AAMF,MAAM,WAAW,cAAc;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AA2CD,wBAAgB,cAAc,CAC1B,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,aAAa,CA8Jf"}
@@ -84,16 +84,26 @@ exports.FIX_TOOL_DEFINITIONS = [
84
84
  required: [],
85
85
  },
86
86
  },
87
+ {
88
+ name: 'git_restore',
89
+ description: 'Discard all uncommitted changes in the working tree. Use this if validation fails BEFORE you have committed, to clean up your attempted edits.',
90
+ input_schema: {
91
+ type: 'object',
92
+ properties: {},
93
+ required: [],
94
+ },
95
+ },
87
96
  {
88
97
  name: 'verify_in_browser',
89
- description: 'Navigate to a URL and take a screenshot to verify a fix. Returns the screenshot path and any console errors.',
98
+ description: 'Navigate to a URL, take a screenshot, and report whether the fix resolved the issue. You MUST set fixed=true only if the original bug is no longer present. Set fixed=false if the bug still reproduces or if you cannot confirm.',
90
99
  input_schema: {
91
100
  type: 'object',
92
101
  properties: {
93
102
  url: { type: 'string', description: 'URL to navigate to for verification' },
94
103
  label: { type: 'string', description: 'Label for the screenshot (e.g. "after-fix-001")' },
104
+ fixed: { type: 'boolean', description: 'true if the original bug is gone, false if it still reproduces or uncertain' },
95
105
  },
96
- required: ['url', 'label'],
106
+ required: ['url', 'label', 'fixed'],
97
107
  },
98
108
  },
99
109
  ];
@@ -220,6 +230,7 @@ function executeFixTool(ctx, name, input) {
220
230
  (0, child_process_1.execFileSync)('git', ['add', ...files], { cwd: ctx.projectRoot, encoding: 'utf-8' });
221
231
  (0, child_process_1.execFileSync)('git', ['commit', '-m', message], { cwd: ctx.projectRoot, encoding: 'utf-8' });
222
232
  const hash = (0, child_process_1.execFileSync)('git', ['rev-parse', '--short', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
233
+ ctx.qaCommitHashes.add(hash);
223
234
  return { output: `Committed: ${hash} — ${message}`, commitHash: hash, filesChanged: files };
224
235
  }
225
236
  catch (err) {
@@ -228,16 +239,32 @@ function executeFixTool(ctx, name, input) {
228
239
  }
229
240
  }
230
241
  case 'git_revert': {
242
+ // Safety: only revert commits created by the fix loop
243
+ const currentHead = (0, child_process_1.execFileSync)('git', ['rev-parse', '--short', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
244
+ if (!ctx.qaCommitHashes.has(currentHead)) {
245
+ return { output: `Blocked: HEAD (${currentHead}) was not created by the fix loop. Refusing to revert a user commit.` };
246
+ }
231
247
  try {
232
248
  (0, child_process_1.execFileSync)('git', ['revert', '--no-edit', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' });
233
- const hash = (0, child_process_1.execFileSync)('git', ['rev-parse', '--short', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
234
- return { output: `Reverted HEAD. New HEAD: ${hash}`, commitHash: hash };
249
+ ctx.qaCommitHashes.delete(currentHead);
250
+ const newHash = (0, child_process_1.execFileSync)('git', ['rev-parse', '--short', 'HEAD'], { cwd: ctx.projectRoot, encoding: 'utf-8' }).trim();
251
+ return { output: `Reverted ${currentHead}. New HEAD: ${newHash}`, commitHash: newHash };
235
252
  }
236
253
  catch (err) {
237
254
  const error = err;
238
255
  return { output: `Git revert failed: ${error.stderr || String(err)}` };
239
256
  }
240
257
  }
258
+ case 'git_restore': {
259
+ try {
260
+ (0, child_process_1.execFileSync)('git', ['checkout', '--', '.'], { cwd: ctx.projectRoot, encoding: 'utf-8' });
261
+ return { output: 'Restored working tree to last commit state. All uncommitted edits discarded.' };
262
+ }
263
+ catch (err) {
264
+ const error = err;
265
+ return { output: `Git restore failed: ${error.stderr || String(err)}` };
266
+ }
267
+ }
241
268
  case 'verify_in_browser': {
242
269
  const url = String(input.url);
243
270
  const label = String(input.label || 'verify').replace(/[^a-zA-Z0-9_-]/g, '_');
@@ -258,7 +285,9 @@ function executeFixTool(ctx, name, input) {
258
285
  catch {
259
286
  // Not available
260
287
  }
261
- return { output: `Screenshot saved: ${screenshotPath}${consoleErrors}`, screenshotPath };
288
+ const fixed = input.fixed === true;
289
+ const verdict = fixed ? 'Bug appears resolved.' : 'Bug still reproduces or unconfirmed.';
290
+ return { output: `Screenshot saved: ${screenshotPath}. ${verdict}${consoleErrors}`, screenshotPath, verifiedFixed: fixed };
262
291
  }
263
292
  default:
264
293
  return { output: `Unknown fix tool: ${name}` };
@@ -7,8 +7,5 @@ export declare function saveBaseline(outputDir: string, healthScore: HealthScore
7
7
  * Load a previously saved baseline. Returns null if no baseline exists.
8
8
  */
9
9
  export declare function loadBaseline(outputDir: string): RegressionBaseline | null;
10
- /**
11
- * Compare current findings against a saved baseline.
12
- */
13
10
  export declare function compareBaselines(currentScore: HealthScore, currentFindings: Finding[], baseline: RegressionBaseline): RegressionComparison;
14
11
  //# sourceMappingURL=baseline.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/regression/baseline.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,oBAAoB,EAAsB,MAAM,aAAa,CAAC;AAKrH;;GAEG;AACH,wBAAgB,YAAY,CACxB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,OAAO,EAAE,EACnB,GAAG,EAAE,MAAM,GACZ,IAAI,CA2BN;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAazE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC5B,YAAY,EAAE,WAAW,EACzB,eAAe,EAAE,OAAO,EAAE,EAC1B,QAAQ,EAAE,kBAAkB,GAC7B,oBAAoB,CAqCtB"}
1
+ {"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../../../src/qa-agent/regression/baseline.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,oBAAoB,EAAsB,MAAM,aAAa,CAAC;AAKrH;;GAEG;AACH,wBAAgB,YAAY,CACxB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,OAAO,EAAE,EACnB,GAAG,EAAE,MAAM,GACZ,IAAI,CA2BN;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAazE;AAaD,wBAAgB,gBAAgB,CAC5B,YAAY,EAAE,WAAW,EACzB,eAAe,EAAE,OAAO,EAAE,EAC1B,QAAQ,EAAE,kBAAkB,GAC7B,oBAAoB,CAqCtB"}
@@ -60,18 +60,25 @@ function loadBaseline(outputDir) {
60
60
  /**
61
61
  * Compare current findings against a saved baseline.
62
62
  */
63
+ /**
64
+ * Build a fingerprint for a finding that includes flow, type, and summary
65
+ * to avoid collapsing distinct issues with the same generic summary.
66
+ */
67
+ function issueFingerprint(issue) {
68
+ return `${issue.flow}|${issue.type}|${issue.summary}`.toLowerCase();
69
+ }
63
70
  function compareBaselines(currentScore, currentFindings, baseline) {
64
- const baselineIssueIds = new Set(baseline.issues.map((i) => i.summary.toLowerCase()));
65
- const currentIssueIds = new Set(currentFindings
71
+ const baselineFingerprints = new Set(baseline.issues.map((i) => issueFingerprint(i)));
72
+ const currentFingerprints = new Set(currentFindings
66
73
  .filter((f) => f.type !== 'verified-ok')
67
- .map((f) => f.summary.toLowerCase()));
74
+ .map((f) => issueFingerprint(f)));
68
75
  // Issues in baseline but not in current = fixed
69
76
  const fixedIssues = baseline.issues
70
- .filter((i) => !currentIssueIds.has(i.summary.toLowerCase()))
77
+ .filter((i) => !currentFingerprints.has(issueFingerprint(i)))
71
78
  .map((i) => `${i.id}: ${i.summary}`);
72
79
  // Issues in current but not in baseline = new
73
80
  const newIssues = currentFindings
74
- .filter((f) => f.type !== 'verified-ok' && !baselineIssueIds.has(f.summary.toLowerCase()))
81
+ .filter((f) => f.type !== 'verified-ok' && !baselineFingerprints.has(issueFingerprint(f)))
75
82
  .map((f) => `${f.id}: ${f.summary}`);
76
83
  // Category deltas
77
84
  const categoryDeltas = {};
@@ -31,7 +31,7 @@ export interface BrowserAction {
31
31
  timestamp: number;
32
32
  }
33
33
  /** Canonical finding categories (v1.1) */
34
- export type FindingCategory = 'visual' | 'functional' | 'ux' | 'content' | 'performance' | 'console' | 'accessibility';
34
+ export type FindingCategory = 'visual' | 'functional' | 'ux' | 'content' | 'performance' | 'console' | 'accessibility' | 'links';
35
35
  /** Legacy finding types kept for backward compatibility */
36
36
  export type LegacyFindingType = 'bug' | 'visual-regression' | 'ux-issue' | 'gap' | 'verified-ok';
37
37
  /** Accepts both canonical categories and legacy type names */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/qa-agent/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/qa-agent/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAMpD,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAMxD,MAAM,WAAW,QAAQ;IACrB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAMD,MAAM,MAAM,iBAAiB,GACvB,UAAU,GACV,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,GACP,WAAW,GACX,QAAQ,GACR,MAAM,GACN,SAAS,GACT,YAAY,GACZ,iBAAiB,GACjB,UAAU,GACV,SAAS,GACT,WAAW,GACX,UAAU,GACV,MAAM,GACN,gBAAgB,GAChB,gBAAgB,GAChB,aAAa,GACb,UAAU,GACV,YAAY,CAAC;AAEnB,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACrB;AAMD,0CAA0C;AAC1C,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,GAAG,eAAe,GAAG,OAAO,CAAC;AAEjI,2DAA2D;AAC3D,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,mBAAmB,GAAG,UAAU,GAAG,KAAK,GAAG,aAAa,CAAC;AAEjG,8DAA8D;AAC9D,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,iBAAiB,CAAC;AAE9D,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;AAM9E,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,GAAG,IAAI,GAAG,aAAa,GAAG,SAAS,GAAG,eAAe,CAAC;AAErI,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACtB;AAMD,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,YAAY,CAAC;AAC1D,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,aAAa,GAAG,UAAU,GAAG,SAAS,CAAC;AAE5E,MAAM,WAAW,SAAS;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,WAAW,CAAC;IAC/B,gBAAgB,EAAE,WAAW,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACnB;AAMD,MAAM,WAAW,kBAAkB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC,EAAE,CAAC;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7D,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,OAAO;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B;AAMD,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC7B,cAAc,EAAE,UAAU,EAAE,CAAC;IAC7B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,6GAA6G;IAC7G,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,aAAa,EAAE,aAAa,EAAE,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACrB;AAMD,MAAM,WAAW,UAAU;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IACzB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC5B;AAMD,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,OAAO,GAAG,aAAa,CAAC;AAE7D,MAAM,WAAW,WAAW;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC3C,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,eAAe,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC7B;AAMD,MAAM,WAAW,QAAQ;IACrB,aAAa,EAAE,OAAO,GAAG,OAAO,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,gBAAgB,EAAE,MAAM,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,cAAc,CAAC;IACxB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC/C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yasserkhanorg/impact-gate",
3
- "version": "2.1.1",
3
+ "version": "2.1.3",
4
4
  "description": "Diff-aware E2E impact analysis and coverage gating for Playwright/Cypress teams. Optional AI features can suggest, generate, and heal tests once your project has a route-families.json manifest.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.js",