What’s a clean way to handle optimistic updates without mutating my normalized cache?

What’s up everyone? I’m wiring up a little UI state store and I’m trying to keep a normalized entity map, but my optimistic updates keep leaking mutations into other views (and then rollback gets weird).

type Entity = { id: string; name: string; version: number };

type State = {
  entities: Record<string, Entity>;
  lists: Record<string, string[]>; // listId -> entity ids
};

function optimisticRename(state: State, id: string, name: string): State {
  // I know this is sketchy, but it’s fast:
  state.entities[id].name = name;
  return state;
}

Given that I also need retries/rollback and don’t want to deep-clone the whole state every time, what mutation strategy or data structure pattern do you actually use here to keep updates isolated and predictable?