@rigour-labs/core 6.12.2-rc.5 → 6.12.2

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,5 +1,7 @@
1
1
  import { Gate } from './base.js';
2
2
  import { Failure, Config, Report, DeepOptions } from '../types/index.js';
3
+ /** The gate a finding came from, for a finding this runner produced; else undefined. */
4
+ export declare function gateOf(failure: Failure): string | undefined;
3
5
  export declare class GateRunner {
4
6
  private config;
5
7
  private gates;
@@ -36,6 +36,12 @@ import { execa } from 'execa';
36
36
  import { Logger } from '../utils/logger.js';
37
37
  import { FileSystemCache } from '../services/filesystem-cache.js';
38
38
  import { onChangedLines, settledChecks } from '../review/settled-checks.js';
39
+ /** The gate each finding of a run came from (a gate may give its findings other ids, such as `AST_COMPLEXITY`). */
40
+ const GATE_OF = new WeakMap();
41
+ /** The gate a finding came from, for a finding this runner produced; else undefined. */
42
+ export function gateOf(failure) {
43
+ return GATE_OF.get(failure);
44
+ }
39
45
  export class GateRunner {
40
46
  config;
41
47
  gates = [];
@@ -166,6 +172,8 @@ export class GateRunner {
166
172
  let skipped;
167
173
  const gateFailures = await gate.run({ cwd, record, ignore, patterns, fileCache, skip: reason => { skipped = reason; } });
168
174
  if (gateFailures.length > 0) {
175
+ for (const f of gateFailures)
176
+ GATE_OF.set(f, gate.id);
169
177
  failures.push(...gateFailures);
170
178
  summary[gate.id] = 'FAIL';
171
179
  }
@@ -38,6 +38,8 @@ export interface ReviewResult {
38
38
  excludedOutsideChangedLines: number;
39
39
  /** Findings the base already had: counted, not reported (baseline.ts). */
40
40
  preexisting: number;
41
+ /** The same, by check. A check whose findings were all the base's reads PASS in the report's summary. */
42
+ preexistingByCheck: Record<string, number>;
41
43
  /** A diff given with no base whose change HEAD already holds (committed work): nothing was compared, so nothing was dropped as the base's. */
42
44
  baseUnknown?: boolean;
43
45
  changedLines: Record<string, Set<number>>;
@@ -8,7 +8,7 @@
8
8
  * and so does a proven gate that crashed (a heuristic one is only listed).
9
9
  */
10
10
  import { spawnSync } from 'child_process';
11
- import { GateRunner } from '../gates/runner.js';
11
+ import { GateRunner, gateOf } from '../gates/runner.js';
12
12
  import { changedLinesByFile, parseDiff, removedByFile } from '../utils/diff.js';
13
13
  import { normalizeScopePatterns } from '../utils/scope.js';
14
14
  import { deepAnalysisError } from '../utils/deep-status.js';
@@ -39,14 +39,14 @@ export async function reviewChange(input) {
39
39
  const changedLines = withoutGenerated(input.cwd, parseDiff(diff));
40
40
  const targets = input.files?.length ? input.files : Object.keys(changedLines);
41
41
  if (targets.length === 0) {
42
- return { status: 'PASS', findings: [], fileFindings: [], contextFindings: [], advisory: [], muted: 0, dismissed: 0, dismissedByGate: {}, unlocated: 0, excludedOutsideChangedLines: 0, preexisting: 0, changedLines, report: null, gateErrors: [], controlFilesChanged: controlFiles(diff), hints: [], covered: [] };
42
+ return { status: 'PASS', findings: [], fileFindings: [], contextFindings: [], advisory: [], muted: 0, dismissed: 0, dismissedByGate: {}, unlocated: 0, excludedOutsideChangedLines: 0, preexisting: 0, preexistingByCheck: {}, changedLines, report: null, gateErrors: [], controlFilesChanged: controlFiles(diff), hints: [], covered: [] };
43
43
  }
44
44
  // The team's compiled checks run before the deep review, so it is told what they found and which lessons they covered.
45
45
  const compiled = compiledChecksOn(input.cwd, changedLines, input.config);
46
46
  const deep = input.deep ? { ...input.deep, focusLines: changedLinesByFile(changedLines), removedLines: removedByFile(diff), diff, settled: settledChecks(compiled.failures), covered: compiled.covered } : undefined;
47
47
  // The team's `commands:` run at push (toolchain.ts), where a failure blocks; here they would only cost time.
48
48
  const report = await new GateRunner({ ...input.config, commands: {} }).run(input.cwd, await normalizeScopePatterns(input.cwd, targets), deep);
49
- const { preexisting, baseUnknown } = await dropPreexisting(input, report, targets);
49
+ const { preexisting, byCheck: preexistingByCheck = {}, baseUnknown } = await dropPreexisting(input, report, targets);
50
50
  if (input.diffTests && deep)
51
51
  report.failures.push(...await diffTestFailures(input.cwd, input.source, deep));
52
52
  // The review's own checks, each recorded in the summary beside the gates, so a report says everything that ran.
@@ -96,6 +96,7 @@ export async function reviewChange(input) {
96
96
  unlocated: split.unlocated,
97
97
  excludedOutsideChangedLines: split.outside,
98
98
  preexisting,
99
+ preexistingByCheck,
99
100
  ...(baseUnknown ? { baseUnknown } : {}),
100
101
  changedLines,
101
102
  report,
@@ -108,6 +109,21 @@ export async function reviewChange(input) {
108
109
  ...(checkedGoal ? { goal: checkedGoal } : {}),
109
110
  };
110
111
  }
112
+ /**
113
+ * A check that failed only on what the base already had passes for this change: its summary reads PASS, and the
114
+ * count, by check, says what it left out. A check with a finding the change introduced keeps FAIL.
115
+ */
116
+ function passOnlyPreexisting(report, preexisting) {
117
+ const byCheck = {};
118
+ const check = (f) => gateOf(f) ?? f.id;
119
+ for (const f of preexisting)
120
+ byCheck[check(f)] = (byCheck[check(f)] ?? 0) + 1;
121
+ const remaining = new Set(report.failures.map(check));
122
+ for (const id of Object.keys(byCheck))
123
+ if (report.summary[id] === 'FAIL' && !remaining.has(id))
124
+ report.summary[id] = 'PASS';
125
+ return byCheck;
126
+ }
111
127
  /**
112
128
  * Drop the rules' findings the base already had; returns how many. Model findings stay: the model reviews only the change.
113
129
  * A diff given with no `--base` is compared with HEAD, which is right for uncommitted work. When HEAD already holds the
@@ -127,7 +143,7 @@ async function dropPreexisting(input, report, targets) {
127
143
  const { preexisting } = splitIntroduced(rules, await baseFindings(input.cwd, input.config, commit, targets));
128
144
  const old = new Set(preexisting);
129
145
  report.failures = report.failures.filter(f => !old.has(f));
130
- return { preexisting: old.size };
146
+ return { preexisting: old.size, byCheck: passOnlyPreexisting(report, preexisting) };
131
147
  }
132
148
  catch {
133
149
  return { preexisting: 0 }; // the comparison is a courtesy; it never fails a review
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigour-labs/core",
3
- "version": "6.12.2-rc.5",
3
+ "version": "6.12.2",
4
4
  "description": "Rigour's review engine: deterministic gates on changed lines, rules and lessons learned from your team's fixes, and per-check precision from what you fix versus dismiss, across TypeScript, JavaScript, Python, Go, Ruby and C#.",
5
5
  "engines": {
6
6
  "node": ">=22.13"
@@ -75,11 +75,11 @@
75
75
  "@anthropic-ai/sdk": "^0.132.1",
76
76
  "pg": "^8.16.3",
77
77
  "openai": "^5.23.2",
78
- "@rigour-labs/brain-darwin-x64": "6.12.2-rc.5",
79
- "@rigour-labs/brain-linux-arm64": "6.12.2-rc.5",
80
- "@rigour-labs/brain-win-x64": "6.12.2-rc.5",
81
- "@rigour-labs/brain-linux-x64": "6.12.2-rc.5",
82
- "@rigour-labs/brain-darwin-arm64": "6.12.2-rc.5"
78
+ "@rigour-labs/brain-darwin-arm64": "6.12.2",
79
+ "@rigour-labs/brain-linux-arm64": "6.12.2",
80
+ "@rigour-labs/brain-darwin-x64": "6.12.2",
81
+ "@rigour-labs/brain-win-x64": "6.12.2",
82
+ "@rigour-labs/brain-linux-x64": "6.12.2"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@types/fs-extra": "^11.0.4",