fix(ui): retain PR links while monitors wait for execution

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
cryppadottaandPaperclip committed 2026-11-06 17:44:02 +00:00
1 parent ee030bdf22
commit c3604bf4b4
2 files changed
+34 -2

No files matched your search

@@ -235,6 +235,34 @@ describe("IssueMonitorBanner / IssueMonitorComposerStrip rendering", () => {
flushSync(() => root.unmount()); flushSync(() => root.unmount());
}); });
it.each([-120_000, 120_000])("keeps pending PR links visible during an active run (offset: %s)", offset => {
const issue = { ...issueWithMonitor(new Date(NOW.getTime() + offset).toISOString()), status: "in_progress" as const };
issue.executionState!.monitor!.serviceName = "github";
const product = {
id: "pr-1", type: "pull_request", title: "Update runtime probe",
url: "https://github.com/example/repo/pull/42", status: "ready_for_review",
reviewState: "needs_board_review", updatedAt: NOW,
} as unknown as IssueWorkProduct;
const root = createRoot(container);
const render = (hasActiveRun: boolean, products = [product]) => flushSync(() => root.render(<>
<IssueMonitorBanner issue={issue} hasActiveRun={hasActiveRun} workProducts={products} onCheckNow={vi.fn()} />
<IssueMonitorComposerStrip issue={issue} hasActiveRun={hasActiveRun} workProducts={products} onCheckNow={vi.fn()} />
</>));
try {
render(true);
expect(container.textContent).toContain("Monitor waits for current run");
expect(Array.from(container.querySelectorAll("a")).map(link => link.href)).toEqual([product.url, product.url]);
expect(container.querySelector("button")).toBeNull();
expect(container.textContent).toContain("Review on GitHub while the current run finishes saving its work.");
expect(container.textContent).not.toMatch(/overdue|next tick|then check status/i);
render(false);
expect(container.textContent).toContain("Pull request review requested");
expect(Array.from(container.querySelectorAll("button")).map(button => button.textContent)).toEqual(["Check status", "Check status"]);
render(true, [{ ...product, metadata: { state: "merged" } }]);
expect(container.querySelector("a")).toBeNull();
} finally { flushSync(() => root.unmount()); }
});
it("explains automatic workspace waiting without promising that a reply bypasses the lock", () => { it("explains automatic workspace waiting without promising that a reply bypasses the lock", () => {
const issue = { const issue = {
status: "todo", scheduledRetry: { status: "scheduled_retry", scheduledRetryReason: "workspace_busy", scheduledRetryAt: NOW.toISOString() }, status: "todo", scheduledRetry: { status: "scheduled_retry", scheduledRetryReason: "workspace_busy", scheduledRetryAt: NOW.toISOString() },
+6 -2
View File
@@ -118,6 +118,7 @@ export function buildMonitorSurfaceCopy(
const eta = formatMonitorEta(derived.nextCheckAt, now); // "in 2h 12m" | "due now" | "overdue by 18m" const eta = formatMonitorEta(derived.nextCheckAt, now); // "in 2h 12m" | "due now" | "overdue by 18m"
const absolute = formatMonitorAbsolute(derived.nextCheckAt, {}, now); // local time, e.g. "Today, 4:08 PM" const absolute = formatMonitorAbsolute(derived.nextCheckAt, {}, now); // local time, e.g. "Today, 4:08 PM"
const awaitingPullRequestReview = derived.source === "monitor" && derived.serviceName?.toLowerCase() === "github" && pendingPullRequestReviews > 0;
if (derived.source === "monitor" && hasActiveRun) { if (derived.source === "monitor" && hasActiveRun) {
const hint = "The check can start after the current run finishes saving its work."; const hint = "The check can start after the current run finishes saving its work.";
return { return {
@@ -128,9 +129,10 @@ export function buildMonitorSurfaceCopy(
tone: "info", tone: "info",
executionWait: true, executionWait: true,
checkUnavailable: true, checkUnavailable: true,
pullRequestReview: awaitingPullRequestReview,
}; };
} }
if (derived.source === "monitor" && derived.serviceName?.toLowerCase() === "github" && pendingPullRequestReviews > 0) { if (awaitingPullRequestReview) {
return { return {
bannerTitle: "Pull request review requested", bannerTitle: "Pull request review requested",
stripTitle: "Pull request review requested", stripTitle: "Pull request review requested",
@@ -351,7 +353,9 @@ export function IssueMonitorComposerStrip({
</div> </div>
{!copy.automaticChecksPaused ? ( {!copy.automaticChecksPaused ? (
<p className="mt-1.5 text-xs text-muted-foreground"> <p className="mt-1.5 text-xs text-muted-foreground">
{copy.pullRequestReview {copy.pullRequestReview && copy.executionWait
? "Review on GitHub while the current run finishes saving its work."
: copy.pullRequestReview
? "Review on GitHub, then check status to have the agent verify the result." ? "Review on GitHub, then check status to have the agent verify the result."
: copy.workspaceWait || copy.executionWait : copy.workspaceWait || copy.executionWait
? "You can keep sending instructions while the agent waits." ? "You can keep sending instructions while the agent waits."