diff --git a/backend/internal/domain/projectconfig.go b/backend/internal/domain/projectconfig.go index 840ad34b9..828890e39 100644 --- a/backend/internal/domain/projectconfig.go +++ b/backend/internal/domain/projectconfig.go @@ -39,7 +39,7 @@ type ProjectConfig struct { // Reviewers names the agent(s) that review a worker's PR when a review is // triggered. It is configured independently of the Worker override; an empty - // list falls back to the worker's own harness (see ResolveReviewerHarness). + // list falls back to claude-code (see ResolveReviewerHarness). Reviewers []ReviewerConfig `json:"reviewers,omitempty"` } @@ -55,15 +55,11 @@ type ReviewerConfig struct { const FallbackReviewerHarness = ReviewerClaudeCode // ResolveReviewerHarness picks the reviewer harness for a worker. A configured -// reviewer wins; otherwise it reuses the worker's own harness when that harness -// is also a supported reviewer, falling back to claude-code. -func (c ProjectConfig) ResolveReviewerHarness(workerHarness AgentHarness) ReviewerHarness { +// reviewer wins; otherwise claude-code is used. +func (c ProjectConfig) ResolveReviewerHarness(_ AgentHarness) ReviewerHarness { if len(c.Reviewers) > 0 { return c.Reviewers[0].Harness } - if h := ReviewerHarness(workerHarness); h.IsKnown() { - return h - } return FallbackReviewerHarness } diff --git a/backend/internal/domain/projectconfig_test.go b/backend/internal/domain/projectconfig_test.go index 58d9c3bad..4ad643d1c 100644 --- a/backend/internal/domain/projectconfig_test.go +++ b/backend/internal/domain/projectconfig_test.go @@ -80,13 +80,12 @@ func TestResolveReviewerHarness(t *testing.T) { t.Fatalf("configured reviewer = %q, want claude-code", got) } - // No reviewer configured: reuse the worker harness when it is also a - // supported reviewer (claude-code is). + // No reviewer configured: always use claude-code. if got := (ProjectConfig{}).ResolveReviewerHarness(HarnessClaudeCode); got != ReviewerClaudeCode { t.Fatalf("default = %q, want reviewer claude-code", got) } - // A worker harness that is not a supported reviewer falls back to claude-code. + // A worker harness that is not claude-code also falls back to claude-code. if got := (ProjectConfig{}).ResolveReviewerHarness(HarnessAider); got != FallbackReviewerHarness { t.Fatalf("fallback = %q, want %q", got, FallbackReviewerHarness) } diff --git a/backend/internal/review/review.go b/backend/internal/review/review.go index 46d47b67c..c6c88f336 100644 --- a/backend/internal/review/review.go +++ b/backend/internal/review/review.go @@ -375,8 +375,7 @@ func (e *Engine) List(ctx stdctx.Context, workerID domain.SessionID) (SessionRev } // reviewerHarness resolves which harness reviews the worker's PR: a configured -// reviewer wins, otherwise the worker's own harness is reused (falling back to -// claude-code), per domain.ResolveReviewerHarness. +// reviewer wins, otherwise claude-code is used, per domain.ResolveReviewerHarness. func (e *Engine) reviewerHarness(ctx stdctx.Context, worker domain.SessionRecord) (domain.ReviewerHarness, error) { var cfg domain.ProjectConfig if e.projects != nil { diff --git a/frontend/src/renderer/components/ProjectSettingsForm.tsx b/frontend/src/renderer/components/ProjectSettingsForm.tsx index 1c86bb850..9b746b7cb 100644 --- a/frontend/src/renderer/components/ProjectSettingsForm.tsx +++ b/frontend/src/renderer/components/ProjectSettingsForm.tsx @@ -270,7 +270,7 @@ function ReviewerSelect({ id, value, onChange }: { id: string; value: string; on - Project default + claude-code (default) {REVIEWER_OPTIONS.map((reviewer) => ( {reviewer} diff --git a/frontend/src/renderer/components/SessionInspector.test.tsx b/frontend/src/renderer/components/SessionInspector.test.tsx index 7c7eddb28..bff3ac9d9 100644 --- a/frontend/src/renderer/components/SessionInspector.test.tsx +++ b/frontend/src/renderer/components/SessionInspector.test.tsx @@ -49,6 +49,11 @@ const session = (prs: PullRequestFacts[]): WorkspaceSession => ({ prs, }); +const sessionWithProvider = (prs: PullRequestFacts[], provider: WorkspaceSession["provider"]): WorkspaceSession => ({ + ...session(prs), + provider, +}); + function renderWithQuery(children: ReactNode) { const client = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false } }, @@ -199,6 +204,36 @@ describe("SessionInspector reviews tab", () => { expect(onOpenReviewerTerminal).toHaveBeenCalledWith({ handleId: "reviewer-pane", harness: "codex" }); }); + it("shows claude-code as the default reviewer before a run exists", async () => { + getMock.mockImplementation(async (path: string) => { + if (path === "/api/v1/sessions/{sessionId}/reviews") { + return { data: { reviewerHandleId: "", reviews: [] } }; + } + if (path === "/api/v1/projects/{id}") { + return { + data: { + status: "ok", + project: { + id: "ws-1", + kind: "git", + name: "my-app", + path: "/repo", + repo: "my-app", + defaultBranch: "main", + config: {}, + }, + }, + }; + } + return { data: undefined }; + }); + + renderWithQuery(); + await openReviewsTab(); + + expect(await screen.findByText("claude-code")).toBeInTheDocument(); + }); + it("shows eligible and up-to-date PR review rows", async () => { mockCommonGets([approvedReview], "reviewer-pane", [ reviewState(3, "needs_review", "abc123"), diff --git a/frontend/src/renderer/components/SessionInspector.tsx b/frontend/src/renderer/components/SessionInspector.tsx index e7ae9d64d..baad79ff4 100644 --- a/frontend/src/renderer/components/SessionInspector.tsx +++ b/frontend/src/renderer/components/SessionInspector.tsx @@ -483,7 +483,7 @@ function ReviewPanel({ } const latest = reviewStates.find((review) => review.latestRun)?.latestRun; - const harness = latest?.harness || config?.reviewers?.[0]?.harness || session.provider || "reviewer"; + const harness = latest?.harness || config?.reviewers?.[0]?.harness || "claude-code"; const terminalEnabled = Boolean(reviewerHandleId && onOpenTerminal); const aggregateVerdict = sessionReviewVerdict(reviewStates); const runAction = reviewSessionRunAction(reviewStates, isTriggering);