diff --git a/action.yml b/action.yml index e419f98..e8c517c 100644 --- a/action.yml +++ b/action.yml @@ -99,6 +99,10 @@ inputs: description: "Override reasoning effort for review flows (passed to Droid Exec as --reasoning-effort). If empty and review_model is also empty, the action defaults internally to gpt-5.2 at high reasoning." required: false default: "high" + validator_model: + description: "Override the model used for the validator pass (phase 2) of code review. If empty, falls back to review_model. Only applies when review_use_validator is true." + required: false + default: "" review_use_validator: description: "Enable two-pass review: generate candidate comments to JSON, then validate and post only approved ones." required: false @@ -347,6 +351,7 @@ runs: REVIEW_CANDIDATES_PATH: ${{ inputs.review_candidates_path }} DROID_COMMENT_ID: ${{ steps.prepare.outputs.droid_comment_id }} REVIEW_MODEL: ${{ inputs.review_model }} + VALIDATOR_MODEL: ${{ inputs.validator_model }} REASONING_EFFORT: ${{ inputs.reasoning_effort }} - name: Run Droid Exec (validator) diff --git a/src/create-prompt/templates/review-candidates-prompt.ts b/src/create-prompt/templates/review-candidates-prompt.ts index 1ee761a..5bdedbc 100644 --- a/src/create-prompt/templates/review-candidates-prompt.ts +++ b/src/create-prompt/templates/review-candidates-prompt.ts @@ -194,9 +194,21 @@ Write output to \`${reviewCandidatesPath}\` using this exact schema: - **comments**: Array of comment objects - \`path\`: Relative file path (e.g., "src/index.ts") - \`body\`: Comment text starting with priority tag [P0|P1|P2], then title, then 1 paragraph explanation + If you have **high confidence** a fix will address the issue and won’t break CI, append a GitHub suggestion block: + + \`\`\`suggestion + + \`\`\` + + **Suggestion rules:** + - Keep suggestion blocks ≤ 100 lines + - Preserve exact leading whitespace + - Use RIGHT-side anchors only; do not include removed/LEFT-side lines + - For insert-only suggestions, repeat the anchor line unchanged, then append new lines - \`line\`: Target line number (single-line) or end line number (multi-line). Must be ≥ 0. - \`startLine\`: \`null\` for single-line comments, or start line number for multi-line comments - - \`side\`: "RIGHT" for new/modified code (default), "LEFT" only for removed code + - \`side\`: "RIGHT" for new/modified code (default). Use "LEFT" only for removed code **without** suggestions. + If you include a suggestion block, choose a RIGHT-side anchor and keep it unchanged so the validator can reuse it. - \`commit_id\`: "${prHeadSha}" - **reviewSummary**: diff --git a/src/create-prompt/templates/review-prompt.ts b/src/create-prompt/templates/review-prompt.ts index 3216ce9..86b02f9 100644 --- a/src/create-prompt/templates/review-prompt.ts +++ b/src/create-prompt/templates/review-prompt.ts @@ -316,6 +316,22 @@ One short paragraph explaining *why* this is a bug and *how* it manifests. * Code snippets ≤3 lines, Markdown fenced * Matter-of-fact, non-accusatory tone +### Suggestion blocks (when applicable) + +If you have **high confidence** a fix will address the issue and won’t break CI, include a GitHub suggestion block after the explanation: + +\`\`\`suggestion + +\`\`\` + +Rules: +* Only include a suggestion when the fix is clear and high-confidence +* Keep the suggestion minimal and scoped to the reported line range +* Do not exceed 100 lines in a suggestion block +* Preserve exact leading whitespace of replaced lines +* Use RIGHT-side anchors only; do not include removed/LEFT-side lines +* For insert-only suggestions, repeat the anchor line unchanged, then append new lines + --- ## Phase 3: Submit Review @@ -333,6 +349,7 @@ One short paragraph explaining *why* this is a bug and *how* it manifests. * Anchor using **path + side + line** * RIGHT = new/modified code, LEFT = removed code * Line numbers must correspond to the chosen side + * If you include a suggestion, keep the anchor on RIGHT-side lines; for insert-only suggestions, repeat the anchor line unchanged, then append new lines * Use \`github_pr___submit_review\` for the summary * Use \`github_pr___delete_comment\` or \`github_pr___minimize_comment\` for outdated "no issues" comments * Use \`github_pr___reply_to_comment\` to acknowledge resolved issues diff --git a/src/create-prompt/templates/review-validator-prompt.ts b/src/create-prompt/templates/review-validator-prompt.ts index f4c124a..e754f4a 100644 --- a/src/create-prompt/templates/review-validator-prompt.ts +++ b/src/create-prompt/templates/review-validator-prompt.ts @@ -106,6 +106,20 @@ Reject if: * It's stylistic / naming / formatting * It's not anchored to a valid changed line * It's already reported (dedupe against existing comments) +* The anchor (path/side/line/startLine) would need to change to make the suggestion work — reject instead + +### Deduplication (STRICT) + +Before approving a candidate, check for duplicates: +1. **Among candidates**: If two or more candidates describe the same underlying bug (same root cause, even if anchored to different lines or worded differently), approve only the ONE with the best anchor and clearest explanation. Reject the rest with reason "duplicate of candidate N". +2. **Against existing comments**: If a candidate repeats an issue already covered by an existing PR comment (from \`${commentsPath}\`), reject it with reason "already reported in existing comments". +3. Same file + overlapping line range + same issue = duplicate, even if the body text differs. + +Suggestion block rules (minimal): +* Preserve exact leading whitespace and keep blocks ≤ 100 lines +* Use RIGHT-side anchors only; do not include removed/LEFT-side lines +* For insert-only suggestions, repeat the anchor line unchanged, then append new lines +* Do not change the anchor fields (path/side/line/startLine) from the candidate — only edit the body When rejecting, write a concise reason. diff --git a/src/tag/commands/review-validator.ts b/src/tag/commands/review-validator.ts index 96f8ea8..40d40de 100644 --- a/src/tag/commands/review-validator.ts +++ b/src/tag/commands/review-validator.ts @@ -103,11 +103,13 @@ export async function prepareReviewValidatorMode({ droidArgParts.push(`--enabled-tools "${allowedTools.join(",")}"`); droidArgParts.push('--tag "code-review"'); + const validatorModel = process.env.VALIDATOR_MODEL?.trim(); const reviewModel = process.env.REVIEW_MODEL?.trim(); + const modelToUse = validatorModel || reviewModel; const reasoningEffort = process.env.REASONING_EFFORT?.trim(); - if (reviewModel) { - droidArgParts.push(`--model "${reviewModel}"`); + if (modelToUse) { + droidArgParts.push(`--model "${modelToUse}"`); } if (reasoningEffort) { droidArgParts.push(`--reasoning-effort "${reasoningEffort}"`);