iterate-plugin 2.7.2 → 2.8.0
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/README.md +21 -0
- package/README.zh-CN.md +21 -0
- package/dist/config-loader.js +1 -1
- package/dist/evidence.js +143 -0
- package/dist/meta-review.js +29 -1
- package/dist/review.js +10 -2
- package/dist/skill-prompt.js +4 -2
- package/dist/tools/review.js +10 -1
- package/lib/client.js +1474 -734
- package/package.json +6 -2
- package/src/client/index.ts +1218 -0
- package/src/config-loader.ts +1 -1
- package/src/evidence.ts +194 -0
- package/src/meta-review.ts +34 -1
- package/src/review.ts +12 -2
- package/src/skill-prompt.ts +4 -2
- package/src/tools/review.ts +10 -1
- package/src/types.ts +1 -1
package/README.md
CHANGED
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
> **Developed and reviewed in the [iterate-skill monorepo](https://github.com/jingzhao-l/iterate-skill)**: the plugin code is maintained in the main repository and synced here via `git subtree`; **releases and npm publishing happen in this (plugin) repository**, which is the canonical publish point for the dsh ecosystem. Please **star / fork the main repository** and file issues at the [main repository Issues](https://github.com/jingzhao-l/iterate-skill/issues).
|
|
9
9
|
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://github.com/jingzhao-l/iterate-plugin"><img src="https://img.shields.io/github/stars/jingzhao-l/iterate-plugin?style=social&label=Star" alt="Stars"></a>
|
|
12
|
+
<a href="https://github.com/jingzhao-l/iterate-skill"><img src="https://img.shields.io/github/stars/jingzhao-l/iterate-skill?style=social&label=Main%20Repo%20Star" alt="Main Repo Stars"></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/iterate-plugin"><img src="https://img.shields.io/npm/dt/iterate-plugin?label=Downloads&logo=npm&logoColor=white" alt="npm downloads"></a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
> ⭐ If this helps your dsh workflow, give the main repo a star — it means a lot!
|
|
17
|
+
|
|
10
18
|
## About This Plugin
|
|
11
19
|
|
|
12
20
|
**iterate** is an open-source project that gives AI coding assistants the ability to review and fix code in multi-round autonomous loops. It targets a concrete pain point:
|
|
@@ -221,6 +229,19 @@ All tests pass:
|
|
|
221
229
|
- **212 unit tests green**, type-check clean
|
|
222
230
|
- Coverage: dedupe, filter, sort, multi-round convergence, meta-review audit, path safety, timeout clamping, config read/write + rollback, triage merge, diff computation, checkpoint validation, fix registry, history read + filter, prune cleanup report + dry-run semantics, UI pure functions (select-all key, runtime status guide).
|
|
223
231
|
|
|
232
|
+
## ⚠️ Disclaimer
|
|
233
|
+
|
|
234
|
+
This project is provided "AS IS", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement.
|
|
235
|
+
|
|
236
|
+
**Automated code review and fixing carries inherent risk.** All changes produced in normal mode are generated by AI models and may introduce bugs, regressions, or unintended behavior. Before merging, you should:
|
|
237
|
+
|
|
238
|
+
- Review every diff before applying it to your main branch or pushing.
|
|
239
|
+
- Make sure your project is under git control and can be rolled back (`git restore`, revert, or restore from backup).
|
|
240
|
+
- Run your project's own test suite and build checks after each round of fixes.
|
|
241
|
+
- Never run this on secrets, credentials, `.env`, or files that must not be modified — configure `protected_paths` accordingly.
|
|
242
|
+
|
|
243
|
+
Users are solely responsible for the code that is generated, modified, or committed as a result of using this project. By using it, you acknowledge that neither the maintainers nor contributors are liable for any loss, damage, or legal consequences arising from its use.
|
|
244
|
+
|
|
224
245
|
## License
|
|
225
246
|
|
|
226
247
|
MIT
|
package/README.zh-CN.md
CHANGED
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
> **开发与评审在 [iterate-skill 主仓库](https://github.com/jingzhao-l/iterate-skill) 完成**:插件代码由主仓库统一维护,通过 `git subtree` 同步到本仓库;**版本发版与 npm 发布在本仓库(插件仓库)进行**,作为 dsh 生态的正式发布位。欢迎 **star / fork 主仓库** 并在 [主仓库 Issues](https://github.com/jingzhao-l/iterate-skill/issues) 反馈问题。
|
|
9
9
|
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://github.com/jingzhao-l/iterate-plugin"><img src="https://img.shields.io/github/stars/jingzhao-l/iterate-plugin?style=social&label=Star" alt="Stars"></a>
|
|
12
|
+
<a href="https://github.com/jingzhao-l/iterate-skill"><img src="https://img.shields.io/github/stars/jingzhao-l/iterate-skill?style=social&label=主仓库%20Star" alt="主仓库 Stars"></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/iterate-plugin"><img src="https://img.shields.io/npm/dt/iterate-plugin?label=Downloads&logo=npm&logoColor=white" alt="npm downloads"></a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
> ⭐ 如果这个插件对你的 dsh 工作流有帮助,欢迎为主仓库点亮 Star,这是对开源维护最大的支持!
|
|
17
|
+
|
|
10
18
|
## 这是什么 / About This Plugin
|
|
11
19
|
|
|
12
20
|
**iterate** 是一个让 AI 编程助手具备多轮自主代码审查与修复能力的开源项目。它解决很具体的痛点:
|
|
@@ -221,6 +229,19 @@ npm test
|
|
|
221
229
|
- **212 个单元测试全绿**,类型检查通过
|
|
222
230
|
- 覆盖:去重、过滤、排序、多轮收敛、meta-review 审计、路径安全、超时钳制、配置读写与回滚、triage 合并、diff 计算、checkpoint 校验、修复注册表、历史读取与过滤、prune 清理报告与 dry-run 语义、UI 纯函数(select-all 键、运行时状态指引)等
|
|
223
231
|
|
|
232
|
+
## ⚠️ 免责声明
|
|
233
|
+
|
|
234
|
+
本项目按「现状」(AS IS)提供,不附带任何明示或暗示的担保,包括但不限于对适销性、特定用途适用性及不侵权性的担保。
|
|
235
|
+
|
|
236
|
+
**自动化的代码审查与修复存在固有风险。** normal 模式下产生的改动均由 AI 模型生成,可能引入缺陷、回归或非预期行为。在合并改动前,你应当:
|
|
237
|
+
|
|
238
|
+
- 在应用到主分支或推送前,逐条 review 每一处 diff。
|
|
239
|
+
- 确保项目处于 git 版本控制之下,并可随时回滚(`git restore`、revert 或从备份恢复)。
|
|
240
|
+
- 在每轮修复后运行项目自身的测试与构建检查。
|
|
241
|
+
- 切勿在密钥、凭证、`.env` 或任何不允许修改的文件上运行本项目;请在配置中设置 `protected_paths` 予以保护。
|
|
242
|
+
|
|
243
|
+
使用者需为本项目使用过程中所产生、修改或提交的代码负全部责任。使用本项目即表示你同意:维护者与贡献者不对因使用本项目而导致的任何损失、损害或法律后果承担责任。
|
|
244
|
+
|
|
224
245
|
## License
|
|
225
246
|
|
|
226
247
|
MIT
|
package/dist/config-loader.js
CHANGED
|
@@ -48,7 +48,7 @@ export function defaultConfig() {
|
|
|
48
48
|
auto_merge: false,
|
|
49
49
|
},
|
|
50
50
|
validation: { command_whitelist: [], commands: {} },
|
|
51
|
-
reviewer: { output_schema_validation: true },
|
|
51
|
+
reviewer: { output_schema_validation: true, evidence_validation: true },
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
/**
|
package/dist/evidence.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic code-evidence verification for review findings.
|
|
3
|
+
*
|
|
4
|
+
* Mirror of `iterate_harness/iterate/evidence.py` for the iterate-plugin.
|
|
5
|
+
*
|
|
6
|
+
* The iterate review loop requires that reviewer subagent findings ANCHOR to
|
|
7
|
+
* real code instead of speculating. This module enforces it:
|
|
8
|
+
*
|
|
9
|
+
* - a finding's `file` must resolve to an existing file under the project root
|
|
10
|
+
* (traversal-safe), otherwise evidence is poisoned (`file_not_found`);
|
|
11
|
+
* - a finding with an explicit line must reference a line that actually exists
|
|
12
|
+
* in that file (`line_out_of_range`);
|
|
13
|
+
* - a whole-file finding (line 0 / undefined) must still reference an existing
|
|
14
|
+
* file, so even structural findings cannot point at nothing;
|
|
15
|
+
* - `readVerified` is a best-effort, NON-gating hint: the plugin's reviewers are
|
|
16
|
+
* subagents whose reads are not aggregated here, so it is only set when a
|
|
17
|
+
* read set is explicitly provided and never fails the audit.
|
|
18
|
+
*
|
|
19
|
+
* Gate rule (user preference): ANY localizable finding with poisoned evidence
|
|
20
|
+
* flips the whole audit to `passed: false`, so the meta-review forces revision.
|
|
21
|
+
*
|
|
22
|
+
* The pure math (`countLines`, `verifyLineBounds`) is separated from the
|
|
23
|
+
* filesystem half (`verifyFinding`) to stay unit-testable without touching disk.
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
26
|
+
import { resolve, sep } from 'node:path';
|
|
27
|
+
/** Sentinel for whole-file findings (line 0 or omitted means the whole file). */
|
|
28
|
+
export const WHOLE_FILE_LINE = 0;
|
|
29
|
+
/** Number of physical lines in `text`. A trailing newline does not add a line. */
|
|
30
|
+
export function countLines(text) {
|
|
31
|
+
if (text === '')
|
|
32
|
+
return 0;
|
|
33
|
+
const parts = text.split(/\r\n|\r|\n/);
|
|
34
|
+
// A trailing newline leaves an empty final element that is NOT a line
|
|
35
|
+
// (mirrors Python `str.splitlines()` used by the harness).
|
|
36
|
+
if (parts[parts.length - 1] === '')
|
|
37
|
+
return parts.length - 1;
|
|
38
|
+
return parts.length;
|
|
39
|
+
}
|
|
40
|
+
/** Resolve `root/rel` and reject any path escaping `root` (returns null). */
|
|
41
|
+
export function resolveWithin(root, rel) {
|
|
42
|
+
const resolved = resolve(root, rel);
|
|
43
|
+
const rootResolved = resolve(root);
|
|
44
|
+
if (resolved === rootResolved)
|
|
45
|
+
return resolved;
|
|
46
|
+
const prefix = rootResolved.endsWith(sep) ? rootResolved : rootResolved + sep;
|
|
47
|
+
if (!resolved.startsWith(prefix))
|
|
48
|
+
return null;
|
|
49
|
+
return resolved;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Pure check that `line` (if anchored) exists in `text`.
|
|
53
|
+
* Whole-file findings (undefined/0) are always bounds-valid.
|
|
54
|
+
*/
|
|
55
|
+
export function verifyLineBounds(line, text) {
|
|
56
|
+
const lineTotal = countLines(text);
|
|
57
|
+
if (line === undefined || line === null || line === WHOLE_FILE_LINE) {
|
|
58
|
+
return { inBounds: true, lineTotal };
|
|
59
|
+
}
|
|
60
|
+
if (line < 1)
|
|
61
|
+
return { inBounds: false, lineTotal };
|
|
62
|
+
return { inBounds: line <= lineTotal, lineTotal };
|
|
63
|
+
}
|
|
64
|
+
/** Verify a single finding's location against the real filesystem. */
|
|
65
|
+
export function verifyFinding(root, input, opts = {}) {
|
|
66
|
+
const relFile = input.file ?? '';
|
|
67
|
+
const line = typeof input.line === 'number' ? input.line : null;
|
|
68
|
+
const resolved = resolveWithin(root, relFile);
|
|
69
|
+
if (resolved === null || !existsSync(resolved)) {
|
|
70
|
+
return {
|
|
71
|
+
file: relFile,
|
|
72
|
+
line,
|
|
73
|
+
lineTotal: null,
|
|
74
|
+
resolvedPath: resolved,
|
|
75
|
+
verified: false,
|
|
76
|
+
error: 'file_not_found',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
let text;
|
|
80
|
+
try {
|
|
81
|
+
text = readFileSync(resolved, 'utf-8');
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return {
|
|
85
|
+
file: relFile,
|
|
86
|
+
line,
|
|
87
|
+
lineTotal: null,
|
|
88
|
+
resolvedPath: resolved,
|
|
89
|
+
verified: false,
|
|
90
|
+
error: 'file_not_found',
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const { inBounds, lineTotal } = verifyLineBounds(line, text);
|
|
94
|
+
if (!inBounds) {
|
|
95
|
+
return {
|
|
96
|
+
file: relFile,
|
|
97
|
+
line,
|
|
98
|
+
lineTotal,
|
|
99
|
+
resolvedPath: resolved,
|
|
100
|
+
verified: false,
|
|
101
|
+
error: 'line_out_of_range',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const outcome = {
|
|
105
|
+
file: relFile,
|
|
106
|
+
line,
|
|
107
|
+
lineTotal,
|
|
108
|
+
resolvedPath: resolved,
|
|
109
|
+
verified: true,
|
|
110
|
+
};
|
|
111
|
+
if (opts.readSet !== undefined) {
|
|
112
|
+
outcome.readVerified = opts.readSet.has(resolved);
|
|
113
|
+
}
|
|
114
|
+
return outcome;
|
|
115
|
+
}
|
|
116
|
+
/** Attest every finding in a list. */
|
|
117
|
+
export function verifyFindings(root, findings, opts = {}) {
|
|
118
|
+
const results = findings.map((f) => verifyFinding(root, f, opts));
|
|
119
|
+
return { checked: results.length, results };
|
|
120
|
+
}
|
|
121
|
+
/** `passed` is true only when no real existence failure exists (read is a hint). */
|
|
122
|
+
export function evidencePassed(audit) {
|
|
123
|
+
return audit.results.every((r) => r.error === undefined);
|
|
124
|
+
}
|
|
125
|
+
/** Violating (non-grounded) results. */
|
|
126
|
+
export function evidenceViolations(audit) {
|
|
127
|
+
return audit.results.filter((r) => r.error !== undefined);
|
|
128
|
+
}
|
|
129
|
+
/** Serialize an audit for tool payloads (pure). */
|
|
130
|
+
export function evidenceToPlain(audit) {
|
|
131
|
+
const computable = audit.results.filter((r) => r.readVerified !== undefined);
|
|
132
|
+
const readRatio = computable.length === 0
|
|
133
|
+
? null
|
|
134
|
+
: Number((computable.filter((r) => r.readVerified === true).length / computable.length).toFixed(3));
|
|
135
|
+
return {
|
|
136
|
+
checked: audit.checked,
|
|
137
|
+
passed: evidencePassed(audit),
|
|
138
|
+
violations: audit.results
|
|
139
|
+
.filter((r) => r.error !== undefined)
|
|
140
|
+
.map((r) => ({ file: r.file, line: r.line, lineTotal: r.lineTotal, verified: r.verified, error: r.error })),
|
|
141
|
+
readVerifiedRatio: readRatio,
|
|
142
|
+
};
|
|
143
|
+
}
|
package/dist/meta-review.js
CHANGED
|
@@ -157,9 +157,37 @@ export function metaReviewReport(report) {
|
|
|
157
157
|
/**
|
|
158
158
|
* Build the final review report: pair the source report with its meta-review
|
|
159
159
|
* verdict and a rolled-up summary. Pure and deterministic.
|
|
160
|
+
*
|
|
161
|
+
* `evidence` (an EvidenceAudit produced against the real repo) is the hard
|
|
162
|
+
* code-evidence gate: every finding whose file/line does not resolve to
|
|
163
|
+
* existing code is emitted as a critical EVIDENCE_VIOLATION and flips the
|
|
164
|
+
* verdict to `needs_revision`. The audit itself reads the filesystem; this
|
|
165
|
+
* function only folds the (pure, precomputed) result in.
|
|
160
166
|
*/
|
|
161
|
-
export function buildFinalReviewReport(report) {
|
|
167
|
+
export function buildFinalReviewReport(report, opts = {}) {
|
|
162
168
|
const meta = metaReviewReport(report);
|
|
169
|
+
const evidence = opts.evidence ?? null;
|
|
170
|
+
if (evidence !== null) {
|
|
171
|
+
meta.checksRun += 1;
|
|
172
|
+
if (evidence.results.some((r) => r.error !== undefined)) {
|
|
173
|
+
for (const violation of evidence.results) {
|
|
174
|
+
if (violation.error === undefined)
|
|
175
|
+
continue;
|
|
176
|
+
const detail = violation.error === 'line_out_of_range'
|
|
177
|
+
? `${violation.line} is beyond this file's ${violation.lineTotal} lines`
|
|
178
|
+
: `${violation.file} does not exist at all (verifiable read required)`;
|
|
179
|
+
meta.issues.push({
|
|
180
|
+
code: 'EVIDENCE_VIOLATION',
|
|
181
|
+
severity: 'critical',
|
|
182
|
+
summary: `Finding references non-existent code: ${violation.file}` +
|
|
183
|
+
(violation.line ? `:${violation.line}` : ''),
|
|
184
|
+
detail: detail + '. Review results must anchor to real, read code.',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
meta.passed = false;
|
|
188
|
+
meta.verdict = 'revise';
|
|
189
|
+
}
|
|
190
|
+
}
|
|
163
191
|
const summary = report?.summary ?? {};
|
|
164
192
|
const verdict = meta.passed ? 'approved' : 'needs_revision';
|
|
165
193
|
return {
|
package/dist/review.js
CHANGED
|
@@ -290,9 +290,17 @@ export function reviewerTaskPrompt(input) {
|
|
|
290
290
|
else {
|
|
291
291
|
parts.push('This is round 1 — report every issue you find in this dimension.');
|
|
292
292
|
}
|
|
293
|
+
parts.push('EVIDENCE RULE (mandatory): read every file you report on with the ' +
|
|
294
|
+
'read_file tool BEFORE judging it. NEVER report a location you did not ' +
|
|
295
|
+
'actually read — speculation about code you never inspected is a ' +
|
|
296
|
+
'disqualifying failure, and fabricated line numbers are treated as ' +
|
|
297
|
+
'poisoned evidence. Anchor every finding to real code.');
|
|
293
298
|
parts.push(`Return a JSON object: {"findings": [...]}.`, `Each finding: dimension (must be "${input.dimension}"), file (relative path), ` +
|
|
294
|
-
'line (
|
|
295
|
-
'
|
|
299
|
+
'line (REQUIRED positive integer — the exact line you READ for an ' +
|
|
300
|
+
'anchored, line-targeted issue; use 0 for whole-file/module-level ' +
|
|
301
|
+
'issues), severity (critical/high/medium/low), summary (one line), ' +
|
|
302
|
+
'failure_scenario (how/when it fails, backed by the code you actually ' +
|
|
303
|
+
'read), suggested_fix (the concrete fix), ' +
|
|
296
304
|
`is_atomic (true if the fix is <= ${input.maxLines} lines within a SINGLE file/function, else false).`, `Write summaries and details in ${input.outputLanguage}.`);
|
|
297
305
|
return parts.join('\n');
|
|
298
306
|
}
|
package/dist/skill-prompt.js
CHANGED
|
@@ -121,10 +121,11 @@ return {
|
|
|
121
121
|
|
|
122
122
|
Key rules for dry-run:
|
|
123
123
|
- **NEVER call a fixer / never edit files / never create branches or worktree.** Reviewers read only.
|
|
124
|
+
- **Every reviewer MUST actually read each file it reports on (read_file) BEFORE judging it, and anchor every finding to a real location. Fabricated file paths or invented line numbers are poisoned evidence and fail the run.** Subagents never report on code they didn't inspect.
|
|
124
125
|
- Each round feeds the already-known findings to reviewers so they hunt NEW issues only → that is what drives convergence.
|
|
125
126
|
- Stop when a round reports 0 new findings (converged) or maxReviewRounds is reached.
|
|
126
127
|
- The report (with per-round convergence stats + suggested fix priorities) is the deliverable.
|
|
127
|
-
- **Meta-review**: after building the report, audit it with \`iterate_review({operation:"meta-review"})\` for internal consistency (counts, severity buckets, dimension sums, sort order, convergence math). The \`finalReport.verdict\` is \`approved\` only when the report passes every check; otherwise \`needs_revision\`. Surface the final report and its verdict as the closing deliverable.
|
|
128
|
+
- **Meta-review**: after building the report, audit it with \`iterate_review({operation:"meta-review"})\` for internal consistency (counts, severity buckets, dimension sums, sort order, convergence math). The meta-review ALSO runs the hard code-evidence gate (default on): every finding's file/line is validated against real files on disk, so any fabricated location surfaces as a critical \`EVIDENCE_VIOLATION\` and flips the verdict to \`needs_revision\`. The \`finalReport.verdict\` is \`approved\` only when the report passes every check AND every finding anchors to real, read code; otherwise \`needs_revision\`. Surface the final report and its verdict as the closing deliverable.
|
|
128
129
|
- Only a single \`report\` entry may be appended to the decision log; nothing else is written.
|
|
129
130
|
|
|
130
131
|
### Normal-mode workflow (autonomous closed loop)
|
|
@@ -328,11 +329,12 @@ Key rules for normal mode:
|
|
|
328
329
|
- Close with \`iterate_status\` metrics and surface the convergence indicators (fixed count, remaining architectural count, abort reason) in the final summary.
|
|
329
330
|
|
|
330
331
|
### Finding schema (for reviewer agents)
|
|
331
|
-
{ "dimension": string, "file": string (relative path), "line": number (
|
|
332
|
+
{ "dimension": string, "file": string (relative path), "line": number (REQUIRED for line-targeted issues — the exact line you READ; use 0 for whole-file/module-level issues),
|
|
332
333
|
"severity": "critical" | "high" | "medium" | "low", "summary": string (one line),
|
|
333
334
|
"failure_scenario": string (how/when it fails), "suggested_fix": string (the concrete fix),
|
|
334
335
|
"is_atomic": boolean (true if fix ≤ max_lines within a single file/function) }
|
|
335
336
|
Atomic = is_atomic true (single file, single function, ≤ config.atomic.max_lines lines change). Architectural = everything else.
|
|
337
|
+
Every finding MUST reference a file the reviewer actually read (read_file) and a real location — never speculate about code that was never inspected. Fabricated paths/lines are poisoned evidence and fail the meta-review evidence gate.
|
|
336
338
|
|
|
337
339
|
### Workflow meta
|
|
338
340
|
Always pass \`meta: { name: "iterate", description: "Autonomous iterate loop" }\`.
|
package/dist/tools/review.js
CHANGED
|
@@ -2,6 +2,7 @@ import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
|
2
2
|
import { loadEffectiveConfig, resolveProjectRoot } from "../config-loader.js";
|
|
3
3
|
import { buildReviewPlan, buildReviewReport } from "../review.js";
|
|
4
4
|
import { buildFinalReviewReport, metaReviewReport } from "../meta-review.js";
|
|
5
|
+
import { evidenceToPlain, verifyFindings } from "../evidence.js";
|
|
5
6
|
/** Default round cap when neither the arg nor config provides one. */
|
|
6
7
|
const DEFAULT_MAX_REVIEW_ROUNDS = 3;
|
|
7
8
|
/**
|
|
@@ -82,6 +83,7 @@ export function registerReviewTool(ctx) {
|
|
|
82
83
|
found: { type: 'boolean' },
|
|
83
84
|
plan: { type: 'json' },
|
|
84
85
|
report: { type: 'json' },
|
|
86
|
+
evidence: { type: 'json' },
|
|
85
87
|
finalReport: { type: 'json' },
|
|
86
88
|
error: { type: 'string' },
|
|
87
89
|
},
|
|
@@ -147,12 +149,19 @@ export function registerReviewTool(ctx) {
|
|
|
147
149
|
};
|
|
148
150
|
}
|
|
149
151
|
const audit = metaReviewReport(source);
|
|
150
|
-
|
|
152
|
+
// Hard code-evidence gate (default on): every finding's file/line is
|
|
153
|
+
// validated against real files on disk before folding into the final
|
|
154
|
+
// verdict. Disable via config `reviewer.evidence_validation: false`.
|
|
155
|
+
const evidenceEnabled = config.reviewer?.evidence_validation !== false;
|
|
156
|
+
const findings = Array.isArray(source.findings) ? source.findings : [];
|
|
157
|
+
const evidence = evidenceEnabled ? verifyFindings(projectRoot, findings) : null;
|
|
158
|
+
const finalReport = buildFinalReviewReport(source, { evidence });
|
|
151
159
|
return {
|
|
152
160
|
operation: 'meta-review',
|
|
153
161
|
mode,
|
|
154
162
|
found: true,
|
|
155
163
|
report: audit,
|
|
164
|
+
evidence: evidence ? evidenceToPlain(evidence) : null,
|
|
156
165
|
finalReport: finalReport,
|
|
157
166
|
};
|
|
158
167
|
}
|