feat(FIR-7): Approve PR CTA for clean reviews - #11
Conversation
When the review pipeline completes with zero findings, display green
"Approve PR" and "Approve PR + Post to GitHub" buttons instead of the
findings submission flow.
- approval.ts: add formatApprovalComment() — clean LGTM comment body
- finalize/route.ts: accept empty decisions array + approve flag; routes
to formatApprovalComment when approve=true
- ReviewShell.tsx: handleApprove() posts { decisions:[], approve:true };
CTA shows when status=done and total=0
Co-authored-by: Cursor <cursoragent@cursor.com>
- Findings: remove local-only Submit, keep single 'Submit + Post to GitHub' - Clean review: remove local-only Approve, keep single '✓ Approve PR on GitHub' Local-only save will return in Phase 8 as 'Save Review' tied to history. Co-authored-by: Cursor <cursoragent@cursor.com>
AI PR Review — REQUEST_CHANGESTwo blocking correctness issues on the server side must be fixed before merging: the approve flag must be validated against actual findings count server-side, and the removal of min(1) on decisions must be compensated with an explicit guard to prevent silent empty-decisions submissions on the normal path. This PR introduces a clean-review approval path so that PRs with zero findings get a dedicated LGTM CTA and a well-formatted approval comment posted to GitHub, rather than falling through the normal findings submission flow. The UI and comment formatting work is solid, but the server-side route contains two correctness gaps that allow the approve path to be exploited or the normal path to silently succeed with empty input. These issues need to be addressed before merging. 🔴 Blocking Issuesapprove flag is not validated against actual findings count — approve path can be triggered on reviews with findings (
Removing min(1) from decisions array allows empty decisions to silently bypass the normal submission path (
|
…ivity 1. approve:true is rejected (400) if the cached review has any findings — prevents a false LGTM comment being posted on a review with issues 2. approve:false with empty decisions is rejected (400) — prevents the normal submission path from silently succeeding with no decisions Addresses two blocking findings from PR #11 harness review. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
AI PR Review — REQUEST_CHANGESThe BLOCKING issue of removing the local-only submit path for findings reviews must be addressed before merge, as it silently degrades existing functionality for users who do not want to post to GitHub. The potential unconditional This PR adds a clean-review LGTM approval path to the PR review harness, introducing an 🔴 Blocking Issuesapprove=true path skips building the decision map but still falls through to submission logic that uses it (
|
The approve=true and findings-submission flows are now completely separate branches after the mutual-exclusivity guards. buildSubmission is no longer called in the approve path — it was only needed for formatGitHubComment which is unused there. Both paths share the same GitHub posting and cache-invalidation logic but are otherwise independent, eliminating any risk of the submission object being built with wrong inputs. Addresses blocking finding from second PR #11 harness review. Co-authored-by: Cursor <cursoragent@cursor.com>
AI PR Review — REQUEST_CHANGESThree blocking correctness issues must be resolved: the misleading error message directing callers with findings toward a dead-end approve path, the ambiguity between API and UI in how 'total findings' is counted for the approval gate, and the silent metadata corruption when prUrl cannot be parsed during storeReview. Address these before merging. This PR adds an explicit approve path for zero-finding reviews to the finalize API route, updates the ReviewShell UI to surface an Approve CTA when no findings exist, introduces a 🔴 Blocking IssuesGuard
totalFindings counts review.suggestions and review.nits but a review with only suggestions/nits (no blockingIssues) will be blocked from approve even though those are non-blocking (
approve path proceeds even when postComment=true but prUrlParts is null — prUrl parsing result not checked before calling storeReview (
|
1. Second guard error message is now conditional: reviews with findings get "decisions must not be empty"; clean reviews get "Use approve:true" — prevents directing callers with findings into a dead-end approve path 2. prUrlParts null check is explicit: logs a warning and includes a 'warning' field in both approve and finalize responses when prUrl cannot be parsed, surfacing metadata degradation rather than silently storing placeholder values 3. Added comment clarifying totalFindings counts all severities intentionally, matching the UI's total===0 condition Addresses three blocking findings from third PR #11 harness review. Co-authored-by: Cursor <cursoragent@cursor.com>
AI PR Review — REQUEST_CHANGESThe PR introduces a valuable and well-structured approve path, but at least two blocking correctness issues must be fixed before merging: the potential TypeError crash from unguarded Phase 11 adds an 'approve' path for clean (zero-finding) reviews, introducing a new 🔴 Blocking IssuesSubmit button onClick changed from handleSubmit(false) to handleSubmit(true) — silent behavior change posts to GitHub on every submit click ( approve guard only checks totalFindings but nits could be zero while blockingIssues is non-zero — logic is correct but the approve path builds submission with
buildSubmission called with postComment=true in approve path but the submission object passed has submitting:false/submitted:true — if buildSubmission uses the postComment flag to set fields on the returned submission that are later read by storeReview, the stored submission may incorrectly record postComment state (
approve path does not call invalidateCachedReview on early-return error paths (
|
Co-authored-by: Cursor <cursoragent@cursor.com>
Problem
When the review pipeline completed with zero findings, the UI displayed "No findings — clean review!" with no further action available. There was no way to post a LGTM signal back to GitHub or record the clean outcome in history.
Separately, the findings submission UI offered two buttons — one that posted to GitHub and one that didn't — but the local-only submit had no real utility without a history feature to store it in.
Solution
Add a dedicated Approve PR on GitHub CTA for clean reviews, and simplify the findings submission flow to a single Submit + Post to GitHub button. Both actions always close the loop on GitHub.
Key Changes
src/agents/pr-review/approval.tsformatApprovalComment()— generates a clean LGTM comment body including the review summary and "What Looks Good" items, with anAPPROVED ✅headerapp/api/review/[id]/finalize/route.tsapprove: booleanflag to theFinalizeBodyschema;decisionsnow defaults to[]approve: truewith findings present → 400 (prevents false LGTM on a review with issues)approve: falsewith empty decisions → 400 (prevents silent no-op on the normal path)buildSubmissionandformatGitHubCommentare never called in the approve path{ status: 'approved' }; submit path returns{ status: 'finalized', summary }app/review/[id]/ReviewShell.tsxhandleApprove()posts{ decisions: [], approve: true, postComment: true }status === 'done' && total === 0Testing Notes
status: 'approved'in responsePOST /finalizewith{ approve: true }on a review with findings → expect 400POST /finalizewith{ decisions: [], approve: false }→ expect 400DRY_RUN=true→ confirm correct comment body returned without GitHub API call