{"record":{"id":"6754a19438814c1d","repo":"Hmbown/CodeWhale","slug":"complete-pr-review-requires-passes-at-max-chars-characters","errorCode":null,"errorMessage":"Complete PR review requires {} passes at {max_chars} characters per pass, but max_passes is {max_passes}. No review was run or posted. Opt in with max_passes/--max-passes of at least {} only after approving the provider spend and run duration.","messagePattern":"Complete PR review requires (.+?) passes at (.+?) characters per pass, but max_passes is (.+?)\\. No review was run or posted\\. Opt in with max_passes/--max-passes of at least (.+?) only after approving the provider spend and run duration\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/review.rs","lineNumber":385,"sourceCode":"        );\n    }\n\n    let mut grouped: Vec<Vec<PrReviewPiece<'_>>> = Vec::new();\n    let mut current: Vec<PrReviewPiece<'_>> = Vec::new();\n    let mut current_chars = 0;\n    for piece in pieces {\n        let piece_chars = super::review_pr::model_diff(&piece.diff).chars().count();\n        if !current.is_empty() && current_chars + piece_chars > max_chars {\n            grouped.push(std::mem::take(&mut current));\n            current_chars = 0;\n        }\n        current.push(piece);\n        current_chars += piece_chars;\n    }\n    if !current.is_empty() {\n        grouped.push(current);\n    }\n    anyhow::ensure!(\n        grouped.len() <= max_passes,\n        \"Complete PR review requires {} passes at {max_chars} characters per pass, but max_passes is {max_passes}. No review was run or posted. Opt in with max_passes/--max-passes of at least {} only after approving the provider spend and run duration.\",\n        grouped.len(),\n        grouped.len()\n    );\n\n    // The completeness guard, byte-for-byte as before: continuation parts\n    // replay the file header, so exactly those repeated headers are stripped\n    // and the rebuilt plan must equal the original diff.\n    anyhow::ensure!(\n        grouped\n            .iter()\n            .flatten()\n            .map(|piece| &piece.diff[piece.header_bytes..])\n            .collect::<String>()\n            == diff,\n        \"PR review plan did not preserve the complete diff byte-for-byte\"\n    );","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/tools/review.rs#L367-L403","documentation":"After grouping patches into passes, plan_pr_review checks that the required pass count fits within max_passes. This ensure! fires when the diff needs more passes than the caller authorized. The message explicitly asks the user to opt in to the higher pass count only after approving the provider spend and run duration, because passes multiply cost linearly.","triggerScenarios":"Calling plan_pr_review where grouped.len() (patches grouped by max_chars) exceeds max_passes — a large PR reviewed with a small per-pass budget and a low pass cap.","commonSituations":"Default max_passes=1 or 2 with a big multi-file PR; user lowered max_chars for cost control without raising max_passes; first run of review on a monorepo-scale PR.","solutions":["Raise max_passes/--max-passes to at least the required count printed in the message, after approving the cost","Increase max_chars so fewer passes are needed","Review the PR in smaller chunks (split the PR) instead of one full review"],"exampleFix":"// before\nplan_pr_review(diff, view, 20_000, 2)? // needs 5 passes\n// after\nplan_pr_review(diff, view, 20_000, 5)? // budget approved for 5 passes","handlingStrategy":"validation","validationCode":"if estimated_passes(diff, max_chars) > max_passes { confirm_spend_with_user(estimated_passes)?; }","typeGuard":null,"tryCatchPattern":"match plan_pr_review(...) { Err(e) if e.to_string().contains(\"requires\") && e.to_string().contains(\"passes\") => escalate_pass_budget_with_approval(e), other => other }","preventionTips":["Estimate pass count before running and require explicit cost approval","Keep max_chars and max_passes configured together (budget = chars x passes)","Split very large PRs instead of multiplying passes"],"tags":["budget","configuration","cost-control"],"backgroundTag":"value-out-of-range","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}