From 9a1665adc0a84e7b6a90a1ed3e268638faccadc9 Mon Sep 17 00:00:00 2001 From: iamnbutler Date: Thu, 22 Oct 2026 12:52:46 -0400 Subject: [PATCH 1/2] fix(app): keep Merge and Close pending until GitHub reports the pull request settled --- apps/app/src/channel.tsx | 17 +++++++++++++---- apps/app/src/git.ts | 18 +++++++++++++++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/apps/app/src/channel.tsx b/apps/app/src/channel.tsx index c8e27b18..07903c94 100644 --- a/apps/app/src/channel.tsx +++ b/apps/app/src/channel.tsx @@ -58,6 +58,8 @@ const dollars = new Intl.NumberFormat("en-US", { style: "currency", currency: "U const date = new Intl.DateTimeFormat(undefined, { dateStyle: "medium", timeStyle: "short" }); // Open checks change on their own, so the shown channel's pull request is read again this often. const PULL = 60_000; +// While a merge or close settles, so the pull request's new state shows within seconds. +const SETTLING = 3_000; /** Links people and agents wrote in the chat, newest mention first; tool output is left out. */ function links(items: Item[], repo?: string): DetailsLink[] { @@ -194,6 +196,7 @@ function usePull( branch: string | undefined, head: string | undefined, busy: boolean, + settling: boolean, ) { const [, render] = useReducer((count: number) => count + 1, 0); const key = repo && branch ? `${repo}\0${branch}` : undefined; @@ -209,12 +212,12 @@ function usePull( () => {}, ); void read(); - const timer = setInterval(read, PULL); + const timer = setInterval(read, settling ? SETTLING : PULL); return () => { active = false; clearInterval(timer); }; - }, [repo, branch, head, busy]); + }, [repo, branch, head, busy, settling]); return key ? pulls.get(key) : undefined; } @@ -222,9 +225,15 @@ export function ChannelDetails({ channel, user }: { channel: Listing; user: stri const right = useLayoutRight(); const details = useDetails(channel.id); const channels = useSyncExternalStore(host.subscribe, () => host.channels); - const { changes, info, items } = details; + const { changes, info, items, asked } = details; const busy = !!info?.chats.find((value) => value.id === chat)?.busy; - const pull = usePull(channel.repo, changes?.branch, changes?.head, busy); + const pull = usePull( + channel.repo, + changes?.branch, + changes?.head, + busy, + asked === "merge" || asked === "close", + ); useEffect(() => publish(channel.id, { pull }), [channel.id, pull]); useAsked(channel.id, busy, pull); const git = useGit(channel, chat, user); diff --git a/apps/app/src/git.ts b/apps/app/src/git.ts index 6b3e8fb0..71595cee 100644 --- a/apps/app/src/git.ts +++ b/apps/app/src/git.ts @@ -176,6 +176,8 @@ export function useGit(channel: Listing, chat: number, user: string) { }; } +const SETTLE = 15_000; + /** * Ends an action's pending state once the run it started ends, or for Create PR once the pull * request exists. Called once per shown channel, by its details, which stay mounted while the @@ -184,15 +186,29 @@ export function useGit(channel: Listing, chat: number, user: string) { export function useAsked(channel: string, busy: boolean, pull: GithubPull | null | undefined) { const { asked } = useDetails(channel); const ran = useRef(false); + const ended = useRef(0); useEffect(() => { if (!asked) { ran.current = false; + ended.current = 0; return; } // A closed or merged pull request from before isn't the one Create PR asked for. const live = pull?.state === "open" || pull?.state === "draft"; if (asked === "create" && live) return publish(channel, { asked: null }); if (busy) ran.current = true; - else if (ran.current) publish(channel, { asked: null }); + else if (ran.current) { + // Merge and Close stay pending until GitHub reports the pull request settled, so the stale + // Merge PR doesn't come back after the run; a refused merge shows it again after SETTLE. + if ((asked === "merge" || asked === "close") && live) { + ended.current ||= Date.now(); + const timer = setTimeout( + () => publish(channel, { asked: null }), + ended.current + SETTLE - Date.now(), + ); + return () => clearTimeout(timer); + } + publish(channel, { asked: null }); + } }, [channel, asked, busy, pull]); } From 4ed9f0d00b4dbdbb8b3515456f53c28080fcd1a8 Mon Sep 17 00:00:00 2001 From: iamnbutler Date: Thu, 22 Oct 2026 12:53:15 -0400 Subject: [PATCH 2/2] chore(app): confirm channel.tsx notes --- ...M4YQY84K8XSFAWY9R9YCMF5C-sidebar-totals-stay-per-grou.md | 6 +++--- ...M53CAJNHYEWYBZVCK2RXYEES-the-pull-request-is-read-whi.md | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.lane/memory/apps/app/src/channel.tsx/01M4YQY84K8XSFAWY9R9YCMF5C-sidebar-totals-stay-per-grou.md b/.lane/memory/apps/app/src/channel.tsx/01M4YQY84K8XSFAWY9R9YCMF5C-sidebar-totals-stay-per-grou.md index ed2ec57c..a44920da 100644 --- a/.lane/memory/apps/app/src/channel.tsx/01M4YQY84K8XSFAWY9R9YCMF5C-sidebar-totals-stay-per-grou.md +++ b/.lane/memory/apps/app/src/channel.tsx/01M4YQY84K8XSFAWY9R9YCMF5C-sidebar-totals-stay-per-grou.md @@ -4,9 +4,9 @@ anchor: function ChannelDetails created: 2026-10-15T02:58:24Z norm: '1' sig: b49433e7f0d6d4f3 -body_hash: c78ae37103f4096d -raw_hash: 7d353ff7240b0bf1 -vouched: 2026-10-22T16:29:20Z +body_hash: 0ae4093a683b3fe7 +raw_hash: 92ed025366ce01d8 +vouched: 2026-10-22T16:53:07Z lines: 161-209 --- diff --git a/.lane/memory/apps/app/src/channel.tsx/01M53CAJNHYEWYBZVCK2RXYEES-the-pull-request-is-read-whi.md b/.lane/memory/apps/app/src/channel.tsx/01M53CAJNHYEWYBZVCK2RXYEES-the-pull-request-is-read-whi.md index 3576c0d1..0cbd1203 100644 --- a/.lane/memory/apps/app/src/channel.tsx/01M53CAJNHYEWYBZVCK2RXYEES-the-pull-request-is-read-whi.md +++ b/.lane/memory/apps/app/src/channel.tsx/01M53CAJNHYEWYBZVCK2RXYEES-the-pull-request-is-read-whi.md @@ -4,8 +4,9 @@ anchor: function usePull created: 2026-10-16T22:11:37Z norm: '1' sig: a9da5d18c3817c7c -body_hash: 1d9b3fa757ffe35b -raw_hash: 32a07fd4de2d6171 +body_hash: 745f3ff60655c30b +raw_hash: e2e769d80fa9b661 +vouched: 2026-10-22T16:53:07Z lines: 191-218 ---