Skip to content

fix(pr-status): address review feedback missed before merge (#570 follow-up)#580

Merged
frankbria merged 2 commits intomainfrom
fix/570-pr-status-review-feedback
Apr 13, 2026
Merged

fix(pr-status): address review feedback missed before merge (#570 follow-up)#580
frankbria merged 2 commits intomainfrom
fix/570-pr-status-review-feedback

Conversation

@frankbria
Copy link
Copy Markdown
Owner

@frankbria frankbria commented Apr 13, 2026

Follow-up to #579 (merged). Five issues from CodeRabbit and claude-review that were not addressed before merge.

What was missed and why

I checked that CI was green and that CodeRabbit passed, but I did not read the inline CodeRabbit comment on get_pr_review_status or either of the two claude-review PR comments before merging.

Fixes

# File Issue Fix
1 github_integration.py get_pr_review_status included DISMISSED and COMMENTED reviews; non-list/non-dict responses would AttributeError Guard against non-list response; filter to dicts; restrict to APPROVED and CHANGES_REQUESTED only
2 pr_v2.py pr_raw["head"]["sha"] etc. would KeyError on partial response → unhandled 500 Use .get() with safe fallbacks; raise 502 with a clear message if required fields absent
3 pr_v2.py httpx.AsyncClient created but never closed → file descriptor leak try/finally: await client.close()
4 PRStatusPanel.tsx SWR cache key used raw workspacePath → cache miss for paths with spaces encodeURIComponent(workspacePath)
5 types/index.ts status, conclusion, review_status, merge_state typed as loose string Union literal types — typos caught at compile time

Test plan

  • uv run pytest tests/ui/test_pr_status.py -v — 20/20 (10 new tests: 502 path, generic 500, client-close verified, 6 direct unit tests for get_pr_review_status corner cases)
  • npm test -- --testPathPattern=PRStatusPanel — 19/19 (SWR key assertion updated for encoded path)
  • uv run ruff check . — clean
  • npm run build — succeeds

Summary by CodeRabbit

  • Bug Fixes

    • More robust handling of GitHub PR and review responses, preventing crashes and returning proper error codes.
    • Corrected review-status aggregation so latest actionable reviewer state determines status.
    • Ensured GitHub client is always closed on completion or error.
    • URL-encoded workspace paths to handle special characters.
  • Tests

    • Added endpoint error-handling and lifecycle tests.
    • Added unit tests covering review-status logic and edge cases.
  • Chores

    • Narrowed UI response types for stricter status/conclusion values.

Five issues identified in review of #570 that were not addressed before merge:

1. get_pr_review_status: guard against non-list response, filter non-dict
   items, and exclude DISMISSED/COMMENTED states (only APPROVED and
   CHANGES_REQUESTED are actionable).

2. pr_v2.py get_pr_status: use safe .get() access on pr_raw (head.sha,
   html_url, state); raise 502 with a clear message if required fields are
   absent rather than letting KeyError surface as unhandled 500.

3. pr_v2.py get_pr_status: wrap GitHub calls in try/finally and call
   await client.close() to prevent httpx AsyncClient file-descriptor leak.

4. PRStatusPanel: encodeURIComponent(workspacePath) in SWR cache key to
   prevent cache mismatches for paths with spaces or special characters.

5. types/index.ts: narrow CICheck.status/conclusion and
   PRStatusResponse.review_status/merge_state to union literal types so
   typos are caught at compile time rather than failing silently at runtime.

Tests: +10 backend (502 path, generic 500, client-close assertions, six
direct unit tests for get_pr_review_status corner cases). All 20 pass.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 524b4025-9b3f-4073-8d34-d026e2513c89

📥 Commits

Reviewing files that changed from the base of the PR and between 1a166d0 and edf08e9.

📒 Files selected for processing (4)
  • codeframe/git/github_integration.py
  • codeframe/ui/routers/pr_v2.py
  • tests/ui/test_pr_status.py
  • web-ui/src/types/index.ts
✅ Files skipped from review due to trivial changes (1)
  • web-ui/src/types/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • codeframe/git/github_integration.py
  • codeframe/ui/routers/pr_v2.py
  • tests/ui/test_pr_status.py

Walkthrough

Defensive GitHub PR review parsing and aggregation; ensured GitHub client is closed on all paths; stricter PR payload validation returning HTTP 502 for malformed inputs; added tests for lifecycle and review-status edge cases; URL-encoded workspace keys and narrowed TypeScript PR/CI status types.

Changes

Cohort / File(s) Summary
GitHub review aggregation
codeframe/git/github_integration.py
Normalize reviews to a list; filter for actionable dict reviews (APPROVED, CHANGES_REQUESTED); collapse per-reviewer to latest actionable review by submitted_at; compute final review_status from filtered latest-per-reviewer results.
Router & client lifecycle
codeframe/ui/routers/pr_v2.py
Create GitHub client before try so finally can always await client.close(); validate pr_raw and head with safe .get() access; return HTTP 502 for malformed/missing PR fields; derive merge_state from validated state/merged_at.
Backend tests
tests/ui/test_pr_status.py
Mock client now exposes async close(); added tests for HTTP 502 on malformed PR payloads, HTTP 500 on unexpected exceptions, lifecycle tests asserting client.close() is awaited, and unit tests for per-reviewer/latest-action review-state behavior and ignored review types.
Frontend: SWR key encoding & tests
web-ui/src/components/review/PRStatusPanel.tsx, web-ui/src/__tests__/components/review/PRStatusPanel.test.tsx
Use encodeURIComponent(workspacePath) in SWR key; update tests to expect URI-encoded workspace_path in the key.
Type tightening
web-ui/src/types/index.ts
Narrowed CICheck.status/conclusion and PRStatusResponse.review_status/merge_state to explicit string-literal unions.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Browser / UI
  participant Router as pr_v2.get_pr_status
  participant GHClient as GitHub Client
  participant GitHub as GitHub API

  Client->>Router: GET /api/v2/pr/status (pr_raw)
  Router->>GHClient: create client
  Router->>GHClient: get_pr_review_status(pr_number)
  GHClient->>GitHub: GET /repos/.../pulls/{pr}/reviews
  GitHub-->>GHClient: reviews (maybe non-list / mixed items)
  GHClient->>GHClient: normalize to list, filter actionable dicts, collapse latest per reviewer
  GHClient-->>Router: review_status
  Router->>GHClient: await client.close()
  Router-->>Client: HTTP 200/502/500 with PR status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 I nibble through reviews, sorting old from new,
I stitch the client closed on every avenue.
Encoded paths and typed delights,
Tests hop in, catching edge-case flights.
A tidy patch — a rabbit’s cheery view!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies this as a fix for review feedback related to PR #570, which aligns with the primary purpose of addressing multiple issues from prior code reviews.
Docstring Coverage ✅ Passed Docstring coverage is 95.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/570-pr-status-review-feedback

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude
Copy link
Copy Markdown

claude bot commented Apr 13, 2026

Review — fix(pr-status): #570 follow-up

Good, targeted cleanup. The five issues from the original review are addressed and the test additions are solid. A few observations:


What's working well

get_pr_review_status (github_integration.py)
The guard-and-filter approach is correct. Checking isinstance(r, dict) before the in ACTIONABLE lookup prevents the AttributeError on malformed items. All six unit tests in TestGetPrReviewStatus cover the meaningful corner cases (DISMISSED ignored, COMMENTED ignored, non-list → pending, non-dict items skipped).

get_pr_status (pr_v2.py)
The placement of _get_github_client() before the try block is the right call — if configuration is missing, the HTTPException propagates before client is ever bound, so the finally block can't encounter a NameError. The comment explaining why also helps future readers. The except HTTPException: raise clause before the generic except Exception is required to prevent the 502 from being swallowed into a 500 — good catch.

Frontend
encodeURIComponent(workspacePath) is the right fix. Simple, correct, test updated to match.

Types (index.ts)
Union literals on status, conclusion, review_status, and merge_state are a real improvement over the comment-style docs that were there before.


One thing to track

Other pr_v2.py endpoints still have the fd leak (lines ~250, 290, 333, 380, 431)

get_pr_status is now the only endpoint with try/finally: await client.close(). The other five (list_pull_requests, get_pull_request, create_pull_request, merge_pull_request, close_pull_request) still allocate an httpx.AsyncClient on every request and never close it.

This PR is scoped to the issues called out before merge, so I understand not fixing everything here. But it should land in a follow-up or tracked in an issue — under load these will accumulate open sockets.


Minor observations (non-blocking)

  • ACTIONABLE = {"APPROVED", "CHANGES_REQUESTED"} is defined inside the method body. Module-level constant would avoid re-creating the set on every call, though the impact is negligible.

  • test_generic_exception_returns_500 verifies the status code but doesn't assert mock_client.close.assert_awaited_once(). Given the fd-leak fix is the main correctness goal here, adding that assertion would make the test more complete. Non-blocking.

  • Backend Pydantic models (pr_v2.py lines 90–96) still use loose str for merge_state, review_status, etc. Only the TS types were tightened. Not critical — the important enforcement direction is at the API boundary — but worth noting if you want end-to-end type consistency.


Overall the PR does exactly what it says: five targeted fixes, tests updated to match, CI clean. Ship it when CodeRabbit finishes its run.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@codeframe/git/github_integration.py`:
- Around line 440-446: The aggregation currently treats every actionable review
independently, so stale reviews from the same reviewer (e.g., CHANGES_REQUESTED
then later APPROVED) still count; update the logic that builds active reviews to
collapse by reviewer and keep only their latest actionable review before
computing has_changes_requested and has_approved. Specifically, group the
reviews (the reviews list and ACTIONABLE filter) by reviewer identity (e.g.,
r.get("user", {}).get("login") or another stable reviewer key), sort/compare by
a timestamp field (e.g., r.get("submitted_at") or r.get("submittedAt")) or
fallback to id to pick the most recent actionable review per reviewer, then
compute has_changes_requested and has_approved from that per-reviewer latest
set; also add a unit test for the sequence "same reviewer CHANGES_REQUESTED then
APPROVED" to ensure regression coverage.

In `@codeframe/ui/routers/pr_v2.py`:
- Around line 176-182: Ensure pr_raw and its nested "head" are dicts before
using .get: validate that pr_raw is an instance of dict and that
pr_raw.get("head") is a dict, then safely extract head_sha, pr_url, and state;
if any of those type checks fail or required fields are missing, raise the
502/invalid-GitHub-response path instead of letting attribute access on
non-dicts raise a 500. Update the logic around pr_raw, head_sha, pr_url, and
state to perform these type checks and early return/raise when the payload shape
is invalid.

In `@web-ui/src/types/index.ts`:
- Around line 277-278: Update the check-run type unions so they match all GitHub
API values: extend the status union on the type that defines "status" (currently
'queued' | 'in_progress' | 'completed') to also include 'waiting', 'requested',
and 'pending', and extend the "conclusion" union (currently 'success' |
'failure' | 'neutral' | 'cancelled' | 'timed_out' | 'action_required' |
'skipped' | null) to also include 'stale'; locate the fields named status and
conclusion in the type declaration in web-ui/src/types/index.ts and add those
missing string literals to the respective unions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0045db76-37cf-4daf-977e-8fc2fe823cd6

📥 Commits

Reviewing files that changed from the base of the PR and between 71cbb25 and 1a166d0.

📒 Files selected for processing (6)
  • codeframe/git/github_integration.py
  • codeframe/ui/routers/pr_v2.py
  • tests/ui/test_pr_status.py
  • web-ui/src/__tests__/components/review/PRStatusPanel.test.tsx
  • web-ui/src/components/review/PRStatusPanel.tsx
  • web-ui/src/types/index.ts

Comment thread codeframe/git/github_integration.py Outdated
Comment thread codeframe/ui/routers/pr_v2.py
Comment thread web-ui/src/types/index.ts Outdated
- github_integration.py: collapse per-reviewer review history before
  aggregating — a reviewer who CHANGES_REQUESTed then APPROVEd now
  correctly counts as approved (was double-counted before)
- pr_v2.py: validate pr_raw is a dict before .get() access; non-dict
  head field also raises 502 rather than AttributeError
- types/index.ts: add missing GitHub API values to CICheck unions
  (status: waiting/requested/pending; conclusion: stale)
- tests: add 4 new cases (same-reviewer sequence ×2, non-dict pr_raw,
  non-dict head field) — 24/24 pass
@claude
Copy link
Copy Markdown

claude bot commented Apr 13, 2026

Follow-up review — latest commit (edf08e9)

All three of CodeRabbit actionable items are addressed in the new commit.

1. Per-reviewer deduplication (github_integration.py)latest_per_reviewer dict is in place, gating on submitted_at ISO string comparison. Tests test_same_reviewer_changes_then_approved and test_same_reviewer_approved_then_changes cover both orderings. Done.

2. pr_raw type validation (pr_v2.py)isinstance(pr_raw, dict) check added before any field access; isinstance(head, dict) gate on the nested object too. test_non_dict_head_field_returns_502 covers the regression. Done.

3. TypeScript union completeness (types/index.ts)status now includes waiting | requested | pending; conclusion includes stale. Done.


Carry-forward from the first review (still open, not new):

  • Five other pr_v2.py endpoints still leak the httpx client. Scope exclusion is fine here — but please file a tracking issue before merge so it does not get lost.
  • ACTIONABLE is still a method-local set — minor, non-blocking.
  • test_generic_exception_returns_500 does not assert client.close.assert_awaited_once()test_client_close_called_on_github_error covers close-on-error for GitHubAPIError but not for a generic ValueError. Low risk given the unconditional finally, but the assertion would make the guarantee explicit.

No new concerns. The substantive correctness issues are resolved. Ready to merge after the client-leak issue is filed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant