- ChatView: Stabilize terminalIds/terminalGroups dependencies by using JSON.stringify so React compares by value instead of reference, preventing an infinite write loop when normalizeThreadTerminals creates new arrays. - persistenceService.test: Fix catchUp ordering assertion to compare each event's seq against the previous event's seq instead of itself. - BranchToolbar: Remove redundant api.threads.updateBranch call since the useEffect in ChatView already syncs branch changes to the server.
Unified · Split
| @@ -125,9 +125,11 @@ describe("PersistenceService", () => { · 124 unmodified lines | ||
| 125 | 125 | |
| 126 | 126 | const catchUp = service.catchUp({ afterSeq: 0 }); |
| 127 | 127 | expect(catchUp.events.length).toBeGreaterThan(0); |
| 128 | − expect(catchUp.events.every((event, index, events) => event.seq === events[index]?.seq)).toBe( | |
| 129 | − true, | |
| 130 | − ); | |
| 128 | + expect( | |
| 129 | + catchUp.events.every( | |
| 130 | + (event, index, events) => index === 0 || event.seq > events[index - 1]!.seq, | |
| 131 | + ), | |
| 132 | + ).toBe(true); | |
| 131 | 133 | expect(catchUp.events.some((event) => event.eventType === "project.upsert")).toBe(true); |
| 132 | 134 | expect(catchUp.events.some((event) => event.eventType === "thread.upsert")).toBe(true); |
| 133 | 135 | expect(catchUp.events.some((event) => event.eventType === "message.upsert")).toBe(true); |
| @@ -120,7 +120,6 @@ export default function BranchToolbar({ · 119 unmodified lines | ||
| 120 | 120 | if (sessionId && worktreePath !== activeWorktreePath) { |
| 121 | 121 | void api?.providers.stopSession({ sessionId }).catch(() => undefined); |
| 122 | 122 | } |
| 123 | − void api?.threads.updateBranch({ threadId: activeThreadId, branch, worktreePath }).catch(() => undefined); | |
| 124 | 123 | dispatch({ type: "SET_THREAD_BRANCH", threadId: activeThreadId, branch, worktreePath }); |
| 125 | 124 | }; |
| 126 | 125 | |
| @@ -782,6 +782,9 @@ export default function ChatView({ threadId }: ChatViewProps) { · 781 unmodified lines | ||
| 782 | 782 | .catch(() => undefined); |
| 783 | 783 | }, [activeThread?.id, activeThread?.branch, activeThread?.worktreePath, api]); |
| 784 | 784 | |
| 785 | + const terminalIdsKey = JSON.stringify(activeThread?.terminalIds ?? null); | |
| 786 | + const terminalGroupsKey = JSON.stringify(activeThread?.terminalGroups ?? null); | |
| 787 | + | |
| 785 | 788 | useEffect(() => { |
| 786 | 789 | if (!api || !activeThread) return; |
| 787 | 790 | void api.threads |
| @@ -795,13 +798,14 @@ export default function ChatView({ threadId }: ChatViewProps) { · 7 unmodified lines | ||
| 795 | 798 | activeTerminalGroupId: activeThread.activeTerminalGroupId, |
| 796 | 799 | }) |
| 797 | 800 | .catch(() => undefined); |
| 801 | + // eslint-disable-next-line react-hooks/exhaustive-deps | |
| 798 | 802 | }, [ |
| 799 | 803 | activeThread?.id, |
| 800 | 804 | activeThread?.terminalOpen, |
| 801 | 805 | activeThread?.terminalHeight, |
| 802 | − activeThread?.terminalIds, | |
| 806 | + terminalIdsKey, | |
| 803 | 807 | activeThread?.activeTerminalId, |
| 804 | − activeThread?.terminalGroups, | |
| 808 | + terminalGroupsKey, | |
| 805 | 809 | activeThread?.activeTerminalGroupId, |
| 806 | 810 | api, |
| 807 | 811 | ]); |