feat(api): expose provider PR review summaries on the session PR API#3011
Conversation
Surface each reviewer's latest decisive review (verdict + submitted body) through the session PR summary so the reviews UI can show a verdict with its summary text. - PRReviewSummary gains Reviews []PRReviewEntry; summarizeReview populates it from the latest approved/changes_requested review per reviewer, sorted by reviewer for determinism - controller DTO gains SessionPRReviewEntry and a reviews field (omitempty, so it stays optional for existing clients) - openapi.yaml + schema.ts regenerated Inline review comment bodies stay private, folded into UnresolvedBy counts and links as before; only the top-level review summary body is exposed. Part of the reviews-tab revamp (#2433); the frontend two-section layout that consumes this is the next PR.
| } | ||
| reviewURLByAuthor[reviewer] = review.URL | ||
| isBot[reviewer] = review.IsBot | ||
| } |
There was a problem hiding this comment.
Reviews here is built from latestChangesRequestedReviews, which only returns reviewers whose latest review is changes_requested (approved reviews are computed and then dropped at the final filter). So verdict is always changes_requested in practice, and an approved review's summary body is never surfaced, even though the doc comment says "latest decisive submitted review per reviewer" and the enum advertises approved/review_required.
Is dropping approved summaries intended? If not, populate these entries from the full latest-per-reviewer set rather than the changes-requested-only subset, so an approved review with a summary body is exposed too.
There was a problem hiding this comment.
good catch, you are right. latestChangesRequestedReviews computes the latest decisive review per reviewer and then drops anyone whose latest is approved, so an approved review body never got surfaced even though the field advertises the approved verdict.
split it into latestDecisiveReviews (latest approved-or-changes_requested per reviewer, no drop) and Reviews now builds from that. the unresolved-comment grouping keeps using the changes-requested-only subset so that behaviour is unchanged. added a service test asserting an approved review body is surfaced. fixed in 5218046.
| if pr.Mergeability.State != domain.MergeConflicting || len(pr.Mergeability.ConflictFiles) != 0 || !containsString(pr.Mergeability.Reasons, "conflicts") { | ||
| t.Fatalf("mergeability = %+v", pr.Mergeability) | ||
| } | ||
| if len(pr.Review.Reviews) != 1 { |
There was a problem hiding this comment.
Consider adding a short assertion over newSessionPRReviewSummary output so the domain-to-DTO field mapping (Reviewer -> reviewerId, URL -> reviewUrl, etc.) is directly covered rather than only via compile plus the api-drift check.
There was a problem hiding this comment.
done. added a controller test over NewSessionPRSummary that covers the domain to DTO mapping directly (Reviewer to reviewerId, URL to reviewUrl, plus verdict/body/submittedAt/isBot) rather than relying on compile + api-drift. 5218046.
summarizeReview built the Reviews list from latestChangesRequestedReviews, which drops every reviewer whose latest decisive verdict is approved, so an approved review's summary body was never exposed despite the field advertising the approved verdict. Split out latestDecisiveReviews (latest approved-or-changes_requested per reviewer) and build Reviews from it, while the unresolved-comment grouping keeps using the changes-requested subset. Add a service test asserting an approved review's body is surfaced and a controller test covering the domain-to-DTO field mapping (Reviewer -> reviewerId, URL -> reviewUrl, etc.).
illegalcall
left a comment
There was a problem hiding this comment.
Standards
-
[P2] Register the new API component in
schemaNames. The generated schema exposesControllersSessionPRReviewEntryatbackend/internal/httpd/apispec/openapi.yaml:2151, leaking the internal package prefix into the public API and generated TypeScript.AGENTS.mdrequires every new named contract type to be registered. Add:"ControllersSessionPRReviewEntry": "SessionPRReviewEntry",
beside the other session PR mappings in
specgen/build.go, then runnpm run api.
No additional standards or code-smell findings.
Spec
No findings. Review selection, deterministic ordering, DTO mapping, optional reviews, privacy boundaries, and unresolved-comment grouping match issue #2433 and the PR description.
Verification
Focused service/controller/API tests and go vet passed; all GitHub checks are green. The full local backend suite encountered unrelated timing and macOS /private/var canonicalization failures. Frontend typecheck could not run independently because the isolated worktree lacks dependencies; GitHub frontend checks pass.
Summary: Standards — 1 finding, worst P2; Spec — 0 findings.
The reflected DTO surfaced as ControllersSessionPRReviewEntry, leaking the internal package prefix into the public OpenAPI spec and generated TypeScript. Register it in schemaNames so it emits as SessionPRReviewEntry, matching the other session PR contract types, and regenerate the spec + client.
|
@illegalcall good catch, fixed. registered |
dcbe5b4
into
feat/scm-fetch-pr-review-body
… (#3046) * feat(api): expose provider PR review summaries on the session PR API Surface each reviewer's latest decisive review (verdict + submitted body) through the session PR summary so the reviews UI can show a verdict with its summary text. - PRReviewSummary gains Reviews []PRReviewEntry; summarizeReview populates it from the latest approved/changes_requested review per reviewer, sorted by reviewer for determinism - controller DTO gains SessionPRReviewEntry and a reviews field (omitempty, so it stays optional for existing clients) - openapi.yaml + schema.ts regenerated Inline review comment bodies stay private, folded into UnresolvedBy counts and links as before; only the top-level review summary body is exposed. Part of the reviews-tab revamp (#2433); the frontend two-section layout that consumes this is the next PR. * fix(api): surface approved review summaries, not just changes-requested summarizeReview built the Reviews list from latestChangesRequestedReviews, which drops every reviewer whose latest decisive verdict is approved, so an approved review's summary body was never exposed despite the field advertising the approved verdict. Split out latestDecisiveReviews (latest approved-or-changes_requested per reviewer) and build Reviews from it, while the unresolved-comment grouping keeps using the changes-requested subset. Add a service test asserting an approved review's body is surfaced and a controller test covering the domain-to-DTO field mapping (Reviewer -> reviewerId, URL -> reviewUrl, etc.). * fix(api): register SessionPRReviewEntry schema name The reflected DTO surfaced as ControllersSessionPRReviewEntry, leaking the internal package prefix into the public OpenAPI spec and generated TypeScript. Register it in schemaNames so it emits as SessionPRReviewEntry, matching the other session PR contract types, and regenerate the spec + client.
Summary
Step 3 of the reviews-tab revamp (#2433): expose each reviewer's submitted review summary (verdict + body) through the session PR API, so the frontend can render a verdict with its summary text.
sessionsvc.PRReviewSummarygainsReviews []PRReviewEntry.summarizeReviewpopulates it from the latest decisive (approved/changes_requested) review per reviewer, sorted by reviewer for deterministic output.SessionPRReviewEntry(reviewerId, verdict, body, reviewUrl, submittedAt, isBot) and areviewsfield onSessionPRReviewSummary.openapi.yaml+schema.tsregenerated.Combines the plan's "add API shape" and "populate summarizeReview" steps, since a plumbed-but-unpopulated field isn't a reviewable unit on its own.
Privacy
Inline review comment bodies stay private — they remain folded into
unresolvedBycounts and links, never surfaced as raw text. Only the top-level review summary body is exposed. The renamed testTestListPRSummariesExposesReviewSummariesButKeepsRawLogsAndCommentBodiesPrivateasserts both: the review summary is surfaced, and comment bodies + CI log tails never leak into the summary.reviewsis optionalAdded with
omitempty, soreviews?is optional in the generated schema. This keeps the field backward-compatible for existing clients and avoids forcing unrelated fixture churn onto this PR; the step-5 UI will consume it with a?? []default.Stacked on #3010 → #3009
Tests
cd backend && go build ./... && go vet ./internal/service/session/ ./internal/httpd/controllers/cleango test ./internal/service/session/ ./internal/httpd/controllers/ ./internal/httpd/apispec/...passnpm run api:spec+openapi-typescriptregen — schema.ts change is purely additivecd frontend && npx tsc --noEmitclean (frontend diff is generated schema.ts only)