From f32e492ab46c120130e0c63eeca3eb353f10d83e Mon Sep 17 00:00:00 2001 From: Ramon Nogueira Date: Mon, 29 Jun 2026 16:04:41 -0400 Subject: [PATCH] fix: return to thread after plan approval (#1637) Co-authored-by: Ramon Nogueira <270434257+ramon-langchain@users.noreply.github.com> Co-authored-by: open-swe[bot] --- tests/e2e/tests/plan_review.spec.ts | 5 +++-- ui/src/components/agents/PlanReview.tsx | 21 +++++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/e2e/tests/plan_review.spec.ts b/tests/e2e/tests/plan_review.spec.ts index e7637a7b..37d19ffa 100644 --- a/tests/e2e/tests/plan_review.spec.ts +++ b/tests/e2e/tests/plan_review.spec.ts @@ -126,10 +126,11 @@ test.describe("Plan review (HTTP comments)", () => { await addComment(collab, "Reviewer: please also add a docstring."); await expect(collab.getByTestId("plan-comment")).toHaveCount(2); - // 5. The owner sees the collaborator's comment (polled), then approves. + // 5. The owner sees the collaborator's comment (polled), then approves and + // returns to the main conversation while implementation starts. await expect(owner.getByTestId("plan-comment")).toHaveCount(2, { timeout: 30_000 }); await owner.getByTestId("approve-plan").click(); - await expect(owner.getByTestId("plan-decision")).toContainText(/implementing/i); + await expect(owner).toHaveURL(new RegExp(`/agents/${threadId}$`)); // 6. The agent implements, opens a PR, and links it back in the Slack thread, // echoing the reviewers' feedback — which proves the comments were stored diff --git a/ui/src/components/agents/PlanReview.tsx b/ui/src/components/agents/PlanReview.tsx index 2fe8e7a7..1dd64768 100644 --- a/ui/src/components/agents/PlanReview.tsx +++ b/ui/src/components/agents/PlanReview.tsx @@ -1,4 +1,5 @@ import { useCallback, useEffect, useState } from "react" +import { useNavigate } from "@tanstack/react-router" import type { PlanComment, PlanData } from "@/lib/plan" import { @@ -48,6 +49,7 @@ async function copyToClipboard(text: string): Promise { } export function PlanReview({ plan }: { plan: PlanData }) { + const navigate = useNavigate() const resolvedTheme = useResolvedTheme() const [comments, setComments] = useState>([]) const [draft, setDraft] = useState("") @@ -153,20 +155,23 @@ export function PlanReview({ plan }: { plan: PlanData }) { setBusy(kind) setError(null) try { - if (kind === "approve") await approvePlan(plan.threadId) - else await rejectPlan(plan.threadId) - setDecision( - kind === "approve" - ? "Plan approved — the agent is implementing it." - : "Changes requested — the agent is revising the plan." - ) + if (kind === "approve") { + await approvePlan(plan.threadId) + await navigate({ + to: "/agents/$threadId", + params: { threadId: plan.threadId }, + }) + return + } + await rejectPlan(plan.threadId) + setDecision("Changes requested — the agent is revising the plan.") } catch (e) { setError((e as Error).message) } finally { setBusy(null) } }, - [plan.threadId] + [navigate, plan.threadId] ) const copyPlan = useCallback(async () => {