From de33238c9d880d94e6cc4e7619bd132da4bb42c1 Mon Sep 17 00:00:00 2001 From: Eric Skavinski Date: Tue, 19 May 2026 03:56:44 +0000 Subject: [PATCH] fix(review-validation): accept legitimate citations beyond prompt truncation The reviewer was rejecting findings whose evidence pointed at lines or quotes that exist in the actual file but happen to sit past the per-file truncation applied to the prompt. Two causes: 1. `validateReviewOutput` counted line ranges against the *truncated* copy of the file. A finding citing `bot.py:2628-2648` in a real 5654-line file was thrown out as "evidence line range exceeds file length" because the truncated copy was only ~600 lines. 2. The truncation limit (24,000 chars) was too aggressive for moderate monolith files. A 228KB source file was reduced to ~10% before ever reaching the reviewer, which silently dropped findings. Changes: - `review-validation.ts`: split the file read. `assertLineRange` now compares against the full on-disk contents (line ranges are factual checks against committed code, not against what the reviewer saw). `assertQuote` keeps using the truncated copy so the existing hallucinated-quote guard still fires. - `prompt.ts`: raise `REVIEW_PROMPT_FILE_CHAR_LIMIT` from 24_000 to 250_000. Lets typical large source files (modules, monolith bots, service entrypoints) fit in one reviewer prompt without truncation. - `review-validation.test.ts`: bump the "beyond truncation" fixture from 24K to 250K so the test still exercises the intended case. In a real review run on a 228KB Python bot daemon, this took the unblocked feature count from 9/14 to 14/14 and surfaced 19 additional findings on the previously-stuck big-file features. --- src/prompt.ts | 2 +- src/review-validation.test.ts | 2 +- src/review-validation.ts | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/prompt.ts b/src/prompt.ts index e5078a1..46cea3c 100644 --- a/src/prompt.ts +++ b/src/prompt.ts @@ -4,7 +4,7 @@ import { ClawpatchConfig, FeatureRecord, FindingRecord, ProjectRecord } from "./ export type ReviewMode = "default" | "deslopify"; -export const REVIEW_PROMPT_FILE_CHAR_LIMIT = 24_000; +export const REVIEW_PROMPT_FILE_CHAR_LIMIT = 250_000; export type ReviewPromptFileRole = "owned" | "context"; diff --git a/src/review-validation.test.ts b/src/review-validation.test.ts index 27db552..d61f5e0 100644 --- a/src/review-validation.test.ts +++ b/src/review-validation.test.ts @@ -106,7 +106,7 @@ describe("validateReviewOutput", () => { it("rejects evidence that only exists beyond the truncated prompt text", async () => { const root = await fixtureRoot("clawpatch-review-validation-truncated-"); - await writeFixture(root, "src/index.ts", `${"a".repeat(24_000)}\nconst value = 'TODO_TAIL';\n`); + await writeFixture(root, "src/index.ts", `${"a".repeat(250_000)}\nconst value = 'TODO_TAIL';\n`); await expect( validateReviewOutput( diff --git a/src/review-validation.ts b/src/review-validation.ts index 7566a70..81ef30c 100644 --- a/src/review-validation.ts +++ b/src/review-validation.ts @@ -27,9 +27,12 @@ export async function validateReviewOutput( if (promptFile === undefined || !promptFile.readable) { throwMalformed(`evidence file was not readable in review context: ${evidence.path}`); } - const contents = await fileContents(root, evidence.path, promptFile.truncated, cache); - assertLineRange(contents, evidence); - assertQuote(contents, evidence); + const fullContents = await fileContents(root, evidence.path, false, cache); + const promptContents = promptFile.truncated + ? await fileContents(root, evidence.path, true, cache) + : fullContents; + assertLineRange(fullContents, evidence); + assertQuote(promptContents, evidence); } } return { ...output, findings };