From 7cc3f3d80182b5100760a5024f060cab2ec1ce47 Mon Sep 17 00:00:00 2001 From: NotAnNvidiaFan <85665968+NotAnNvidiaFan@users.noreply.github.com> Date: Tue, 27 Oct 2026 13:02:00 -0500 Subject: [PATCH] fix(web): a failed review guide keeps Try again reachable Fixes #26033. A failed guide showed raw provider diagnostics as its message, so long CLI output pushed Try again out of the clipped panel. The failed state now shows a short message and the retry action, with the full output under a bounded Technical details block. The stale-guide banner gets the same treatment. --- .../PullRequestGuideTab.browser.test.tsx | 75 +++++++++++++++++++ .../pullRequest/PullRequestGuideTab.tsx | 48 +++++++++--- 2 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 apps/web/src/components/pullRequest/PullRequestGuideTab.browser.test.tsx diff --git a/apps/web/src/components/pullRequest/PullRequestGuideTab.browser.test.tsx b/apps/web/src/components/pullRequest/PullRequestGuideTab.browser.test.tsx new file mode 100644 index 0000000000000..0b746725e6253 --- /dev/null +++ b/apps/web/src/components/pullRequest/PullRequestGuideTab.browser.test.tsx @@ -0,0 +1,75 @@ +import { EnvironmentId, ProjectId, type PullRequestRef } from "@t3tools/contracts"; +import { expect, it, vi } from "vite-plus/test"; +import { userEvent } from "vite-plus/test/browser"; + +vi.mock(import("./guide/useReviewGuideWriter"), () => ({ useReviewGuideWriter: vi.fn() })); + +import { render } from "../../../test/render"; +import { useReviewGuideWriter } from "./guide/useReviewGuideWriter"; +import { PullRequestGuideTab } from "./PullRequestGuideTab"; + +const reference: PullRequestRef = { + projectId: ProjectId.make("project"), + repository: "owner/repo", + number: 1, +}; + +const verboseFailure = [ + "Codex CLI command failed: OpenAI Codex v0.164.0", + ...Array.from({ length: 200 }, (_, line) => `prompt line ${line}: read the change in order`), + "ERROR: provider auth command `sh` timed out after 5000 ms", +].join("\n"); + +it("keeps Try again inside a short panel when a failed guide has long diagnostics", async () => { + const write = vi.fn(async () => {}); + vi.mocked(useReviewGuideWriter).mockReturnValue({ + query: { isPending: false, error: null } as never, + data: { + guide: null, + run: { + _tag: "Failed", + headSha: "head", + failedAt: "2026-10-27T16:37:47.661Z", + detail: verboseFailure, + }, + currentHeadSha: "head", + }, + running: false, + canWrite: true, + write, + }); + const panel = document.createElement("div"); + panel.style.cssText = "position: relative; width: 480px; height: 360px; overflow: hidden"; + const tab = document.createElement("div"); + tab.style.cssText = "position: absolute; inset: 0"; + panel.append(tab); + document.body.append(panel); + using _panel = { [Symbol.dispose]: () => panel.remove() }; + + await using screen = await render( + {}} + />, + { container: tab }, + ); + + const retry = screen.getByRole("button", { name: "Try again" }); + await expect.element(retry).toBeVisible(); + const panelBox = panel.getBoundingClientRect(); + const retryBox = retry.element().getBoundingClientRect(); + expect(retryBox.top).toBeGreaterThanOrEqual(panelBox.top); + expect(retryBox.bottom).toBeLessThanOrEqual(panelBox.bottom); + + await userEvent.click(retry); + expect(write).toHaveBeenCalledOnce(); +}); diff --git a/apps/web/src/components/pullRequest/PullRequestGuideTab.tsx b/apps/web/src/components/pullRequest/PullRequestGuideTab.tsx index 8046653f11012..660219fd1377a 100644 --- a/apps/web/src/components/pullRequest/PullRequestGuideTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestGuideTab.tsx @@ -103,18 +103,24 @@ export function PullRequestGuideTab({ ); } return ( -
+

- {running ? "Writing a review guide" : "No review guide yet"} + {running + ? "Writing a review guide" + : failure === null + ? "No review guide yet" + : "Could not write a review guide"}

- {failure ?? - query.error ?? - (running - ? "An agent is reading the change. A large one takes a few minutes. You can leave; you will be told when it is ready." - : "A guide is a tour of the change: stops through the code in reading order, with the calls only a maintainer can make.")} + {running + ? "An agent is reading the change. A large one takes a few minutes. You can leave; you will be told when it is ready." + : failure === null + ? (query.error ?? + "A guide is a tour of the change: stops through the code in reading order, with the calls only a maintainer can make.") + : FAILURE_HINT}

{writeButton(failure === null ? "Write guide" : "Try again", "sm")} + {failure === null || running ? null : }
); } @@ -124,9 +130,16 @@ export function PullRequestGuideTab({ {running ? : } - {running - ? "Rewriting this guide. You will be told when the new one is ready." - : (failure ?? "Written before the latest push.")} + {running ? ( + "Rewriting this guide. You will be told when the new one is ready." + ) : failure === null ? ( + "Written before the latest push." + ) : ( + <> + {`Could not rewrite this guide. ${FAILURE_HINT}`} + + + )} {!running && canWrite ? {writeButton("Rewrite", "xs")} : null} @@ -155,3 +168,18 @@ export function PullRequestGuideTab({ ); } + +const FAILURE_HINT = "Check the review guide model in Settings → Source control."; + +function FailureDetails({ detail }: { detail: string }) { + return ( +
+ + Technical details + +
+        {detail}
+      
+
+ ); +}