fix(native-chat): bring the structured core branch back under max-lines (#15168)

Static analysis was red on the branch itself, which is why every stacked PR
inherited three failing checks. Two files were over their caps, both from this
branch's own growth:

- `use-app-startup-hydration.ts` (307/300): the startup store subscription moves
  to `use-app-startup-actions.ts`. It is a separate concern from the boot chain
  and was the only reason the file needed `useShallow`.
- the mobile session route (5036/5015): the structured chat view was wired
  inline with eighteen props. It moves to `MobileStructuredSessionPane`, so the
  route passes one entry object.

No per-file cap was raised and no rule was disabled.

Verified: repo-wide oxlint clean (0 errors, 15765 files), web typecheck, mobile
typecheck, and the startup source-slice suite (25 tests).
This commit is contained in:
Brennan Benson
2026-08-17 13:57:55 -07:00
committed by GitHub
parent 735b9860f1
commit 041dd5f05b
4 changed files with 89 additions and 64 deletions
+3 -29
View File
@@ -228,7 +228,7 @@ import {
} from '../../../../src/dictation/mobile-dictation-setup'
import { TerminalPaneView } from '../../../../src/session/TerminalPaneView'
import { MobileNativeChatOverlay } from '../../../../src/session/MobileNativeChatOverlay'
import { MobileStructuredAgentSessionView } from '../../../../src/session/MobileStructuredAgentSessionView'
import { MobileStructuredSessionPane } from '../../../../src/session/MobileStructuredSessionPane'
import { MobileStructuredSessionCreateError } from '../../../../src/session/MobileStructuredSessionCreateError'
import { useMobileStructuredSessionEntry } from '../../../../src/session/use-mobile-structured-session-entry'
import { showMobileStructuredChatChoice } from '../../../../src/session/mobile-structured-session-create'
@@ -4684,36 +4684,10 @@ export default function SessionScreen() {
)}
</View>
) : activeStructuredTab ? (
<MobileStructuredAgentSessionView
<MobileStructuredSessionPane
key={activeStructuredTab.sessionId}
items={structuredSessionEntry.session.items}
status={structuredSessionEntry.session.status}
error={structuredSessionEntry.session.error}
hasOlder={structuredSessionEntry.session.hasOlder}
loadingOlder={structuredSessionEntry.session.loadingOlder}
onLoadOlder={structuredSessionEntry.session.loadOlder}
entry={structuredSessionEntry}
onOpenFile={handleNativeChatFileTap}
outbox={structuredSessionEntry.writes.outbox}
writeError={structuredSessionEntry.writes.error}
onSend={async (text, restored) => {
const accepted = await structuredSessionEntry.writes.send(text, [
...restored,
...structuredSessionEntry.attachments.attachments
])
if (accepted) {
structuredSessionEntry.attachments.clear()
}
return accepted
}}
onTakeQueuedForEdit={structuredSessionEntry.writes.takeQueuedForEdit}
onRetry={structuredSessionEntry.writes.retry}
onRespondToPrompt={structuredSessionEntry.writes.respondToPrompt}
sessionOptions={structuredSessionEntry.sessionOptions}
attachments={structuredSessionEntry.attachments.attachments}
isAttaching={structuredSessionEntry.attachments.attaching}
onAttachImage={() => void structuredSessionEntry.attachments.attach('library')}
onRemoveAttachment={structuredSessionEntry.attachments.remove}
onCancel={structuredSessionEntry.writes.cancel}
/>
) : activePendingTerminalTab ? (
<View style={styles.emptyState}>
@@ -0,0 +1,46 @@
// Wires one structured agent session entry into the chat view. Kept out of the
// session route so the route threads one object instead of eighteen props.
import { MobileStructuredAgentSessionView } from './MobileStructuredAgentSessionView'
import type { useMobileStructuredSessionEntry } from './use-mobile-structured-session-entry'
type StructuredSessionEntry = ReturnType<typeof useMobileStructuredSessionEntry>
export function MobileStructuredSessionPane({
entry,
onOpenFile
}: {
entry: StructuredSessionEntry
onOpenFile: (path: string, line?: number) => void
}) {
const { session, writes, attachments, sessionOptions } = entry
return (
<MobileStructuredAgentSessionView
items={session.items}
status={session.status}
error={session.error}
hasOlder={session.hasOlder}
loadingOlder={session.loadingOlder}
onLoadOlder={session.loadOlder}
onOpenFile={onOpenFile}
outbox={writes.outbox}
writeError={writes.error}
onSend={async (text, restored) => {
const accepted = await writes.send(text, [...restored, ...attachments.attachments])
if (accepted) {
attachments.clear()
}
return accepted
}}
onTakeQueuedForEdit={writes.takeQueuedForEdit}
onRetry={writes.retry}
onRespondToPrompt={writes.respondToPrompt}
sessionOptions={sessionOptions}
attachments={attachments.attachments}
isAttaching={attachments.attaching}
onAttachImage={() => void attachments.attach('library')}
onRemoveAttachment={attachments.remove}
onCancel={writes.cancel}
/>
)
}
@@ -0,0 +1,39 @@
// The renderer boot chain's store subscription, kept apart from the chain itself
// so one useShallow equality check covers every startup action.
import { useShallow } from 'zustand/react/shallow'
import { useAppStore } from '../store'
export function useStartupActions() {
// Why: consolidate action refs into one useShallow subscription so React runs one equality check per store mutation instead of one per action.
return useAppStore(
useShallow((s) => ({
fetchReposForAllHosts: s.fetchReposForAllHosts,
awaitLocalRepoCatalogSettlement: s.awaitLocalRepoCatalogSettlement,
fetchProjectGroupsForAllHosts: s.fetchProjectGroupsForAllHosts,
fetchFolderWorkspacesForAllHosts: s.fetchFolderWorkspacesForAllHosts,
fetchAllWorktrees: s.fetchAllWorktrees,
fetchWorktrees: s.fetchWorktrees,
fetchWorktreeLineage: s.fetchWorktreeLineage,
fetchOrcaProfiles: s.fetchOrcaProfiles,
fetchSettings: s.fetchSettings,
awaitOwnerWorktreeVisibilityDefaultsHydration:
s.awaitOwnerWorktreeVisibilityDefaultsHydration,
fetchKeybindings: s.fetchKeybindings,
initGitHubCache: s.initGitHubCache,
hydrateWorkspaceSession: s.hydrateWorkspaceSession,
hydrateTabsSession: s.hydrateTabsSession,
hydrateEditorSession: s.hydrateEditorSession,
hydrateBrowserSession: s.hydrateBrowserSession,
fetchBrowserSessionProfiles: s.fetchBrowserSessionProfiles,
reconnectPersistedTerminals: s.reconnectPersistedTerminals,
setTerminalStartupRestorationReady: s.setTerminalStartupRestorationReady,
setDeferredSshReconnectTargets: s.setDeferredSshReconnectTargets,
setSshConnectionState: s.setSshConnectionState,
hydratePersistedUI: s.hydratePersistedUI,
setHydrationSucceeded: s.setHydrationSucceeded,
pruneLastVisitedTimestamps: s.pruneLastVisitedTimestamps,
seedActiveWorktreeLastVisitedIfMissing: s.seedActiveWorktreeLastVisitedIfMissing
}))
)
}
@@ -1,8 +1,8 @@
import { useEffect, useRef } from 'react'
import { useShallow } from 'zustand/react/shallow'
import { syncZoomCSSVar } from '@/lib/ui-zoom'
import { installCodexDetachedPaneRestartExecutor } from '@/components/terminal-pane/codex-detached-pane-restart-scheduler'
import { useAppStore } from '../store'
import { useStartupActions } from './use-app-startup-actions'
import { WORKTREE_REFRESH_CONCURRENCY } from '../store/slices/worktrees'
import { sweepRestoredCodexPanesForStaleAccounts } from '../lib/codex-stale-pane-sweep'
import { fetchWorkspaceSessionWithRuntimeHostOwners } from '../lib/workspace-session-host-persistence'
@@ -46,40 +46,6 @@ async function listRuntimeSessionHostIdsForStartup(): Promise<ExecutionHostId[]>
}
}
function useStartupActions() {
// Why: consolidate action refs into one useShallow subscription so React runs one equality check per store mutation instead of one per action.
return useAppStore(
useShallow((s) => ({
fetchReposForAllHosts: s.fetchReposForAllHosts,
awaitLocalRepoCatalogSettlement: s.awaitLocalRepoCatalogSettlement,
fetchProjectGroupsForAllHosts: s.fetchProjectGroupsForAllHosts,
fetchFolderWorkspacesForAllHosts: s.fetchFolderWorkspacesForAllHosts,
fetchAllWorktrees: s.fetchAllWorktrees,
fetchWorktrees: s.fetchWorktrees,
fetchWorktreeLineage: s.fetchWorktreeLineage,
fetchOrcaProfiles: s.fetchOrcaProfiles,
fetchSettings: s.fetchSettings,
awaitOwnerWorktreeVisibilityDefaultsHydration:
s.awaitOwnerWorktreeVisibilityDefaultsHydration,
fetchKeybindings: s.fetchKeybindings,
initGitHubCache: s.initGitHubCache,
hydrateWorkspaceSession: s.hydrateWorkspaceSession,
hydrateTabsSession: s.hydrateTabsSession,
hydrateEditorSession: s.hydrateEditorSession,
hydrateBrowserSession: s.hydrateBrowserSession,
fetchBrowserSessionProfiles: s.fetchBrowserSessionProfiles,
reconnectPersistedTerminals: s.reconnectPersistedTerminals,
setTerminalStartupRestorationReady: s.setTerminalStartupRestorationReady,
setDeferredSshReconnectTargets: s.setDeferredSshReconnectTargets,
setSshConnectionState: s.setSshConnectionState,
hydratePersistedUI: s.hydratePersistedUI,
setHydrationSucceeded: s.setHydrationSucceeded,
pruneLastVisitedTimestamps: s.pruneLastVisitedTimestamps,
seedActiveWorktreeLastVisitedIfMissing: s.seedActiveWorktreeLastVisitedIfMissing
}))
)
}
/**
* Runs the renderer's one-shot boot chain: settings, persisted UI, the local repo catalog,
* the workspace session, SSH reconnect, and terminal restoration — then unlocks the session