From 5412276776fbcfe3cfefa7ca44d7c10e0fbde1dd Mon Sep 17 00:00:00 2001
From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
Date: Fri, 4 Sep 2026 15:09:15 -0400
Subject: [PATCH] Fix status bar runtime target render loop (#18685)
---
...rtsStatusSegment.render-stability.test.tsx | 89 +++++++++++++++++++
.../runtime/use-worktree-runtime-target.ts | 6 +-
2 files changed, 92 insertions(+), 3 deletions(-)
create mode 100644 src/renderer/src/components/status-bar/PortsStatusSegment.render-stability.test.tsx
diff --git a/src/renderer/src/components/status-bar/PortsStatusSegment.render-stability.test.tsx b/src/renderer/src/components/status-bar/PortsStatusSegment.render-stability.test.tsx
new file mode 100644
index 00000000000..d6d4ad95184
--- /dev/null
+++ b/src/renderer/src/components/status-bar/PortsStatusSegment.render-stability.test.tsx
@@ -0,0 +1,89 @@
+// @vitest-environment happy-dom
+
+import React, { act } from 'react'
+import { createRoot, type Root } from 'react-dom/client'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+vi.mock('@/components/ui/popover', () => ({
+ Popover: ({ children }: { children: React.ReactNode }) => <>{children}>,
+ PopoverContent: ({ children }: { children: React.ReactNode }) => <>{children}>,
+ PopoverTrigger: ({ children }: { children: React.ReactNode }) => <>{children}>
+}))
+
+vi.mock('@/components/ui/tooltip', () => ({
+ Tooltip: ({ children }: { children: React.ReactNode }) => <>{children}>,
+ TooltipContent: ({ children }: { children: React.ReactNode }) => <>{children}>,
+ TooltipTrigger: ({ children }: { children: React.ReactNode }) => <>{children}>
+}))
+
+vi.mock('@/components/SelectedTextCopyMenu', () => ({
+ SelectedTextCopyMenu: ({ children }: { children: React.ReactNode }) => <>{children}>
+}))
+
+vi.mock('./ports-status-popover-rows', () => ({
+ PortRow: () =>
,
+ WorkspaceGroupRows: () =>
+}))
+
+vi.mock('@/lib/react-error-boundary-reporting', () => ({
+ reportReactErrorBoundaryCrash: vi.fn()
+}))
+
+vi.mock('@/i18n/i18n', () => ({
+ translate: (_key: string, fallback: string, options?: Record) =>
+ options
+ ? fallback.replace(/{{(\w+)}}/g, (_match, name: string) => String(options[name] ?? ''))
+ : fallback
+}))
+
+import { RecoverableRenderErrorBoundary } from '../error-boundaries/RecoverableRenderErrorBoundary'
+import { useAppStore } from '@/store'
+import { PortsStatusSegment } from './PortsStatusSegment'
+
+describe('PortsStatusSegment render stability', () => {
+ let container: HTMLDivElement
+ let root: Root
+ let consoleError: ReturnType
+
+ beforeEach(() => {
+ useAppStore.setState(useAppStore.getInitialState(), true)
+ container = document.createElement('div')
+ document.body.appendChild(container)
+ root = createRoot(container)
+ consoleError = vi.spyOn(console, 'error').mockImplementation(() => undefined)
+ })
+
+ afterEach(() => {
+ act(() => root.unmount())
+ container.remove()
+ consoleError.mockRestore()
+ })
+
+ it('keeps the status controls mounted when the runtime owner is local', () => {
+ act(() => {
+ root.render(
+
+
+
+ )
+ })
+
+ const underlyingException = consoleError.mock.calls.find(
+ ([message, error]) =>
+ message === '[oracle.status-bar] render crash contained by boundary' &&
+ error instanceof Error &&
+ error.message.includes('Maximum update depth exceeded')
+ )
+
+ expect.soft(container.textContent).not.toContain('The status bar hit an error.')
+ expect.soft(underlyingException).toBeUndefined()
+ expect(container.querySelector('button[aria-label^="Ports, 0 workspace"]')).not.toBeNull()
+ })
+})
diff --git a/src/renderer/src/runtime/use-worktree-runtime-target.ts b/src/renderer/src/runtime/use-worktree-runtime-target.ts
index 25bd9099e90..22ef4ed5c4b 100644
--- a/src/renderer/src/runtime/use-worktree-runtime-target.ts
+++ b/src/renderer/src/runtime/use-worktree-runtime-target.ts
@@ -1,3 +1,4 @@
+import { useMemo } from 'react'
import { useAppStore } from '@/store'
import { getExecutionHostIdForWorktree } from '@/lib/worktree-runtime-owner'
import { runtimeTargetForExecutionHostId, type RuntimeClientTarget } from './runtime-client-target'
@@ -10,7 +11,6 @@ import { runtimeTargetForExecutionHostId, type RuntimeClientTarget } from './run
export function useWorktreeRuntimeTarget(
worktreeId: string | null | undefined
): RuntimeClientTarget | null {
- return useAppStore((state) =>
- runtimeTargetForExecutionHostId(getExecutionHostIdForWorktree(state, worktreeId))
- )
+ const executionHostId = useAppStore((state) => getExecutionHostIdForWorktree(state, worktreeId))
+ return useMemo(() => runtimeTargetForExecutionHostId(executionHostId), [executionHostId])
}