@rigour-labs/core 6.6.5 → 6.6.6
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/gates/frontend-secret-exposure.d.ts +1 -1
- package/dist/gates/frontend-secret-exposure.js +9 -3
- package/dist/gates/frontend-secret-exposure.test.js +4 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/stop-review.d.ts +6 -9
- package/dist/hooks/stop-review.js +5 -10
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -2
- package/dist/review/backtest-init.d.ts +8 -0
- package/dist/review/backtest-init.js +87 -0
- package/dist/review/backtest-init.test.d.ts +1 -0
- package/dist/review/backtest-init.test.js +74 -0
- package/dist/review/backtest.d.ts +315 -0
- package/dist/review/backtest.js +197 -0
- package/dist/review/backtest.test.d.ts +1 -0
- package/dist/review/backtest.test.js +129 -0
- package/dist/review/dead-code.test.js +8 -0
- package/dist/review/lint-overlay.d.ts +21 -0
- package/dist/review/lint-overlay.js +89 -0
- package/dist/review/quiet.d.ts +6 -0
- package/dist/review/quiet.js +12 -2
- package/dist/review/quiet.test.js +11 -0
- package/dist/review/review.d.ts +6 -0
- package/dist/review/review.js +8 -1
- package/dist/review/reviewer/adapters.d.ts +35 -0
- package/dist/review/reviewer/adapters.js +156 -0
- package/dist/review/reviewer/background.d.ts +34 -0
- package/dist/review/reviewer/background.js +128 -0
- package/dist/review/reviewer/background.test.d.ts +1 -0
- package/dist/review/reviewer/background.test.js +104 -0
- package/dist/review/reviewer/exec.d.ts +17 -0
- package/dist/review/reviewer/exec.js +23 -0
- package/dist/review/reviewer/inputs.d.ts +37 -0
- package/dist/review/reviewer/inputs.js +103 -0
- package/dist/review/reviewer/merge-impact.d.ts +2 -0
- package/dist/review/reviewer/merge-impact.js +64 -0
- package/dist/review/reviewer/prompt.d.ts +21 -0
- package/dist/review/reviewer/prompt.js +123 -0
- package/dist/review/reviewer/store.d.ts +26 -0
- package/dist/review/reviewer/store.js +57 -0
- package/dist/review/reviewer/verdict.d.ts +127 -0
- package/dist/review/reviewer/verdict.js +156 -0
- package/dist/review/reviewer.d.ts +41 -49
- package/dist/review/reviewer.js +210 -202
- package/dist/review/reviewer.test.js +199 -48
- package/dist/review/toolchain.d.ts +5 -2
- package/dist/review/toolchain.js +81 -6
- package/dist/review/toolchain.test.js +39 -1
- package/dist/review/typed/program.d.ts +27 -0
- package/dist/review/typed/program.js +90 -0
- package/dist/review/typed/redundancy.d.ts +10 -0
- package/dist/review/typed/redundancy.js +339 -0
- package/dist/review/typed/redundancy.test.d.ts +1 -0
- package/dist/review/typed/redundancy.test.js +121 -0
- package/dist/review/unused-exports.js +8 -4
- package/dist/templates/universal-config.js +8 -1
- package/dist/types/index.d.ts +94 -8
- package/dist/types/index.js +24 -2
- package/package.json +6 -6
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export interface PriorPoint {
|
|
2
|
+
point: string;
|
|
3
|
+
review?: string;
|
|
4
|
+
severity?: 'blocking' | 'should-fix' | 'non-blocking';
|
|
5
|
+
resolved: boolean;
|
|
6
|
+
evidence?: string;
|
|
7
|
+
checked_siblings?: string[];
|
|
8
|
+
reviewer?: string;
|
|
9
|
+
}
|
|
10
|
+
interface Redundant {
|
|
11
|
+
file: string;
|
|
12
|
+
line?: number;
|
|
13
|
+
what: string;
|
|
14
|
+
made_redundant_by?: string;
|
|
15
|
+
removed: boolean;
|
|
16
|
+
reviewer?: string;
|
|
17
|
+
}
|
|
18
|
+
interface Read {
|
|
19
|
+
file: string;
|
|
20
|
+
line?: number;
|
|
21
|
+
read: string;
|
|
22
|
+
rules?: Array<{
|
|
23
|
+
rule: string;
|
|
24
|
+
known_before_read: boolean;
|
|
25
|
+
applied_before_read: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
narrower_source?: string | null;
|
|
28
|
+
keys?: Array<{
|
|
29
|
+
name: string;
|
|
30
|
+
inputs: string;
|
|
31
|
+
stable_under_edit: boolean;
|
|
32
|
+
}>;
|
|
33
|
+
window_bounded?: boolean | null;
|
|
34
|
+
keyset?: boolean | null;
|
|
35
|
+
reviewer?: string;
|
|
36
|
+
}
|
|
37
|
+
interface Scan {
|
|
38
|
+
file: string;
|
|
39
|
+
line?: number;
|
|
40
|
+
function: string;
|
|
41
|
+
outer: string;
|
|
42
|
+
inner: string;
|
|
43
|
+
fix: string;
|
|
44
|
+
reviewer?: string;
|
|
45
|
+
}
|
|
46
|
+
interface MergeImpactItem {
|
|
47
|
+
symbol: string;
|
|
48
|
+
main_file: string;
|
|
49
|
+
call_site: string;
|
|
50
|
+
holds: boolean;
|
|
51
|
+
why: string;
|
|
52
|
+
reviewer?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface Finding {
|
|
55
|
+
class: string;
|
|
56
|
+
file: string;
|
|
57
|
+
line?: number;
|
|
58
|
+
issue: string;
|
|
59
|
+
why?: string;
|
|
60
|
+
reviewer?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface Verdict {
|
|
63
|
+
prior_points: PriorPoint[];
|
|
64
|
+
redundant: Redundant[];
|
|
65
|
+
reads: Read[];
|
|
66
|
+
scans: Scan[];
|
|
67
|
+
merge_impact: MergeImpactItem[];
|
|
68
|
+
findings: Finding[];
|
|
69
|
+
carried: string[];
|
|
70
|
+
resolved_previous: Array<{
|
|
71
|
+
id: string;
|
|
72
|
+
evidence: string;
|
|
73
|
+
}>;
|
|
74
|
+
reviewer?: string;
|
|
75
|
+
cost_usd?: number;
|
|
76
|
+
reviewers?: Array<{
|
|
77
|
+
reviewer: string;
|
|
78
|
+
cost_usd?: number;
|
|
79
|
+
}>;
|
|
80
|
+
}
|
|
81
|
+
export interface OpenItem {
|
|
82
|
+
id: string;
|
|
83
|
+
kind: 'prior' | 'redundant' | 'read' | 'scan' | 'merge' | 'finding';
|
|
84
|
+
class: string;
|
|
85
|
+
file?: string;
|
|
86
|
+
line?: number;
|
|
87
|
+
issue: string;
|
|
88
|
+
evidence?: string;
|
|
89
|
+
reviewer?: string;
|
|
90
|
+
/** In delta mode, how the item reached this verdict. */
|
|
91
|
+
status?: 'carried' | 'not accounted for';
|
|
92
|
+
}
|
|
93
|
+
/** Checks a file (and a line) against the checkout; an item that fails cannot block. */
|
|
94
|
+
export type Verify = (file: string, line: number | undefined) => boolean;
|
|
95
|
+
/** The verdict in a reviewer's answer, or why it is not one. `needsPriorPoints`: a human review exists and none of its points is carried. */
|
|
96
|
+
export declare function parseVerdict(text: string, needsPriorPoints: boolean, reviewer: string, costUsd: number | undefined): {
|
|
97
|
+
verdict: Verdict;
|
|
98
|
+
} | {
|
|
99
|
+
error: string;
|
|
100
|
+
};
|
|
101
|
+
/** Several reviewers as one: a prior point is resolved only when every reviewer says so; every other item stands, tagged with who found it. */
|
|
102
|
+
export declare function mergeVerdicts(parts: Verdict[]): Verdict;
|
|
103
|
+
/**
|
|
104
|
+
* Delta mode: a human point the previous verdict resolved stays resolved unless the new commits
|
|
105
|
+
* touch a file its evidence names; it is carried into the new verdict so nothing disappears, and
|
|
106
|
+
* the reviewer is not paid to judge it again.
|
|
107
|
+
*/
|
|
108
|
+
export declare function carryResolved(verdict: Verdict, previous: Verdict | undefined, touched: Set<string>): Verdict;
|
|
109
|
+
/** True when the evidence names a file in the set (`src/a.ts:12`, `src/a.ts`). */
|
|
110
|
+
export declare function evidenceTouched(evidence: string | undefined, touched: Set<string>): boolean;
|
|
111
|
+
export interface Accounting {
|
|
112
|
+
/** What blocks: everything the reviewers reported that checks out, plus previous items still open. */
|
|
113
|
+
open: OpenItem[];
|
|
114
|
+
/** Items naming code the checkout does not have: shown, never a block. */
|
|
115
|
+
unverified: OpenItem[];
|
|
116
|
+
/** Previous open items the reviewer resolved, with the evidence. */
|
|
117
|
+
resolved: Array<{
|
|
118
|
+
item: OpenItem;
|
|
119
|
+
evidence: string;
|
|
120
|
+
}>;
|
|
121
|
+
/** Prior points the human marked non-blocking and the code still leaves open: for the reply. */
|
|
122
|
+
answerInReply: PriorPoint[];
|
|
123
|
+
}
|
|
124
|
+
/** Everything the reviewers reported blocks; in delta mode, previous open items carry unless resolved with evidence. */
|
|
125
|
+
export declare function account(verdict: Verdict, previousOpen: OpenItem[] | undefined, verify: Verify): Accounting;
|
|
126
|
+
export declare function itemLine(item: OpenItem): string;
|
|
127
|
+
export {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A reviewer's answer as a verdict, several verdicts as one, and the open items a verdict leaves:
|
|
3
|
+
* each with a stable id, so a later run, the branch record and a reply all name the same thing.
|
|
4
|
+
* Fails closed: an API error, a timeout, a crash or a malformed answer is never a pass. In delta
|
|
5
|
+
* mode nothing from the previous verdict disappears silently: an item the reviewer neither carried
|
|
6
|
+
* nor resolved with evidence stays open, marked "not accounted for". Every item that names code is
|
|
7
|
+
* checked against the checkout before it can block: a file not in the repository, or a line past
|
|
8
|
+
* its end, is a reviewer's slip, reported apart and never a block.
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from 'crypto';
|
|
11
|
+
const SHAPE = ['prior_points', 'reads', 'findings'];
|
|
12
|
+
const LISTS = ['redundant', 'scans', 'merge_impact', 'carried', 'resolved_previous'];
|
|
13
|
+
/** The verdict in a reviewer's answer, or why it is not one. `needsPriorPoints`: a human review exists and none of its points is carried. */
|
|
14
|
+
export function parseVerdict(text, needsPriorPoints, reviewer, costUsd) {
|
|
15
|
+
const parsed = verdictIn(text);
|
|
16
|
+
if (!parsed || !SHAPE.every(key => Array.isArray(parsed[key])))
|
|
17
|
+
return { error: `${reviewer}: no valid verdict (${text.slice(0, 200).replace(/\s+/g, ' ')})` };
|
|
18
|
+
if (needsPriorPoints && parsed.prior_points.length === 0)
|
|
19
|
+
return { error: `${reviewer} did not report on the human reviews` };
|
|
20
|
+
for (const key of LISTS)
|
|
21
|
+
if (!Array.isArray(parsed[key]))
|
|
22
|
+
parsed[key] = [];
|
|
23
|
+
return { verdict: { ...parsed, reviewer, ...(costUsd !== undefined ? { cost_usd: costUsd } : {}) } };
|
|
24
|
+
}
|
|
25
|
+
/** The whole answer, a fenced block, or the last object that starts with "prior_points" (a model sometimes writes a summary around it). */
|
|
26
|
+
function verdictIn(text) {
|
|
27
|
+
const candidates = [
|
|
28
|
+
text.trim(),
|
|
29
|
+
...[...text.matchAll(/```(?:json)?\s*([\s\S]*?)```/g)].map(m => m[1].trim()).reverse(),
|
|
30
|
+
...[...text.matchAll(/\{\s*"prior_points"/g)].map(m => text.slice(m.index)).reverse(),
|
|
31
|
+
];
|
|
32
|
+
for (const candidate of candidates) {
|
|
33
|
+
for (let end = candidate.lastIndexOf('}'); end > 0; end = candidate.lastIndexOf('}', end - 1)) {
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(candidate.slice(0, end + 1));
|
|
36
|
+
if (parsed && typeof parsed === 'object' && 'prior_points' in parsed)
|
|
37
|
+
return parsed;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// not a whole object yet: try a shorter one
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
/** Several reviewers as one: a prior point is resolved only when every reviewer says so; every other item stands, tagged with who found it. */
|
|
47
|
+
export function mergeVerdicts(parts) {
|
|
48
|
+
const norm = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim().slice(0, 80);
|
|
49
|
+
const prior = new Map();
|
|
50
|
+
for (const part of parts) {
|
|
51
|
+
for (const point of part.prior_points) {
|
|
52
|
+
const key = norm(point.point);
|
|
53
|
+
const current = prior.get(key);
|
|
54
|
+
if (!current || (current.resolved && !point.resolved))
|
|
55
|
+
prior.set(key, { ...point, reviewer: part.reviewer });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const tagged = (pick) => parts.flatMap(part => pick(part).map(item => ({ ...item, reviewer: part.reviewer })));
|
|
59
|
+
return {
|
|
60
|
+
prior_points: [...prior.values()],
|
|
61
|
+
redundant: tagged(part => part.redundant),
|
|
62
|
+
reads: tagged(part => part.reads),
|
|
63
|
+
scans: tagged(part => part.scans),
|
|
64
|
+
merge_impact: tagged(part => part.merge_impact),
|
|
65
|
+
findings: tagged(part => part.findings),
|
|
66
|
+
carried: parts.flatMap(part => part.carried),
|
|
67
|
+
resolved_previous: parts.length === 1 ? parts[0].resolved_previous : parts[0].resolved_previous.filter(x => parts.every(part => part.resolved_previous.some(y => y.id === x.id))),
|
|
68
|
+
reviewers: parts.map(part => ({ reviewer: part.reviewer ?? '?', ...(part.cost_usd !== undefined ? { cost_usd: part.cost_usd } : {}) })),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Delta mode: a human point the previous verdict resolved stays resolved unless the new commits
|
|
73
|
+
* touch a file its evidence names; it is carried into the new verdict so nothing disappears, and
|
|
74
|
+
* the reviewer is not paid to judge it again.
|
|
75
|
+
*/
|
|
76
|
+
export function carryResolved(verdict, previous, touched) {
|
|
77
|
+
if (!previous)
|
|
78
|
+
return verdict;
|
|
79
|
+
const norm = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim().slice(0, 80);
|
|
80
|
+
const judged = new Set(verdict.prior_points.map(p => norm(p.point)));
|
|
81
|
+
const carried = previous.prior_points.filter(p => p.resolved && !judged.has(norm(p.point)) && !evidenceTouched(p.evidence, touched));
|
|
82
|
+
return carried.length ? { ...verdict, prior_points: [...verdict.prior_points, ...carried] } : verdict;
|
|
83
|
+
}
|
|
84
|
+
/** True when the evidence names a file in the set (`src/a.ts:12`, `src/a.ts`). */
|
|
85
|
+
export function evidenceTouched(evidence, touched) {
|
|
86
|
+
for (const match of (evidence ?? '').matchAll(/[\w./-]+\.[A-Za-z]+/g))
|
|
87
|
+
if (touched.has(match[0]))
|
|
88
|
+
return true;
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
const id = (...parts) => createHash('sha1').update(parts.map(p => String(p ?? '')).join('|').toLowerCase().replace(/\s+/g, ' ')).digest('hex').slice(0, 10);
|
|
92
|
+
/** Everything the reviewers reported blocks; in delta mode, previous open items carry unless resolved with evidence. */
|
|
93
|
+
export function account(verdict, previousOpen, verify) {
|
|
94
|
+
const open = [];
|
|
95
|
+
const unverified = [];
|
|
96
|
+
const seen = new Set();
|
|
97
|
+
// A prior point is the human's and needs no file; anything else must name code the checkout has.
|
|
98
|
+
const add = (item) => {
|
|
99
|
+
if (seen.has(item.id))
|
|
100
|
+
return;
|
|
101
|
+
seen.add(item.id);
|
|
102
|
+
const placed = item.kind === 'prior' || (!!item.file && verify(item.file, item.line));
|
|
103
|
+
(placed ? open : unverified).push(item);
|
|
104
|
+
};
|
|
105
|
+
const answerInReply = [];
|
|
106
|
+
for (const p of verdict.prior_points) {
|
|
107
|
+
if (p.resolved)
|
|
108
|
+
continue;
|
|
109
|
+
if (p.severity === 'non-blocking')
|
|
110
|
+
answerInReply.push(p);
|
|
111
|
+
else
|
|
112
|
+
add({ id: id('prior', p.review, p.point), kind: 'prior', class: 'prior point', issue: p.point, evidence: p.evidence, reviewer: p.reviewer });
|
|
113
|
+
}
|
|
114
|
+
for (const r of verdict.redundant) {
|
|
115
|
+
if (r.removed === false)
|
|
116
|
+
add({ id: id('dead-code', r.file, r.what), kind: 'redundant', class: 'dead-code', file: r.file, line: r.line, issue: r.what, evidence: r.made_redundant_by ? `made redundant by ${r.made_redundant_by}` : undefined, reviewer: r.reviewer });
|
|
117
|
+
}
|
|
118
|
+
for (const r of verdict.reads) {
|
|
119
|
+
const problems = [
|
|
120
|
+
...(r.rules ?? []).filter(x => x.known_before_read && x.applied_before_read !== true).map(x => ({ cls: 'production-cost', text: `known before the read, applied after: ${x.rule}` })),
|
|
121
|
+
...(r.narrower_source ? [{ cls: 'production-cost', text: `narrower source: ${r.narrower_source}` }] : []),
|
|
122
|
+
...(r.window_bounded === false ? [{ cls: 'production-cost', text: 'window not bounded at both ends' }] : []),
|
|
123
|
+
...(r.keyset === false ? [{ cls: 'production-cost', text: 'OFFSET paging' }] : []),
|
|
124
|
+
...(r.keys ?? []).filter(k => k.stable_under_edit === false).map(k => ({ cls: 'correctness', text: `key ${k.name} changes when the user edits: ${k.inputs}` })),
|
|
125
|
+
];
|
|
126
|
+
for (const p of problems)
|
|
127
|
+
add({ id: id(p.cls, r.file, r.read, p.text), kind: 'read', class: p.cls, file: r.file, line: r.line, issue: `${r.read}: ${p.text}`, reviewer: r.reviewer });
|
|
128
|
+
}
|
|
129
|
+
for (const s of verdict.scans)
|
|
130
|
+
add({ id: id('production-cost', s.file, s.function), kind: 'scan', class: 'production-cost', file: s.file, line: s.line, issue: `${s.function} scans ${s.inner} per item of ${s.outer}`, evidence: s.fix, reviewer: s.reviewer });
|
|
131
|
+
for (const m of verdict.merge_impact) {
|
|
132
|
+
if (m.holds === false)
|
|
133
|
+
add({ id: id('correctness', m.call_site, m.symbol), kind: 'merge', class: 'correctness', file: m.call_site.split(':')[0], line: Number(m.call_site.split(':')[1]) || undefined, issue: `${m.symbol} from ${m.main_file} changed in main: ${m.why}`, reviewer: m.reviewer });
|
|
134
|
+
}
|
|
135
|
+
for (const f of verdict.findings)
|
|
136
|
+
add({ id: id(f.class, f.file, f.issue), kind: 'finding', class: f.class, file: f.file, line: f.line, issue: f.issue, evidence: f.why, reviewer: f.reviewer });
|
|
137
|
+
const resolved = [];
|
|
138
|
+
if (previousOpen) {
|
|
139
|
+
const evidence = new Map(verdict.resolved_previous.filter(r => r?.id && r.evidence).map(r => [r.id, r.evidence]));
|
|
140
|
+
for (const item of previousOpen) {
|
|
141
|
+
if (evidence.has(item.id))
|
|
142
|
+
resolved.push({ item, evidence: evidence.get(item.id) });
|
|
143
|
+
else if (!seen.has(item.id)) {
|
|
144
|
+
seen.add(item.id);
|
|
145
|
+
open.push({ ...item, status: verdict.carried.includes(item.id) ? 'carried' : 'not accounted for' });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { open, unverified, resolved, answerInReply };
|
|
150
|
+
}
|
|
151
|
+
export function itemLine(item) {
|
|
152
|
+
const where = item.file ? ` ${item.file}${item.line ? `:${item.line}` : ''}` : '';
|
|
153
|
+
const by = item.reviewer ? ` (${item.reviewer})` : '';
|
|
154
|
+
const status = item.status ? `, ${item.status}` : '';
|
|
155
|
+
return `[${item.class}${status}]${by}${where} ${item.issue}${item.evidence ? `\n ${item.evidence}` : ''}`;
|
|
156
|
+
}
|
|
@@ -1,55 +1,47 @@
|
|
|
1
1
|
import type { Config } from '../types/index.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
2
|
+
import { type ReviewerName } from './reviewer/adapters.js';
|
|
3
|
+
import { type Exec, type Progress } from './reviewer/exec.js';
|
|
4
|
+
import { type Accounting, type OpenItem, type PriorPoint } from './reviewer/verdict.js';
|
|
5
|
+
export { defaultExec, githubEnv, parseJsonArrays, type Exec, type Progress } from './reviewer/exec.js';
|
|
6
|
+
export { itemLine, type OpenItem } from './reviewer/verdict.js';
|
|
7
|
+
export type ReviewerOutcome = 'passed' | 'findings' | 'unavailable' | 'skipped';
|
|
8
|
+
export interface ReviewerOptions {
|
|
9
|
+
/** The pull request to read when the checkout is detached (a backtest). */
|
|
10
|
+
pr?: number;
|
|
11
|
+
/** ISO time: a review or comment posted from then on is not shown to the reviewer (a backtest). */
|
|
12
|
+
reviewsBefore?: string;
|
|
13
|
+
/** At push the ready-pull-request rule applies; a review command or a backtest always reviews. */
|
|
14
|
+
trigger?: 'push' | 'review' | 'backtest';
|
|
15
|
+
/** Two vendors, verdicts merged: the step before requesting a human review. */
|
|
16
|
+
full?: boolean;
|
|
17
|
+
/** Review again even when a verdict for these inputs is cached. */
|
|
18
|
+
force?: boolean;
|
|
19
|
+
/** Deterministic hints (a nested scan, a property that only leaves through serialisation): candidates for the reviewer to confirm. */
|
|
20
|
+
hints?: string;
|
|
21
|
+
/** The branch the commit was pushed from, when reviewing it in a detached worktree (background.ts). */
|
|
22
|
+
branch?: string;
|
|
22
23
|
}
|
|
23
24
|
export interface ReviewerResult {
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
|
|
25
|
+
outcome: ReviewerOutcome;
|
|
26
|
+
/** What blocks: every open item, with a stable id. */
|
|
27
|
+
items: OpenItem[];
|
|
28
|
+
/** Items naming code the checkout does not have: shown, never a block. */
|
|
29
|
+
unverified: OpenItem[];
|
|
30
|
+
/** Previous open items resolved since the last verdict, with the evidence. */
|
|
31
|
+
resolved: Accounting['resolved'];
|
|
32
|
+
/** Non-blocking human points still open: answered in the reply, not held against the push. */
|
|
33
|
+
answerInReply: PriorPoint[];
|
|
34
|
+
/** Why there is no verdict (unavailable) or why none was sought (skipped). */
|
|
35
|
+
reason?: string;
|
|
36
|
+
reviewers: ReviewerName[];
|
|
37
|
+
scope?: 'full' | 'delta';
|
|
38
|
+
why?: string;
|
|
39
|
+
costUsd?: number;
|
|
29
40
|
cached: boolean;
|
|
41
|
+
/** The latest human review it worked from (`<login>, <date> (<n> reviews)`). */
|
|
42
|
+
previousReview?: string;
|
|
43
|
+
pr?: number;
|
|
30
44
|
}
|
|
31
|
-
/**
|
|
32
|
-
export type Exec = (command: string, args: string[], options: {
|
|
33
|
-
cwd: string;
|
|
34
|
-
timeoutMs: number;
|
|
35
|
-
env?: Record<string, string>;
|
|
36
|
-
}) => Promise<{
|
|
37
|
-
exitCode: number;
|
|
38
|
-
stdout: string;
|
|
39
|
-
stderr: string;
|
|
40
|
-
}>;
|
|
41
|
-
/** Called while the reviewer works, so a slow run and a stuck one look different. */
|
|
42
|
-
export type Progress = (message: string) => void;
|
|
43
|
-
export declare function runReviewer(cwd: string, base: string, config: Config, exec?: Exec, progress?: Progress): Promise<ReviewerResult>;
|
|
44
|
-
/** Open blocking points and new blocking issues: what stops a push. An open non-blocking point is answered in the reply, not held against the push. */
|
|
45
|
+
/** Findings and no verdict stop a push; a skipped review does not, and is reported as owed. */
|
|
45
46
|
export declare function reviewerBlocks(result: ReviewerResult): boolean;
|
|
46
|
-
|
|
47
|
-
export declare function parseVerdict(answer: {
|
|
48
|
-
exitCode: number;
|
|
49
|
-
stdout: string;
|
|
50
|
-
stderr: string;
|
|
51
|
-
}, hadReview: boolean): {
|
|
52
|
-
verdict: ReviewerVerdict;
|
|
53
|
-
} | {
|
|
54
|
-
error: string;
|
|
55
|
-
};
|
|
47
|
+
export declare function runReviewer(cwd: string, base: string, config: Config, exec?: Exec, progress?: Progress, options?: ReviewerOptions): Promise<ReviewerResult>;
|