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] <open-swe@users.noreply.github.com>
This commit is contained in:
Ramon Nogueira 2026-06-29 16:04:41 -04:00 • committed by GitHub
parent ae04b72b41
commit f32e492ab4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 16 additions and 10 deletions

View file

@ -126,10 +126,11 @@ test.describe("Plan review (HTTP comments)", () => {
await addComment(collab, "Reviewer: please also add a docstring."); await addComment(collab, "Reviewer: please also add a docstring.");
await expect(collab.getByTestId("plan-comment")).toHaveCount(2); 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 expect(owner.getByTestId("plan-comment")).toHaveCount(2, { timeout: 30_000 });
await owner.getByTestId("approve-plan").click(); 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, // 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 // echoing the reviewers' feedback — which proves the comments were stored

View file

@ -1,4 +1,5 @@
import { useCallback, useEffect, useState } from "react" import { useCallback, useEffect, useState } from "react"
import { useNavigate } from "@tanstack/react-router"
import type { PlanComment, PlanData } from "@/lib/plan" import type { PlanComment, PlanData } from "@/lib/plan"
import { import {
@ -48,6 +49,7 @@ async function copyToClipboard(text: string): Promise<boolean> {
} }
export function PlanReview({ plan }: { plan: PlanData }) { export function PlanReview({ plan }: { plan: PlanData }) {
const navigate = useNavigate()
const resolvedTheme = useResolvedTheme() const resolvedTheme = useResolvedTheme()
const [comments, setComments] = useState<Array<PlanComment>>([]) const [comments, setComments] = useState<Array<PlanComment>>([])
const [draft, setDraft] = useState("") const [draft, setDraft] = useState("")
@ -153,20 +155,23 @@ export function PlanReview({ plan }: { plan: PlanData }) {
setBusy(kind) setBusy(kind)
setError(null) setError(null)
try { try {
if (kind === "approve") await approvePlan(plan.threadId) if (kind === "approve") {
else await rejectPlan(plan.threadId) await approvePlan(plan.threadId)
setDecision( await navigate({
kind === "approve" to: "/agents/$threadId",
? "Plan approved — the agent is implementing it." params: { threadId: plan.threadId },
: "Changes requested — the agent is revising the plan." })
) return
}
await rejectPlan(plan.threadId)
setDecision("Changes requested — the agent is revising the plan.")
} catch (e) { } catch (e) {
setError((e as Error).message) setError((e as Error).message)
} finally { } finally {
setBusy(null) setBusy(null)
} }
}, },
[plan.threadId] [navigate, plan.threadId]
) )
const copyPlan = useCallback(async () => { const copyPlan = useCallback(async () => {