Skip to Content

Store Architecture

Overview

AppStateStore (src/state/AppStateStore.ts, ~21,847 lines) is a custom, purpose-built store that manages all application state in a single centralized object. Rather than relying on third-party libraries like Redux or Zustand, Claude Code implements its own store with direct mutation methods and a synchronous subscriber notification model.

The store is intentionally not immutable. State is mutated in-place through class methods, which simplifies the programming model but requires careful change-detection logic downstream.

Mutation Flow

Actions originate from user interactions, tool responses, or background processes. Each action calls a mutation method on the store, which updates the internal state object and then synchronously notifies all registered subscribers.

State Shape

The top-level state object contains clearly separated slices:

interface AppState { messages: Message[]; tasks: TaskState; agents: AgentState; permissions: PermissionState; notifications: Notification[]; overlays: OverlayState; uiState: UIState; // ... additional slices }

Each slice owns a distinct domain of the application and is accessed through typed property paths on the store.

Mutation Patterns

State is updated through direct mutation methods defined on the AppStateStore class. There is no action-reducer indirection:

class AppStateStore { addMessage(message: Message): void { this.state.messages.push(message); this.notify(); } updatePermission(id: string, granted: boolean): void { const perm = this.state.permissions.pending.find(p => p.id === id); if (perm) { perm.granted = granted; this.notify(); } } }

Every mutation method ends with a call to this.notify(), which fans out to all subscribers.

Slice Organization

SlicePurposeComplexity
messagesConversation history, streaming tokens, tool resultsVery High
tasksBackground task tracking, progress, cancellationHigh
agentsSub-agent lifecycle, spawning, completionHigh
permissionsTool-use approvals, auto-grant rulesMedium
notificationsUser-facing alerts, toasts, bannersLow
overlaysModals, dialogs, menusLow
uiStateScroll position, focus, input stateMedium

Subscription Model

The store implements a straightforward observer pattern. Subscribers register a callback and are notified on every state change:

const unsubscribe = store.subscribe(() => { // Called on ANY state mutation const currentState = store.getState(); // Decide locally whether to act });

Because subscribers are notified on every mutation (not per-slice), downstream consumers must implement their own filtering to avoid unnecessary work. This is where selectors and change detection become critical.

Initialization

At application startup the store goes through a defined initialization sequence:

  1. Load persisted state — Read previously saved state from disk (conversation history, user preferences).
  2. Apply defaults — Fill in any missing slices with default values.
  3. Register side-effect handlers — Wire up persistence, notification, and sync handlers via onChangeAppState.
  4. Signal ready — Mark the store as initialized so the UI can begin rendering.

Design Patterns

The store architecture relies on three core patterns:

  • Singleton — A single AppStateStore instance exists for the entire application lifetime. All consumers share the same reference.
  • Observer — Subscribers register callbacks that are invoked on state changes, decoupling the store from its consumers.
  • Mediator — The store mediates communication between otherwise unrelated components (e.g., the permission system and the message list).
Last updated on