Fragmenting global state across Zustand and Jotai (or any two stores) means some components subscribe to one source of truth while others subscribe to another, and no selector sees the complete application state. Cross-store updates require manual synchronization that inevitably drifts, causing UI components to render stale data, race conditions on login/logout flows, and bugs that only reproduce when state transitions span both stores in a specific order.
High because state fragmentation produces race conditions and stale renders that are expensive to reproduce and debug.
Choose one store and move every piece of client state into it. Create a single entry point at src/store/index.ts and delete the secondary store module entirely once migration completes.
// src/store/index.ts
import { create } from 'zustand'
export const useStore = create((set) => ({
user: null,
setUser: (user) => set({ user }),
}))
Server-state libraries like TanStack Query are not in conflict and should remain alongside your chosen client store.
ID: ai-slop-code-drift.ui-stack-drift.dual-state-manager
Severity: high
What to look for: Apply the three-condition rule against this exact global state allowlist: redux, @reduxjs/toolkit, react-redux, zustand, jotai, valtio, mobx, mobx-react, recoil, effector, @xstate/react, nanostores, @nanostores/react. Treat redux + @reduxjs/toolkit + react-redux as a single Redux unit (they always coexist). EXCEPT: server-state libraries (@tanstack/react-query, swr, urql, @apollo/client) are NOT global state managers — they manage server cache, which can legitimately coexist with a client-state library. Count all REMAINING libraries that meet all three conditions (at least 1 non-escape-hatch importing file).
Pass criteria: 0 or 1 global state managers from the allowlist actively used. Report even on pass: "Canonical state manager: [name] ([N] importing files)."
Fail criteria: 2 or more global state managers from the allowlist (excluding server-state libs) actively used.
Skip (N/A) when: 0 global state managers from the allowlist appear in RUNTIME_DEPS.
Detail on fail: "2 active state managers: 'zustand' (8 files) AND 'jotai' (4 files). Pick one for global state."
Remediation: Multiple global state managers fragment your application state — some components subscribe to one, some to the other, neither sees the full picture. Pick one and migrate:
// Bad: store split between two libs
// src/store/zustand-store.ts AND src/atoms/jotai-atoms.ts
// Good: single store
// src/store/index.ts using your chosen library
import { create } from 'zustand'
export const useStore = create((set) => ({ ... }))