From 2f4867619ff9c75a659b62ccb521a07bdba71f46 Mon Sep 17 00:00:00 2001 From: klopez4212 Date: Sun, 5 Jul 2026 08:30:10 +0100 Subject: [PATCH] Pin each chat's PR so panels stop converging on one PR Two live effects made different chats' work panels show the same PR: the windowed message fetch ages the posted PR link out of the loaded history (losing the strongest per-chat signal), and agents reuse a worktree across chats in one project, so branch discovery resolves several chats to the same pull request. Each chat now pins the PR it resolves (localStorage): a link posted in the chat always wins and re-pins, the pin covers revisits after the link leaves the window, and branch discovery only runs for chats with no posted link and no pin. Co-Authored-By: Claude Fable 5 --- .../features/chats/lib/chatWorkAutomation.ts | 30 +++++++++++++++++++ .../src/features/chats/ui/ChatWorkPanel.tsx | 15 ++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/desktop/src/features/chats/lib/chatWorkAutomation.ts b/desktop/src/features/chats/lib/chatWorkAutomation.ts index 25e3127a08..ee02f9b9f5 100644 --- a/desktop/src/features/chats/lib/chatWorkAutomation.ts +++ b/desktop/src/features/chats/lib/chatWorkAutomation.ts @@ -143,3 +143,33 @@ export function useChatWorkAutomation(chatId: string): ChatWorkAutomation { return state; } + +const PR_STORAGE_PREFIX = "buzz:chat-work-pr:v1"; + +/** + * The PR pinned to a chat. Posted links age out of the windowed message + * fetch and branch discovery can resolve several chats sharing a reused + * worktree to the same PR — the pin keeps each chat on the PR it actually + * resolved first, with posted links always overriding. + */ +export function readChatPinnedPr(chatId: string): string | null { + if (typeof window === "undefined") { + return null; + } + try { + return window.localStorage.getItem(`${PR_STORAGE_PREFIX}:${chatId}`); + } catch { + return null; + } +} + +export function writeChatPinnedPr(chatId: string, href: string) { + if (typeof window === "undefined") { + return; + } + try { + window.localStorage.setItem(`${PR_STORAGE_PREFIX}:${chatId}`, href); + } catch { + // Best-effort. + } +} diff --git a/desktop/src/features/chats/ui/ChatWorkPanel.tsx b/desktop/src/features/chats/ui/ChatWorkPanel.tsx index 574f6f7bc7..4d14b03e50 100644 --- a/desktop/src/features/chats/ui/ChatWorkPanel.tsx +++ b/desktop/src/features/chats/ui/ChatWorkPanel.tsx @@ -11,8 +11,10 @@ import { } from "lucide-react"; import { + readChatPinnedPr, updateChatWorkAutomation, useChatWorkAutomation, + writeChatPinnedPr, } from "@/features/chats/lib/chatWorkAutomation"; import { type GithubCheckSummary, @@ -83,11 +85,20 @@ export function ChatWorkPanel({ // hidden panel stops polling entirely. const monitorActive = open || automation.autoFixCi || automation.addressComments; + // Pin resolution order: a link posted in THIS chat wins, then the chat's + // previously pinned PR, then branch discovery — discovery alone is + // ambiguous when agents reuse a worktree across chats in one project. + const pinnedHref = React.useMemo(() => readChatPinnedPr(chatId), [chatId]); const discoveredPrQuery = useGithubPrForBranchQuery( - monitorActive && !prHref ? projectPath : null, + monitorActive && !prHref && !pinnedHref ? projectPath : null, branch, ); - const effectiveHref = prHref ?? discoveredPrQuery.data ?? null; + const effectiveHref = prHref ?? pinnedHref ?? discoveredPrQuery.data ?? null; + React.useEffect(() => { + if (effectiveHref && effectiveHref !== readChatPinnedPr(chatId)) { + writeChatPinnedPr(chatId, effectiveHref); + } + }, [chatId, effectiveHref]); const preview = effectiveHref ? parseSupportedLinkPreview(effectiveHref) : null;