From 917a5e63267b699e4a5ffc612c61de8090110da3 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Wed, 12 Aug 2026 23:24:38 -0700 Subject: [PATCH] type(store): mark worktree lineage maps readonly Widen reuseEqualRecordMap and the renderer lineage store fields to Readonly> so a reused no-op refresh map cannot be mutated in place. Co-authored-by: Orca --- .../src/store/slices/repo-identity-reconcile.ts | 6 +++--- .../src/store/slices/worktree-helpers.ts | 4 ++-- src/renderer/src/store/slices/worktrees.ts | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/renderer/src/store/slices/repo-identity-reconcile.ts b/src/renderer/src/store/slices/repo-identity-reconcile.ts index 4809b23adda..ea823a00108 100644 --- a/src/renderer/src/store/slices/repo-identity-reconcile.ts +++ b/src/renderer/src/store/slices/repo-identity-reconcile.ts @@ -85,9 +85,9 @@ export function reconcileFetchedRepos( * changed — so a cloned no-op refresh leaves Object.is subscribers untouched. */ export function reuseEqualRecordMap( - previous: Record, - next: Record -): Record { + previous: Readonly>, + next: Readonly> +): Readonly> { const nextKeys = Object.keys(next) const previousKeys = Object.keys(previous) let identical = nextKeys.length === previousKeys.length diff --git a/src/renderer/src/store/slices/worktree-helpers.ts b/src/renderer/src/store/slices/worktree-helpers.ts index 9675be9a693..c2980357ee8 100644 --- a/src/renderer/src/store/slices/worktree-helpers.ts +++ b/src/renderer/src/store/slices/worktree-helpers.ts @@ -89,8 +89,8 @@ export type ActiveWorktreeStateTransition = (state: AppState) => { export type WorktreeSlice = { worktreesByRepo: Record detectedWorktreesByRepo: Record - worktreeLineageById: Record - workspaceLineageByChildKey: Record + worktreeLineageById: Readonly> + workspaceLineageByChildKey: Readonly> activeWorktreeId: string | null activeWorkspaceKey: WorkspaceKey | null activeWorkspaceExecutionHostId: ExecutionHostId | null diff --git a/src/renderer/src/store/slices/worktrees.ts b/src/renderer/src/store/slices/worktrees.ts index 01e76aacf93..00e421cb7df 100644 --- a/src/renderer/src/store/slices/worktrees.ts +++ b/src/renderer/src/store/slices/worktrees.ts @@ -1391,8 +1391,8 @@ async function listWorktreeLineageForRuntime( settings: AppState['settings'], options: BackgroundRuntimeRefreshOptions = {} ): Promise<{ - worktreeLineageById: Record - workspaceLineageByChildKey: Record + worktreeLineageById: Readonly> + workspaceLineageByChildKey: Readonly> }> { const target = getActiveRuntimeTarget(settings) type LineageListResponse = { @@ -1575,8 +1575,8 @@ function applyHostLineageRefresh( set: Parameters>[0], hostId: ExecutionHostId, lineage: { - worktreeLineageById: Record - workspaceLineageByChildKey: Record + worktreeLineageById: Readonly> + workspaceLineageByChildKey: Readonly> } ): void { set((s) => { @@ -1648,8 +1648,8 @@ function mergeLineageForHost( 'repos' | 'settings' | 'worktreesByRepo' | 'detectedWorktreesByRepo' | 'worktreeLineageById' >, hostId: ExecutionHostId, - lineage: Record -): Record { + lineage: Readonly> +): Readonly> { const next: Record = {} for (const [worktreeId, existing] of Object.entries(state.worktreeLineageById)) { if (getWorktreeHostId(state, worktreeId) !== hostId) { @@ -1672,8 +1672,8 @@ function mergeWorkspaceLineageForHost( | 'workspaceLineageByChildKey' >, hostId: ExecutionHostId, - lineage: Record -): Record { + lineage: Readonly> +): Readonly> { const next: Record = {} for (const [childKey, existing] of Object.entries(state.workspaceLineageByChildKey)) { const childScope = parseWorkspaceKey(existing.childWorkspaceKey)