type(store): mark worktree lineage maps readonly

Widen reuseEqualRecordMap and the renderer lineage store fields to
Readonly<Record<...>> so a reused no-op refresh map cannot be mutated
in place.

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Neil
2026-08-12 23:24:38 -07:00
co-authored by Orca
parent 49ffbe5169
commit 917a5e6326
3 changed files with 13 additions and 13 deletions
@@ -85,9 +85,9 @@ export function reconcileFetchedRepos(
* changed — so a cloned no-op refresh leaves Object.is subscribers untouched.
*/
export function reuseEqualRecordMap<T>(
previous: Record<string, T>,
next: Record<string, T>
): Record<string, T> {
previous: Readonly<Record<string, T>>,
next: Readonly<Record<string, T>>
): Readonly<Record<string, T>> {
const nextKeys = Object.keys(next)
const previousKeys = Object.keys(previous)
let identical = nextKeys.length === previousKeys.length
@@ -89,8 +89,8 @@ export type ActiveWorktreeStateTransition = (state: AppState) => {
export type WorktreeSlice = {
worktreesByRepo: Record<string, Worktree[]>
detectedWorktreesByRepo: Record<string, DetectedWorktreeListResult>
worktreeLineageById: Record<string, WorktreeLineage>
workspaceLineageByChildKey: Record<WorkspaceKey, WorkspaceLineage>
worktreeLineageById: Readonly<Record<string, WorktreeLineage>>
workspaceLineageByChildKey: Readonly<Record<WorkspaceKey, WorkspaceLineage>>
activeWorktreeId: string | null
activeWorkspaceKey: WorkspaceKey | null
activeWorkspaceExecutionHostId: ExecutionHostId | null
+8 -8
View File
@@ -1391,8 +1391,8 @@ async function listWorktreeLineageForRuntime(
settings: AppState['settings'],
options: BackgroundRuntimeRefreshOptions = {}
): Promise<{
worktreeLineageById: Record<string, WorktreeLineage>
workspaceLineageByChildKey: Record<string, WorkspaceLineage>
worktreeLineageById: Readonly<Record<string, WorktreeLineage>>
workspaceLineageByChildKey: Readonly<Record<string, WorkspaceLineage>>
}> {
const target = getActiveRuntimeTarget(settings)
type LineageListResponse = {
@@ -1575,8 +1575,8 @@ function applyHostLineageRefresh(
set: Parameters<StateCreator<AppState>>[0],
hostId: ExecutionHostId,
lineage: {
worktreeLineageById: Record<string, WorktreeLineage>
workspaceLineageByChildKey: Record<string, WorkspaceLineage>
worktreeLineageById: Readonly<Record<string, WorktreeLineage>>
workspaceLineageByChildKey: Readonly<Record<string, WorkspaceLineage>>
}
): void {
set((s) => {
@@ -1648,8 +1648,8 @@ function mergeLineageForHost(
'repos' | 'settings' | 'worktreesByRepo' | 'detectedWorktreesByRepo' | 'worktreeLineageById'
>,
hostId: ExecutionHostId,
lineage: Record<string, WorktreeLineage>
): Record<string, WorktreeLineage> {
lineage: Readonly<Record<string, WorktreeLineage>>
): Readonly<Record<string, WorktreeLineage>> {
const next: Record<string, WorktreeLineage> = {}
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<string, WorkspaceLineage>
): Record<string, WorkspaceLineage> {
lineage: Readonly<Record<string, WorkspaceLineage>>
): Readonly<Record<string, WorkspaceLineage>> {
const next: Record<string, WorkspaceLineage> = {}
for (const [childKey, existing] of Object.entries(state.workspaceLineageByChildKey)) {
const childScope = parseWorkspaceKey(existing.childWorkspaceKey)