feat: add per-file +/− summary and noise-filtered scope to review prompts

Emit a ### Changed Files Markdown table (| File | +/− | Type | rows plus
total added/removed) ahead of the raw diff in both committed and
uncommitted review prompts, parsed from the diff via the shared
parseDiff engine. Add an ### Excluded Files (n) section listing filtered
noise (path, +/− counts, reason), and replace byte-truncation of oversized
diffs with a file-list + read instruction when the cleaned diff exceeds
50KB or touches more than 20 files.

Also: distinguish diff-computation failure from genuinely no changes in the
review loop (tri-state result, never treats a broken base ref as a clean
verified task), map critical→blocker in finding severity parsing, inject a
per-review custom focus/instructions section from config, and make the
noise-filter ignore rules project-configurable via review.extraIgnorePatterns
and review.ignorePaths. Add same-model retry before cycling to the next
model in task/follow-up/fix sessions.
This commit is contained in:
2026-08-08 18:10:08 -04:00
parent 88f6b4df93
commit b86174782f
9 changed files with 698 additions and 95 deletions

View File

@@ -0,0 +1,53 @@
/**
* Tests for the severity taxonomy alignment in review verdict parsing
* (src/review.ts): the `critical` token is accepted and normalized to
* ralpi's `blocker` severity, mirroring @piex-dev/review's grading.
*/
import { describe, test, expect } from "bun:test";
import { extractReview } from "../src/review";
/** Build a full review-agent output ending in a REVIEW VERDICT block. */
function reviewOutput(findings: string[]): string {
return [
"Prose: looks mostly fine, a few issues to fix.",
"## REVIEW VERDICT",
"VERDICT: fail",
"SUMMARY: Needs fixes.",
"FINDINGS:",
...findings,
].join("\n");
}
describe("extractReview severity normalization", () => {
test("maps critical → blocker, keeps warning/nit/info", () => {
const out = reviewOutput([
"- [critical] src/auth.ts:12 hardcoded secret",
"- [warning] src/auth.ts:30 unused import",
"- [nit] src/auth.ts:5 style",
"- [info] src/auth.ts:1 note",
]);
const review = extractReview(out, "01", "abc1234");
expect(review).not.toBeNull();
const severities = review!.findings.map((f) => f.severity);
expect(severities).toEqual(["blocker", "warning", "nit", "info"]);
});
test("normalizes the warn synonym to warning", () => {
const out = reviewOutput(["- [warn] src/a.ts:2 thing"]);
const review = extractReview(out, "01", "abc1234");
expect(review!.findings[0].severity).toBe("warning");
});
test("uppercase CRITICAL token also maps to blocker", () => {
const out = reviewOutput(["- [CRITICAL] src/a.ts:2 thing"]);
const review = extractReview(out, "01", "abc1234");
expect(review!.findings[0].severity).toBe("blocker");
});
test("findings without a severity are still parsed", () => {
const out = reviewOutput(["- src/a.ts:2 plain line"]);
const review = extractReview(out, "01", "abc1234");
expect(review!.findings[0].severity).toBe("info");
});
});