@rigour-labs/core 6.12.1 → 6.12.2-rc.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.
package/dist/index.d.ts CHANGED
@@ -49,7 +49,7 @@ export { decideCompiledCheck, proposeCompiledChecks, readCompiledChecks, suspens
49
49
  export { ruleWriterFor } from './review/reviewer/rule-writer.js';
50
50
  export { backtestLast, formatLast, scoreLast, LAST_LEDGER_PATH, type LastReport } from './review/backtest-last.js';
51
51
  export { buildRecord, recordLines, recordIntact, type ReviewRecord } from './review/reviewer/record.js';
52
- export { readLessons, writeLessons, decideLesson, acceptSuggestedText, scopeLesson, lessonState, pendingDecision, matchLessons, lessonText, lessonsPath, type ReviewLesson, type LessonEvidence } from './review-learning/lessons.js';
52
+ export { readLessons, writeLessons, decideLesson, acceptSuggestedText, scopeLesson, lessonState, pendingDecision, quietBotCandidate, matchLessons, lessonText, lessonsPath, type ReviewLesson, type LessonEvidence } from './review-learning/lessons.js';
53
53
  export { lessonsForDiff, lessonsSection, type LessonMode } from './review-learning/team-lessons.js';
54
54
  export { recordAgentWrites, captureHumanEdits } from './review-learning/human-edits.js';
55
55
  export { readRepoRules, rulesForDiff, rulesSection, splitRules, type RepoRule } from './review-learning/repo-rules.js';
package/dist/index.js CHANGED
@@ -47,7 +47,7 @@ export { decideCompiledCheck, proposeCompiledChecks, readCompiledChecks, suspens
47
47
  export { ruleWriterFor } from './review/reviewer/rule-writer.js';
48
48
  export { backtestLast, formatLast, scoreLast, LAST_LEDGER_PATH } from './review/backtest-last.js';
49
49
  export { buildRecord, recordLines, recordIntact } from './review/reviewer/record.js';
50
- export { readLessons, writeLessons, decideLesson, acceptSuggestedText, scopeLesson, lessonState, pendingDecision, matchLessons, lessonText, lessonsPath } from './review-learning/lessons.js';
50
+ export { readLessons, writeLessons, decideLesson, acceptSuggestedText, scopeLesson, lessonState, pendingDecision, quietBotCandidate, matchLessons, lessonText, lessonsPath } from './review-learning/lessons.js';
51
51
  export { lessonsForDiff, lessonsSection } from './review-learning/team-lessons.js';
52
52
  export { recordAgentWrites, captureHumanEdits } from './review-learning/human-edits.js';
53
53
  export { readRepoRules, rulesForDiff, rulesSection, splitRules } from './review-learning/repo-rules.js';
@@ -77,6 +77,8 @@ export interface ReviewLesson {
77
77
  * evidence, never a promotion. A record from before evidence kinds keeps the state it had.
78
78
  */
79
79
  export declare function lessonState(lesson: ReviewLesson): Pick<ReviewLesson, 'state' | 'promotedBy'>;
80
+ /** Every point on the lesson came from a review bot: it is recorded, but never verified or served until a person decides. */
81
+ export declare function raisedOnlyByBots(lesson: ReviewLesson): boolean;
80
82
  /**
81
83
  * The point of a review comment: its bold title, else its first sentence and, when that only says what is
82
84
  * wrong, the first later sentence that says what to do. Without tool output or markup.
@@ -135,6 +137,11 @@ export declare function readLessons(cwd: string): ReviewLesson[];
135
137
  * (`lines`); undefined when nothing waits on a person. Studio and the outcome numbers read it, so they never disagree.
136
138
  */
137
139
  export declare function pendingDecision(lesson: ReviewLesson): LessonEvidence | undefined;
140
+ /**
141
+ * A candidate only review bots raised, with nothing waiting on a person: hidden from the default lists (`--list`,
142
+ * Studio) and counted instead. One taken back or reclassified stays in view, so its reason is seen.
143
+ */
144
+ export declare function quietBotCandidate(lesson: ReviewLesson): boolean;
138
145
  export declare function writeLessons(cwd: string, lessons: ReviewLesson[]): void;
139
146
  /** A person's decision on a lesson, kept as evidence: accepted makes it a lesson, rejected an anti-lesson. Undefined for an unknown id. */
140
147
  export declare function decideLesson(cwd: string, id: string, decision: 'accepted' | 'rejected' | 'dismissed', by: string, why?: string): ReviewLesson | undefined;
@@ -62,6 +62,9 @@ export function lessonState(lesson) {
62
62
  // real history, it was most often unrelated work. It is shown in Studio for a person to promote.
63
63
  if (kinds.has('counter'))
64
64
  return { state: 'candidate' };
65
+ // A lesson is never verified without a person: review bots agreeing with each other are not a team's standard.
66
+ if (raisedOnlyByBots(lesson))
67
+ return { state: 'candidate' };
65
68
  const points = lesson.evidence.filter(e => (e.kind ?? 'point') === 'point');
66
69
  const prs = new Set(points.map(e => e.pr)).size;
67
70
  const authors = new Set(points.map(e => e.prAuthor).filter(Boolean)).size;
@@ -71,6 +74,11 @@ export function lessonState(lesson) {
71
74
  const independent = reviewers >= 2 || wordings >= 2;
72
75
  return prs >= RECUR_PRS && authors >= RECUR_AUTHORS && independent ? { state: 'verified', promotedBy: 'recurrence' } : { state: 'candidate' };
73
76
  }
77
+ /** Every point on the lesson came from a review bot: it is recorded, but never verified or served until a person decides. */
78
+ export function raisedOnlyByBots(lesson) {
79
+ const points = lesson.evidence.filter(e => (e.kind ?? 'point') === 'point');
80
+ return points.length > 0 && points.every(e => e.source === 'bot');
81
+ }
74
82
  /** A sentence that tells the author what to do, not only what is wrong. */
75
83
  const INSTRUCTION = /^(?:please\s+)?(?:use|pick|avoid|don'?t|do not|never|always|make|keep|move|read|filter|check|change|drop|add|remove|pass|split|prefer|replace|rename|derive|select|return|validate|extract|bound|lock)\b|\b(?:should|must|instead)\b/i;
76
84
  /**
@@ -235,7 +243,7 @@ export function matchLessons(lessons, change, options = {}) {
235
243
  // A lesson whose only evidence is the pull request under review is already in front of the judge as the reviewer's own points.
236
244
  if (options.excludePr !== undefined)
237
245
  lessons = lessons.filter(l => !l.evidence.length || l.evidence.some(e => e.pr !== options.excludePr));
238
- const inPlay = (l) => l.state === 'verified' || (!!options.includeCandidates && l.state === 'candidate');
246
+ const inPlay = (l) => l.state === 'verified' || (!!options.includeCandidates && l.state === 'candidate' && !raisedOnlyByBots(l));
239
247
  // A person made these the repository's standards: every change gets them, whatever its files or words; most-raised first.
240
248
  const repo = lessons
241
249
  .filter(l => l.scope === 'repo' && inPlay(l))
@@ -327,22 +335,33 @@ export function pendingDecision(lesson) {
327
335
  const last = lesson.evidence.filter(e => e.kind === 'demoted' || e.kind === 'lines' || e.kind === 'reclassified' || e.kind === 'accepted' || e.kind === 'rejected' || e.kind === 'dismissed').at(-1);
328
336
  return last?.kind === 'demoted' || last?.kind === 'lines' || last?.kind === 'reclassified' ? last : undefined;
329
337
  }
338
+ /**
339
+ * A candidate only review bots raised, with nothing waiting on a person: hidden from the default lists (`--list`,
340
+ * Studio) and counted instead. One taken back or reclassified stays in view, so its reason is seen.
341
+ */
342
+ export function quietBotCandidate(lesson) {
343
+ return lesson.state === 'candidate' && raisedOnlyByBots(lesson) && !pendingDecision(lesson);
344
+ }
330
345
  /** Why a lesson an outcome alone had promoted is a candidate again. */
331
346
  const RECLASSIFIED = 'promoted by the exact-line rule, which no longer promotes on its own';
347
+ /** Why a lesson recurrence had promoted on review bots' points alone is a candidate again. */
348
+ const BOTS_ONLY = 'only review bots raised it (no person)';
332
349
  /**
333
- * A lesson stored as verified by an outcome (a later fix on its lines, or a revert), as every reader sees it now that
334
- * outcomes no longer promote: its state worked out again from its evidence, and, when that leaves it a candidate, one
335
- * `reclassified` record saying why. Never deleted, nothing else changed; the next write keeps it, and a lesson that
336
- * already has the record is left as it is. Recurrence, a correction or a person's decision keeps a lesson verified.
350
+ * A lesson stored as verified by a rule that no longer promotes on its own, as every reader sees it now: an outcome
351
+ * (a later fix on its lines, or a revert), or recurrence on review bots' points alone. Its state is worked out again
352
+ * from its evidence, and, when that leaves it a candidate, one `reclassified` record says why. Never deleted, nothing
353
+ * else changed; the next write keeps it, and a lesson that already has the record is left as it is. Recurrence with a
354
+ * person's point, a correction or a person's decision keeps a lesson verified.
337
355
  */
338
356
  function reclassified(lesson) {
339
- if (lesson.state !== 'verified' || lesson.promotedBy !== 'outcome' || lesson.evidence.some(e => e.kind === 'reclassified'))
357
+ const byBots = lesson.promotedBy === 'recurrence' && raisedOnlyByBots(lesson);
358
+ if (lesson.state !== 'verified' || !(lesson.promotedBy === 'outcome' || byBots) || lesson.evidence.some(e => e.kind === 'reclassified'))
340
359
  return lesson;
341
360
  const next = lessonState(lesson);
342
361
  if (next.state === 'verified')
343
362
  return { ...lesson, ...next };
344
363
  const at = new Date().toISOString();
345
- return { ...lesson, state: 'candidate', promotedBy: undefined, evidence: [...lesson.evidence, { kind: 'reclassified', pr: lesson.evidence[0]?.pr ?? 0, comment: `reclassified-${lesson.id}`, author: '', detail: RECLASSIFIED, at }] };
364
+ return { ...lesson, state: 'candidate', promotedBy: undefined, evidence: [...lesson.evidence, { kind: 'reclassified', pr: lesson.evidence[0]?.pr ?? 0, comment: `reclassified-${lesson.id}`, author: '', detail: byBots ? BOTS_ONLY : RECLASSIFIED, at }] };
346
365
  }
347
366
  export function writeLessons(cwd, lessons) {
348
367
  const file = lessonsPath(cwd);
@@ -5,14 +5,14 @@
5
5
  */
6
6
  import path from 'path';
7
7
  import { parseDiff } from '../utils/diff.js';
8
- import { matchLessons, readLessons } from './lessons.js';
8
+ import { matchLessons, raisedOnlyByBots, readLessons } from './lessons.js';
9
9
  /** Lessons a person or repeated evidence verified reach the reviewer unless a team turns them off. */
10
10
  export const DEFAULT_LESSON_MODE = 'verified';
11
11
  /** The team's review lessons in play for this mode: none when off, verified ones by default. */
12
12
  export function activeLessons(cwd, mode = DEFAULT_LESSON_MODE) {
13
13
  if (mode === 'off')
14
14
  return [];
15
- return readLessons(cwd).filter(l => mode === 'all' || l.state === 'verified');
15
+ return readLessons(cwd).filter(l => mode === 'all' ? !(l.state === 'candidate' && raisedOnlyByBots(l)) : l.state === 'verified');
16
16
  }
17
17
  /** `standards`, `limit`, `perFile`: how many team standards and file lessons may come, and how many per file (a judge reading a whole pull request takes more than an agent's one question). */
18
18
  export function lessonsForDiff(cwd, diff, mode = DEFAULT_LESSON_MODE, standards, limit, perFile, excludePr) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigour-labs/core",
3
- "version": "6.12.1",
3
+ "version": "6.12.2-rc.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.1",
79
- "@rigour-labs/brain-linux-x64": "6.12.1",
80
- "@rigour-labs/brain-linux-arm64": "6.12.1",
81
- "@rigour-labs/brain-darwin-arm64": "6.12.1",
82
- "@rigour-labs/brain-win-x64": "6.12.1"
78
+ "@rigour-labs/brain-darwin-arm64": "6.12.2-rc.2",
79
+ "@rigour-labs/brain-darwin-x64": "6.12.2-rc.2",
80
+ "@rigour-labs/brain-linux-arm64": "6.12.2-rc.2",
81
+ "@rigour-labs/brain-win-x64": "6.12.2-rc.2",
82
+ "@rigour-labs/brain-linux-x64": "6.12.2-rc.2"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@types/fs-extra": "^11.0.4",