-
-
Notifications
You must be signed in to change notification settings - Fork 759
feat: enhance PR review system to auto add tags #449
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
Add automatic GitHub label sync when PR reviews are posted: - AC: Approved - no blocking issues (can merge) - AC: Changes Requested - has critical/high findings - AC: Needs Re-review - new commits after review Changes: - Add pr-labels.ts with clean, modular label sync logic - Wire up syncPRLabel after posting reviews - Wire up markPRNeedsRereview when new commits detected - Fix handleApprove to use onPostReview instead of onPostComment - Add PR_LABEL_SYNC.md documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughAdds a new PR label management utility and integrates it into PR post-review and new-commits flows; review metadata sourcing changed to use persisted review records (e.g., Changes
Sequence DiagramsequenceDiagram
actor User
participant PRDetail as PRDetail (UI)
participant Handler as PR Handler
participant Labeler as PR Labeler
participant GitHub as GitHub API
User->>PRDetail: Click Approve / Auto-Approve
PRDetail->>Handler: onPostReview(findings or [])
Handler->>GitHub: POST review
GitHub-->>Handler: Review posted (200)
rect rgb(240,248,255)
Note over Handler,Labeler: Sync AC labels after review
Handler->>Labeler: syncPRLabel(token, repo, prNumber, status)
Labeler->>GitHub: GET /issues/:pr/labels
GitHub-->>Labeler: current labels
Labeler->>GitHub: DELETE old AC:* labels (parallel)
GitHub-->>Labeler: deletions acknowledged
Labeler->>GitHub: POST new AC: label
GitHub-->>Labeler: label added
end
Handler->>Handler: checkForNewCommits(review.reviewedCommitSha)
alt new commits exist after postedAt
Handler->>Labeler: markPRNeedsRereview(token, repo, prNumber)
Labeler->>GitHub: ensure `AC: Needs Re-review` label present
GitHub-->>Labeler: label ensured
end
Handler-->>PRDetail: complete
PRDetail-->>User: UI updated
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (3)apps/frontend/src/**/*.{ts,tsx,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
apps/frontend/src/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
apps/frontend/**/*.{ts,tsx}⚙️ CodeRabbit configuration file
Files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
🔇 Additional comments (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Thank you for your contribution! Before we can accept your PR, you need to sign our Contributor License Agreement (CLA). To sign the CLA, please comment on this PR with exactly: You can read the full CLA here: CLA.md Why do we need a CLA? Auto Claude is licensed under AGPL-3.0. The CLA ensures the project has proper licensing flexibility should we introduce additional licensing options in the future. You retain full copyright ownership of your contributions. I have read the CLA Document and I hereby sign the CLA 0 out of 2 committers have signed the CLA. |
Summary of ChangesHello @AlexMadera, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the PR review system by automating the application of GitHub labels to reflect the current review status. It introduces labels for "Approved", "Changes Requested", and "Needs Re-review", providing immediate visual feedback on the state of a PR. This change centralizes label management and integrates seamlessly with the existing review posting mechanisms, simplifying the workflow for both reviewers and authors. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a great new feature to automatically add labels to pull requests based on the review status. The implementation is solid, with new utility functions for handling labels and updates to the main PR handler and frontend component.
I've identified a few areas for improvement:
- A logic bug in how review statuses are mapped to labels.
- Opportunities to refactor the new label utilities for better maintainability and efficiency.
- Some areas where code could be made more robust by using existing helper functions and idiomatic React patterns.
My detailed comments are below. Overall, this is a valuable addition and these changes will help make the new feature more robust and easier to maintain.
| function mapStatusToLabel(status: ReviewStatus): string { | ||
| if (status === 'request_changes') { | ||
| return AC_LABELS.CHANGES_REQUESTED; | ||
| } | ||
| return AC_LABELS.APPROVED; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation of mapStatusToLabel incorrectly maps a review with only comments (status === 'comment') to the AC: Approved label. This could be misleading, as a PR with non-blocking suggestions would be marked as fully approved. A comment status should likely not result in an Approved label.
Consider returning null for comment and only applying a label if the function returns a non-null value. This would require a check in the calling function, syncPRLabel.
function mapStatusToLabel(status: ReviewStatus): string | null {
if (status === 'request_changes') {
return AC_LABELS.CHANGES_REQUESTED;
}
if (status === 'approve') {
return AC_LABELS.APPROVED;
}
return null;
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not misleading, this is more for documentation as tags. what happens inside PR review that is internal
| const newCommitCount = comparison.ahead_by || comparison.total_commits || 1; | ||
|
|
||
| // Sync "Needs Re-review" label if review was posted and has new commits | ||
| const hasPostedFindings = review.hasPostedFindings || (review as any).has_posted_findings; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line review.hasPostedFindings || (review as any).has_posted_findings is a defensive workaround for inconsistent property naming. It indicates that the review object is not being properly hydrated from the raw JSON file data.
The getReviewResult function already handles the mapping from snake_case (from the file) to camelCase. Instead of manually reading and parsing the file within this handler, you should reuse getReviewResult at the beginning of the handler to ensure the review object is correctly typed and structured. This will make the code cleaner, more robust, and remove the need for (review as any) type assertions and manual property mapping.
| export async function syncPRLabel( | ||
| token: string, | ||
| repo: string, | ||
| prNumber: number, | ||
| status: ReviewStatus | ||
| ): Promise<void> { | ||
| const newLabel = mapStatusToLabel(status); | ||
|
|
||
| try { | ||
| const currentLabels = await fetchCurrentLabels(token, repo, prNumber); | ||
| await removeOldACLabels(token, repo, prNumber, currentLabels, newLabel); | ||
| await addLabel(token, repo, prNumber, newLabel); | ||
| } catch { | ||
| // Label sync is non-critical, fail silently | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Marks PR as needing re-review after new commits are pushed. | ||
| */ | ||
| export async function markPRNeedsRereview( | ||
| token: string, | ||
| repo: string, | ||
| prNumber: number | ||
| ): Promise<void> { | ||
| try { | ||
| const currentLabels = await fetchCurrentLabels(token, repo, prNumber); | ||
| await removeOldACLabels(token, repo, prNumber, currentLabels, AC_LABELS.NEEDS_REREVIEW); | ||
| await addLabel(token, repo, prNumber, AC_LABELS.NEEDS_REREVIEW); | ||
| } catch { | ||
| // Label sync is non-critical, fail silently | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The functions syncPRLabel and markPRNeedsRereview contain very similar logic, leading to code duplication. This can be refactored into a single helper function.
Additionally, the current approach of removing old labels one by one can be inefficient. The GitHub API provides a PUT /repos/{owner}/{repo}/issues/{issue_number}/labels endpoint that replaces all labels on an issue in a single, atomic API call. This would be more efficient and robust.
Finally, swallowing errors silently in the catch block makes debugging difficult if label syncing fails. It would be better to log the error (e.g., using debugLog).
Consider creating a single helper function like setACLabel(..., newLabel) that:
- Fetches current labels.
- Constructs a new list of labels (non-AC labels + the new AC label).
- Uses the
PUTmethod to update the labels in one call. - Wraps the logic in a
try...catchthat logs any errors.
Both syncPRLabel and markPRNeedsRereview would then call this helper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
valid, DRY principle
| const success = await onPostReview([]); | ||
| if (success) { | ||
| setTimeout(() => checkForNewCommits(), 500); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using setTimeout with a fixed delay to wait for state updates is brittle and not an idiomatic React pattern. It can lead to race conditions or unnecessary delays. The delay is likely intended to ensure checkForNewCommits runs after the component has re-rendered with updated state from onPostReview.
A more robust approach is to use a useEffect hook to react to state changes. For example, you could introduce a state variable that triggers the check.
const [shouldCheckCommits, setShouldCheckCommits] = useState(false);
useEffect(() => {
if (shouldCheckCommits) {
checkForNewCommits();
setShouldCheckCommits(false);
}
}, [shouldCheckCommits, checkForNewCommits]);
// In handleApprove:
const success = await onPostReview([]);
if (success) {
setShouldCheckCommits(true);
}This ensures checkForNewCommits is called reliably after the state has been updated and the component has re-rendered, without relying on arbitrary timers. This pattern should be applied in handlePostReview and handleAutoApprove as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Valid
AndyMik90
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 Auto Claude PR Review
Merge Verdict: 🟠 NEEDS REVISION
3 issue(s) must be addressed (1 required, 2 recommended), 2 suggestions
Risk Assessment
| Factor | Level | Notes |
|---|---|---|
| Complexity | Low | Based on lines changed |
| Security Impact | None | Based on security findings |
| Scope Coherence | Good | Based on structural review |
Findings Summary
- High: 1 issue(s)
- Medium: 2 issue(s)
- Low: 2 issue(s)
Generated by Auto Claude PR Review
Findings (5 selected of 5 total)
🟠 [HIGH] mapStatusToLabel incorrectly maps 'comment' status to APPROVED
📁 apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts:24
The function only checks for 'request_changes' and falls through to APPROVED for any other status, including 'comment'. A review with status 'comment' (reviewer left comments without approving or requesting changes) should NOT be labeled as 'AC: Approved'. This is semantically incorrect and could mislead maintainers about the actual review state.
Suggested fix:
Add explicit handling for 'comment' status. Either: (1) return a new label like AC_LABELS.COMMENTED, (2) return AC_LABELS.NEEDS_REREVIEW for 'comment' status, or (3) only return APPROVED when status explicitly equals 'approve'. Recommended fix:
function mapStatusToLabel(status: ReviewStatus): string {
switch (status) {
case 'approve':
return AC_LABELS.APPROVED;
case 'request_changes':
return AC_LABELS.CHANGES_REQUESTED;
case 'comment':
default:
return AC_LABELS.NEEDS_REREVIEW;
}
}
🟡 [MEDIUM] setTimeout callback may fire after component unmount
📁 apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx:378
The setTimeout at line 378-380 calls checkForNewCommits() after 500ms without cleanup on component unmount. If the user navigates away within that window, the callback will attempt to update state on an unmounted component. While React 18 handles this more gracefully, it's still a memory leak pattern and can cause console warnings.
Suggested fix:
Add cleanup for the timeout using a ref and useEffect cleanup:
const postReviewTimeoutRef = useRef<NodeJS.Timeout | null>(null);
// In the handler:
if (success) {
postReviewTimeoutRef.current = setTimeout(() => checkForNewCommits(), 500);
}
// In useEffect cleanup:
useEffect(() => {
return () => {
if (postReviewTimeoutRef.current) {
clearTimeout(postReviewTimeoutRef.current);
}
};
}, []);
🟡 [MEDIUM] Type safety bypass with 'as any' for snake_case field access
📁 apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts:1000
The code uses review.hasPostedFindings || (review as any).has_posted_findings to handle both camelCase and snake_case property names. This bypasses TypeScript's type checking and indicates the review object isn't being normalized properly. The existing getReviewResult() function already handles this conversion at lines 496-539, but it's not being used here.
Suggested fix:
Use the existing getReviewResult() function instead of manually parsing the JSON, which already handles snake_case to camelCase conversion:
const review = getReviewResult(project, prNumber);
if (!review) {
return { hasNewCommits: false, newCommitCount: 0 };
}
const hasPostedFindings = review.hasPostedFindings;
🔵 [LOW] Code duplication between syncPRLabel and markPRNeedsRereview
📁 apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts:92
Both functions share nearly identical logic: fetch current labels, remove old AC labels, add new label, with silent try/catch. This duplication means any bug fix or improvement needs to be applied twice.
Suggested fix:
Extract shared logic into a private helper function:
async function updateACLabel(
token: string,
repo: string,
prNumber: number,
newLabel: string
): Promise<void> {
try {
const currentLabels = await fetchCurrentLabels(token, repo, prNumber);
await removeOldACLabels(token, repo, prNumber, currentLabels, newLabel);
await addLabel(token, repo, prNumber, newLabel);
} catch {
// Label sync is non-critical, fail silently
}
}
export async function syncPRLabel(...): Promise<void> {
await updateACLabel(token, repo, prNumber, mapStatusToLabel(status));
}
export async function markPRNeedsRereview(...): Promise<void> {
await updateACLabel(token, repo, prNumber, AC_LABELS.NEEDS_REREVIEW);
}
🔵 [LOW] Silent error swallowing without logging
📁 apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts:100
The catch blocks in both syncPRLabel and markPRNeedsRereview silently swallow all errors without any logging. While label sync is noted as 'non-critical', having no visibility into failures makes debugging difficult. Network errors, permission issues, or rate limiting would be invisible.
Suggested fix:
Add optional debug logging to help diagnose issues:
} catch (error) {
// Label sync is non-critical - log for debugging but don't throw
console.debug('Label sync failed:', error instanceof Error ? error.message : error);
}
Or use the existing debugLog utility if available in this module.
This review was generated by Auto Claude.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (4)
apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts (1)
1321-1398: UsegetReviewResultinstead of manual file reading and type assertions.The code manually reads and parses the review file, then uses defensive type assertions like
(review as any).has_posted_findingsto access snake_case properties. This bypasses TypeScript's type safety and is inconsistent with the codebase pattern.The
getReviewResultfunction (lines 510-554) already handles snake_case to camelCase conversion properly. Refactor this handler to use it:🔎 Proposed refactor
- const result = await withProjectOrNull(projectId, async (project) => { - // Check if review exists and has reviewed_commit_sha - const githubDir = path.join(project.path, '.auto-claude', 'github'); - const reviewPath = path.join(githubDir, 'pr', `review_${prNumber}.json`); - - let review: PRReviewResult; - try { - const rawData = fs.readFileSync(reviewPath, 'utf-8'); - const sanitizedData = sanitizeNetworkData(rawData); - review = JSON.parse(sanitizedData); - } catch { - // File doesn't exist or couldn't be read - return { hasNewCommits: false, newCommitCount: 0 }; - } - - // Convert snake_case to camelCase for the field - const reviewedCommitSha = review.reviewedCommitSha || (review as any).reviewed_commit_sha; + const result = await withProjectOrNull(projectId, async (project) => { + const review = getReviewResult(project, prNumber); + if (!review) { + return { hasNewCommits: false, newCommitCount: 0 }; + } + + const reviewedCommitSha = review.reviewedCommitSha; if (!reviewedCommitSha) { debugLog('No reviewedCommitSha in review', { prNumber }); return { hasNewCommits: false, newCommitCount: 0 }; } // ... rest of the logic ... - const postedAt = review.postedAt || (review as any).posted_at; + const postedAt = review.postedAt; // ... rest of the logic ... - const hasPostedFindings = review.hasPostedFindings || (review as any).has_posted_findings; + const hasPostedFindings = review.hasPostedFindings;Based on past review comments indicating this defensive pattern should be addressed.
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx (1)
446-458: ReplacesetTimeoutwith proper React state management.The use of
setTimeoutto delaycheckForNewCommitsafter posting is brittle and can cause race conditions. This pattern repeats in bothhandleApproveandhandleAutoApprove(line 478).🔎 Recommended fix using useEffect
+ const [shouldCheckCommits, setShouldCheckCommits] = useState(false); + + useEffect(() => { + if (shouldCheckCommits) { + checkForNewCommits(); + setShouldCheckCommits(false); + } + }, [shouldCheckCommits, checkForNewCommits]); const handleApprove = async () => { if (!reviewResult) return; setIsPosting(true); try { const success = await onPostReview([]); if (success) { - setTimeout(() => checkForNewCommits(), 500); + setShouldCheckCommits(true); } } finally { setIsPosting(false); } };Apply the same pattern to
handleAutoApproveat line 478.Based on past review comments indicating this pattern should be addressed.
apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts (2)
93-125: Eliminate code duplication with a shared helper function.Both
syncPRLabelandmarkPRNeedsRereviewfollow the same pattern: fetch current labels, remove old AC labels, add new label. This violates the DRY principle and makes maintenance harder.🔎 Proposed refactor
+async function setACLabel( + token: string, + repo: string, + prNumber: number, + newLabel: string +): Promise<void> { + try { + const currentLabels = await fetchCurrentLabels(token, repo, prNumber); + await removeOldACLabels(token, repo, prNumber, currentLabels, newLabel); + await addLabel(token, repo, prNumber, newLabel); + } catch (error) { + // Label sync is non-critical, but log for debugging + debugLog('Failed to set AC label', { prNumber, newLabel, error }); + } +} export async function syncPRLabel( token: string, repo: string, prNumber: number, status: ReviewStatus ): Promise<void> { const newLabel = mapStatusToLabel(status); - try { - const currentLabels = await fetchCurrentLabels(token, repo, prNumber); - await removeOldACLabels(token, repo, prNumber, currentLabels, newLabel); - await addLabel(token, repo, prNumber, newLabel); - } catch { - // Label sync is non-critical, fail silently - } + await setACLabel(token, repo, prNumber, newLabel); } export async function markPRNeedsRereview( token: string, repo: string, prNumber: number ): Promise<void> { - try { - const currentLabels = await fetchCurrentLabels(token, repo, prNumber); - await removeOldACLabels(token, repo, prNumber, currentLabels, AC_LABELS.NEEDS_REREVIEW); - await addLabel(token, repo, prNumber, AC_LABELS.NEEDS_REREVIEW); - } catch { - // Label sync is non-critical, fail silently - } + await setACLabel(token, repo, prNumber, AC_LABELS.NEEDS_REREVIEW); }Note: You'll need to import
debugLogfrom the logger utility. This also addresses the silent error handling issue raised in past reviews.Based on past review comments where you acknowledged this as valid DRY principle violation.
75-87: Consider atomic label updates for better performance and reliability.The current implementation removes old AC labels one by one (lines 84-86), then adds the new label, requiring multiple API calls. GitHub's API supports
PUT /repos/{owner}/{repo}/issues/{issue_number}/labelsto replace all labels atomically in a single call.🔎 Proposed atomic update approach
-async function removeOldACLabels( +async function replaceACLabel( token: string, repo: string, prNumber: number, currentLabels: string[], - keepLabel: string + newLabel: string ): Promise<void> { - const labelsToRemove = getACLabelsToRemove(currentLabels, keepLabel); - - await Promise.allSettled( - labelsToRemove.map((label) => removeLabel(token, repo, prNumber, label)) - ); + // Keep all non-AC labels and add the new AC label + const nonACLabels = currentLabels.filter(label => !label.startsWith(LABEL_PREFIX)); + const updatedLabels = [...nonACLabels, newLabel]; + + // Replace all labels atomically + await githubFetch(token, `/repos/${repo}/issues/${prNumber}/labels`, { + method: 'PUT', + body: JSON.stringify({ labels: updatedLabels }), + }); } async function setACLabel( token: string, repo: string, prNumber: number, newLabel: string ): Promise<void> { try { const currentLabels = await fetchCurrentLabels(token, repo, prNumber); - await removeOldACLabels(token, repo, prNumber, currentLabels, newLabel); - await addLabel(token, repo, prNumber, newLabel); + await replaceACLabel(token, repo, prNumber, currentLabels, newLabel); } catch (error) { debugLog('Failed to set AC label', { prNumber, newLabel, error }); } }This reduces API calls from 3+ to 2 and avoids race conditions where other processes might be updating labels concurrently.
Based on past review comments suggesting this approach.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
apps/frontend/src/main/ipc-handlers/github/pr-handlers.tsapps/frontend/src/main/ipc-handlers/github/utils/pr-labels.tsapps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx
🧰 Additional context used
📓 Path-based instructions (3)
apps/frontend/src/**/*.{ts,tsx,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always use i18n translation keys for all user-facing text in the frontend instead of hardcoded strings
Files:
apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.tsapps/frontend/src/renderer/components/github-prs/components/PRDetail.tsxapps/frontend/src/main/ipc-handlers/github/pr-handlers.ts
apps/frontend/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use
useTranslation()hook with namespace prefixes (e.g., 'navigation:items.key') for accessing translation strings in React components
Files:
apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.tsapps/frontend/src/renderer/components/github-prs/components/PRDetail.tsxapps/frontend/src/main/ipc-handlers/github/pr-handlers.ts
apps/frontend/**/*.{ts,tsx}
⚙️ CodeRabbit configuration file
apps/frontend/**/*.{ts,tsx}: Review React patterns and TypeScript type safety.
Check for proper state management and component composition.
Files:
apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.tsapps/frontend/src/renderer/components/github-prs/components/PRDetail.tsxapps/frontend/src/main/ipc-handlers/github/pr-handlers.ts
🧬 Code graph analysis (2)
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx (1)
apps/backend/runners/github/errors.py (1)
success(483-484)
apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts (1)
apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts (2)
syncPRLabel(93-108)markPRNeedsRereview(113-125)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: CodeQL (python)
- GitHub Check: CodeQL (javascript-typescript)
- GitHub Check: Cursor Bugbot
🔇 Additional comments (4)
apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts (2)
30-30: LGTM: PR label utilities imported correctly.The import of
syncPRLabelandmarkPRNeedsRereviewfrom the new pr-labels module is clean and properly integrated into the review flow.
1069-1072: LGTM: Label sync integrated correctly after posting review.The label synchronization is properly placed after successful review posting, with appropriate logging for debugging. The non-critical nature of label sync (fails silently in the utility) ensures the main review flow isn't disrupted by labeling failures.
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx (2)
463-483: LGTM: Two-step approval flow is logical.The auto-approve flow correctly posts LOW severity findings as non-blocking suggestions first, then posts an empty approval review. The early return on failure (line 471) ensures the approval doesn't proceed if suggestion posting fails.
The
setTimeoutissue at line 478 is addressed in the previous comment.
460-462: Verify i18n compliance for posted review content.The comment indicates GitHub PR comments are intentionally in English. However, the coding guidelines require i18n translation keys for all user-facing text in the frontend.
Verify whether the review body generated in
pr-handlers.ts(lines 990-1014) should use i18n translation keys instead of hardcoded English strings. If the team has decided that GitHub review comments should remain in English for international collaboration, document this exception in the coding guidelines.As per coding guidelines requiring i18n for user-facing text in frontend.
| env: getAugmentedEnv(), | ||
| }); | ||
| debugLog('PR merged successfully', { prNumber }); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Remove extraneous blank line.
This extra blank line appears to be unintentional and doesn't add to readability.
🔎 Proposed fix
});
debugLog('PR merged successfully', { prNumber });
-
return true;🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts around line 1233,
remove the single extraneous blank line so there is no unintended empty line at
that location; ensure surrounding code lines are adjacent with proper spacing
and formatting preserved.
| return AC_LABELS.CHANGES_REQUESTED; | ||
| } | ||
| return AC_LABELS.APPROVED; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment status incorrectly labeled as Approved
The mapStatusToLabel function maps both 'approve' and 'comment' status to the APPROVED label. When a review has non-blocking findings, the overallStatus is set to 'comment' (as seen in pr-handlers.ts line 1020), but this gets labeled as "AC: Approved". In GitHub semantics, a COMMENT review explicitly does not approve the PR - it only leaves feedback. Labeling such reviews as "Approved" is misleading and could cause PRs to appear approved when they only received comments.
| if (hasPostedFindings) { | ||
| await markPRNeedsRereview(config.token, config.repo, prNumber); | ||
| debugLog('Added needs re-review label', { prNumber, newCommitCount }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-review label ignores commits-after-posting check
The markPRNeedsRereview call only checks hasPostedFindings but ignores the hasCommitsAfterPosting flag that's computed earlier. This causes the "Needs Re-review" label to be added whenever there are new commits since the reviewed commit, even if those commits were pushed before the review was posted. The code computes hasCommitsAfterPosting specifically to distinguish these cases (lines 1371-1389), but this value isn't used in the condition for adding the re-review label.
AndyMik90
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 Auto Claude PR Review
Merge Verdict: 🟠 NEEDS REVISION
3 issue(s) must be addressed (1 required, 2 recommended), 2 suggestions
Risk Assessment
| Factor | Level | Notes |
|---|---|---|
| Complexity | Low | Based on lines changed |
| Security Impact | None | Based on security findings |
| Scope Coherence | Good | Based on structural review |
Findings Summary
- High: 1 issue(s)
- Medium: 2 issue(s)
- Low: 2 issue(s)
Generated by Auto Claude PR Review
Findings (5 selected of 5 total)
🟠 [HIGH] 'comment' status incorrectly maps to 'Approved' label
📁 apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts:24
The mapStatusToLabel function only checks for 'request_changes' status and defaults everything else (including 'comment') to 'AC: Approved'. A 'comment'-only review indicates neither approval nor rejection - labeling it as 'Approved' misleads contributors about their PR status. This was independently flagged by 3 AI reviewers (Gemini, CodeRabbit, Cursor) with high confidence.
Suggested fix:
Add explicit handling for 'comment' status. Either map to a new label like 'AC: Reviewed' or 'AC: Feedback', or only apply the 'Approved' label when status is explicitly 'approve':
function mapStatusToLabel(status: ReviewStatus): string {
switch (status) {
case 'approve': return AC_LABELS.APPROVED;
case 'request_changes': return AC_LABELS.CHANGES_REQUESTED;
case 'comment': return AC_LABELS.REVIEWED; // New label
}
}
🟡 [MEDIUM] Re-review label ignores hasCommitsAfterPosting check
📁 apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts:1393
The markPRNeedsRereview call only checks hasPostedFindings but ignores hasCommitsAfterPosting. If new commits happened BEFORE findings were posted (not after), the review already covered those commits and re-review label may be inappropriate. The codebase has sophisticated logic distinguishing these cases (see lines 1363-1383) but the label logic doesn't use it.
Suggested fix:
Add hasCommitsAfterPosting to the condition:
if (hasPostedFindings && hasCommitsAfterPosting) {
await markPRNeedsRereview(config.token, config.repo, prNumber);
debugLog('Added needs re-review label', { prNumber, newCommitCount });
}
🟡 [MEDIUM] Type casting workaround bypasses type safety
📁 apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts:1393
The code uses (review as any).has_posted_findings as a fallback for snake_case property from Python backend. While this is a documented pattern in the codebase, it bypasses TypeScript's type safety and could hide bugs if the field name changes. The existing getReviewResult() function on line 509 already handles this transformation properly.
Suggested fix:
Use the existing `getReviewResult(project, prNumber)` function instead of duplicating the JSON parsing logic. This function already handles snake_case to camelCase transformation correctly.
🔵 [LOW] Code duplication between syncPRLabel and markPRNeedsRereview
📁 apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts:92
The functions syncPRLabel (lines 92-107) and markPRNeedsRereview (lines 112-125) have nearly identical structure: fetch labels, remove old AC labels, add new label, catch silently. This duplication makes maintenance harder and violates DRY principle.
Suggested fix:
Extract common logic into a helper function:
async function updateACLabel(token, repo, prNumber, newLabel): Promise<void> {
try {
const currentLabels = await fetchCurrentLabels(token, repo, prNumber);
await removeOldACLabels(token, repo, prNumber, currentLabels, newLabel);
await addLabel(token, repo, prNumber, newLabel);
} catch {
// Label sync is non-critical
}
}
Then simplify both functions to call this helper.
🔵 [LOW] setTimeout for state synchronization is brittle
📁 apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx:452
Using setTimeout(() => checkForNewCommits(), 500) after async operation completion is a fragile pattern. The 500ms delay is arbitrary and may not match actual backend timing. If backend takes longer, check fails silently; if faster, time is wasted. Note: AI triage marked this as intentional pattern - severity reduced accordingly.
Suggested fix:
Have `onPostReview()` return the updated review state directly, eliminating the need to re-fetch. Alternatively, use a callback pattern where the backend signals completion, or implement retry with exponential backoff.
This review was generated by Auto Claude.
…fety - Add 'AC: Reviewed' label for comment-only reviews instead of incorrectly mapping to 'Approved' - Add hasCommitsAfterPosting check before applying re-review label - Use getReviewResult() for proper snake_case to camelCase transformation instead of type casting workarounds - Extract updateACLabel helper to reduce duplication between syncPRLabel and markPRNeedsRereview - Remove brittle setTimeout delays for state synchronization, relying on React's useEffect to trigger checkForNewCommits when reviewResult changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (2)
apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts (1)
1239-1239: Remove extraneous blank line.This blank line was flagged in a previous review and should be removed for consistency.
apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts (1)
99-112: Consider logging label sync errors for observability.While label syncing is correctly treated as non-critical (silently failing won't break core functionality), completely swallowing errors makes debugging difficult if label sync consistently fails. Consider logging errors to help diagnose issues while maintaining the non-blocking behavior.
🔎 Proposed fix
+import { debugLog } from './logger'; + /** * Updates AC label on a PR: removes old AC labels and applies the new one. * Label sync is non-critical, so errors are silently ignored. */ async function updateACLabel( token: string, repo: string, prNumber: number, newLabel: string ): Promise<void> { try { const currentLabels = await fetchCurrentLabels(token, repo, prNumber); await removeOldACLabels(token, repo, prNumber, currentLabels, newLabel); await addLabel(token, repo, prNumber, newLabel); - } catch { + } catch (error) { - // Label sync is non-critical, fail silently + // Label sync is non-critical, fail silently but log for debugging + debugLog('PR label sync failed', { prNumber, newLabel, error: error instanceof Error ? error.message : error }); } }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
apps/frontend/src/main/ipc-handlers/github/pr-handlers.tsapps/frontend/src/main/ipc-handlers/github/utils/pr-labels.tsapps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx
🧰 Additional context used
📓 Path-based instructions (3)
apps/frontend/src/**/*.{ts,tsx,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always use i18n translation keys for all user-facing text in the frontend instead of hardcoded strings
Files:
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsxapps/frontend/src/main/ipc-handlers/github/pr-handlers.tsapps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts
apps/frontend/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use
useTranslation()hook with namespace prefixes (e.g., 'navigation:items.key') for accessing translation strings in React components
Files:
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsxapps/frontend/src/main/ipc-handlers/github/pr-handlers.tsapps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts
apps/frontend/**/*.{ts,tsx}
⚙️ CodeRabbit configuration file
apps/frontend/**/*.{ts,tsx}: Review React patterns and TypeScript type safety.
Check for proper state management and component composition.
Files:
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsxapps/frontend/src/main/ipc-handlers/github/pr-handlers.tsapps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts
🧬 Code graph analysis (2)
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx (1)
apps/backend/runners/github/errors.py (1)
success(483-484)
apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts (2)
apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts (2)
syncPRLabel(118-125)markPRNeedsRereview(130-136)apps/frontend/src/main/ipc-handlers/github/utils/logger.ts (1)
debugLog(29-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: CodeQL (javascript-typescript)
- GitHub Check: CodeQL (python)
🔇 Additional comments (6)
apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts (2)
30-30: LGTM: Clean import of PR label utilities.The import statement correctly brings in the new label management functions that are used later in the file.
1075-1078: LGTM: PR label syncing properly integrated.The call to
syncPRLabelis correctly placed after successful review posting, ensuring GitHub PR labels stay in sync with the review status.apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx (3)
440-441: Excellent fix: Removed brittle setTimeout pattern.This directly addresses the previous review concern about using
setTimeoutto wait for state updates. The direct call tocheckForNewCommits()is more reliable and follows React best practices.
453-456: LGTM: Streamlined approval flow.The simplified approval flow posts an empty review (which triggers approval status) and then checks for new commits. This is cleaner than the previous approach of posting detailed approval comments.
462-485: LGTM: Well-structured auto-approval implementation.The refactored auto-approval logic is clean and defensive:
- Posts low-severity findings as non-blocking suggestions
- Early returns if posting findings fails
- Posts the approval via
onPostReview([])- Triggers commit check on success
This aligns well with the new streamlined approval flow.
apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts (1)
27-36: Well done: Resolved past concern about comment status labeling.The function now correctly maps
'comment'status toAC: Reviewedinstead ofAC: Approved, addressing the previous concern that comment-only reviews were being misleadingly labeled as approved.
| } else if (!review.postedAt) { | ||
| // If findings haven't been posted yet, any new commits should trigger follow-up | ||
| hasCommitsAfterPosting = true; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarify misleading comment about unposted findings.
The comment states "If findings haven't been posted yet, any new commits should trigger follow-up", but the condition on line 1388 requires review.hasPostedFindings to be true before adding the re-review label. This means the re-review label will NOT be added if findings haven't been posted yet, contradicting the comment.
The comment should be updated to clarify that hasCommitsAfterPosting = true is a safe default when posting date cannot be determined, rather than suggesting unposted findings should trigger re-review.
🔎 Proposed fix
- } else if (!review.postedAt) {
- // If findings haven't been posted yet, any new commits should trigger follow-up
- hasCommitsAfterPosting = true;
+ } else if (!review.postedAt) {
+ // Default to true if posting date is unavailable (safeguards against missing data)
+ hasCommitsAfterPosting = true;
}🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/github/pr-handlers.ts around lines 1381
to 1384, the inline comment incorrectly implies that unposted findings will
trigger a re-review label; instead update the comment to state that setting
hasCommitsAfterPosting = true is a conservative default when review.postedAt is
missing (posting date cannot be determined), and that the actual re-review label
logic still depends on review.hasPostedFindings being true elsewhere. Keep the
comment concise and accurate about intent so it no longer suggests unposted
findings cause label addition.
| async function removeOldACLabels( | ||
| token: string, | ||
| repo: string, | ||
| prNumber: number, | ||
| currentLabels: string[], | ||
| keepLabel: string | ||
| ): Promise<void> { | ||
| const labelsToRemove = getACLabelsToRemove(currentLabels, keepLabel); | ||
|
|
||
| await Promise.allSettled( | ||
| labelsToRemove.map((label) => removeLabel(token, repo, prNumber, label)) | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider using GitHub's PUT endpoint for atomic label updates.
The current implementation removes old AC labels individually (multiple DELETE calls) then adds the new one (POST). GitHub's PUT /repos/{owner}/{repo}/issues/{issue_number}/labels endpoint can replace all labels atomically in a single API call, which would be more efficient and reduce race conditions.
Current flow:
- Fetch labels (GET)
- Remove old AC labels (multiple DELETEs)
- Add new label (POST)
Suggested flow:
- Fetch labels (GET)
- Replace with new label list (single PUT)
This would reduce API calls from 3+ to 2 and make the operation atomic.
🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/github/utils/pr-labels.ts around lines 81
to 93, the current removeOldACLabels removes AC labels with multiple DELETEs
then adds the new one, which is inefficient and racy; instead, fetch current
labels, compute the new array by filtering out AC labels and adding the
keepLabel alongside any non-AC labels, then call GitHub’s PUT
/repos/{owner}/{repo}/issues/{issue_number}/labels endpoint once to replace all
labels atomically; update the function to build that label list and perform a
single authenticated PUT request (with proper error handling and retries) rather
than issuing multiple DELETE/POST calls.
…0/Auto-Claude into feat-pr-review-adds-tag
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx (1)
200-207: Consider logging refresh errors for debugging.While ignoring transient refresh errors in a periodic polling loop is reasonable, completely silent suppression can make troubleshooting difficult. Consider adding a console.debug or console.warn for visibility during development.
🔎 Suggested refinement
try { const logs = await onGetLogs(); setPrLogs(logs); - } catch { - // Ignore refresh errors + } catch (err) { + // Ignore transient refresh errors during polling + console.debug('Log refresh failed:', err); }
♻️ Duplicate comments (1)
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx (1)
373-389: Past review concern not fully addressed: timing brittleness remains.The
setTimeoutdelays were removed, which is an improvement, but the past reviewer's core concern about timing brittleness was not properly addressed. CallingcheckForNewCommits()immediately afteronPostReview()succeeds can still execute before React state updates propagate through the component tree.The previously suggested pattern (useEffect + state flag) would ensure
checkForNewCommitsruns after the component re-renders with updated state. This is more reliable and idiomatic in React.🔎 Implement the suggested useEffect pattern
+const [shouldCheckCommits, setShouldCheckCommits] = useState(false); + +useEffect(() => { + if (shouldCheckCommits) { + checkForNewCommits(); + setShouldCheckCommits(false); + } +}, [shouldCheckCommits, checkForNewCommits]); + const handlePostReview = async () => { const idsToPost = Array.from(selectedFindingIds); if (idsToPost.length === 0) return; setIsPostingFindings(true); try { const success = await onPostReview(idsToPost); if (success) { setPostedFindingIds(prev => new Set([...prev, ...idsToPost])); setSelectedFindingIds(new Set()); setPostSuccess({ count: idsToPost.length, timestamp: Date.now() }); - checkForNewCommits(); + setShouldCheckCommits(true); } } finally { setIsPostingFindings(false); } }; const handleApprove = async () => { if (!reviewResult) return; setIsPosting(true); try { const success = await onPostReview([]); if (success) { - checkForNewCommits(); + setShouldCheckCommits(true); } } finally { setIsPosting(false); } }; const handleAutoApprove = async () => { if (!reviewResult) return; setIsPosting(true); try { const lowFindingIds = lowSeverityFindings.map(f => f.id); if (lowFindingIds.length > 0) { const success = await onPostReview(lowFindingIds); if (!success) return; setPostedFindingIds(prev => new Set([...prev, ...lowFindingIds])); } const approvalSuccess = await onPostReview([]); if (approvalSuccess) { - checkForNewCommits(); + setShouldCheckCommits(true); } } finally { setIsPosting(false); } };Based on learnings, this implements the pattern previously suggested and marked as valid by the developer.
Also applies to: 391-403, 406-424
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx
🧰 Additional context used
📓 Path-based instructions (3)
apps/frontend/src/**/*.{ts,tsx,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always use i18n translation keys for all user-facing text in the frontend instead of hardcoded strings
Files:
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx
apps/frontend/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use
useTranslation()hook with namespace prefixes (e.g., 'navigation:items.key') for accessing translation strings in React components
Files:
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx
apps/frontend/**/*.{ts,tsx}
⚙️ CodeRabbit configuration file
apps/frontend/**/*.{ts,tsx}: Review React patterns and TypeScript type safety.
Check for proper state management and component composition.
Files:
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: CodeQL (javascript-typescript)
- GitHub Check: CodeQL (python)
🔇 Additional comments (2)
apps/frontend/src/renderer/components/github-prs/components/PRDetail.tsx (2)
1-82: LGTM: Excellent i18n compliance and React patterns.The component demonstrates strong adherence to coding guidelines:
- All user-facing text uses i18n translation keys with proper namespace prefixes
- The
useTranslationhook is correctly used throughout- The
AbortControllerpattern (lines 91, 128-131) effectively prevents race conditions in async operations- State management and hooks follow React 19 best practices
Also applies to: 90-155
426-433: Verify the hardcoded merge method.The merge method is hardcoded to
'squash', which limits flexibility. Ensure this aligns with your team's merge strategy, or consider making it configurable (e.g., from user preferences or PR settings).#!/bin/bash # Description: Check if merge method configuration exists elsewhere in the codebase # Search for merge method configuration or preferences rg -nP --type=ts --type=tsx -C3 'mergeMethod|merge.*method|squash.*merge' -g '!node_modules' -g '!*.test.*' # Search for other calls to onMergePR to see if they use different methods ast-grep --pattern 'onMergePR($$$)'
|
Hey @AlexMadera 👋 Tried to merge this but hit some blockers: 🔴 Merge ConflictsThe PR has conflicts with 📋 Open Review ItemsGemini flagged a high priority bug:
There are also some medium-priority nitpicks (snake_case hydration, atomic label updates) but those can be follow-up PRs. Once you rebase and address the — Mike |
Summary by CodeRabbit
New Features
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.