Claude Code uses a dual-state architecture: an immutable React-facing AppState for UI rendering and a mutable bootstrap/state.ts singleton for session-level bookkeeping. This separation keeps the React reconciler efficient (shallow comparisons on immutable snapshots) while giving hot-path code (telemetry, cost tracking, token budgets) zero-overhead mutable access.
Store Pattern
The store implementation in src/state/store.ts is a minimal Zustand-style reactive container. It provides three operations (getState, setState, and subscribe) with an optional onChange callback for side-effect wiring.
export type Store<T> = {
getState: () => T
setState: (updater: (prev: T) => T) => void
subscribe: (listener: Listener) => () => void
}
export function createStore<T>(
initialState: T,
onChange?: OnChange<T>,
): Store<T> {
let state = initialState
const listeners = new Set<Listener>()
return {
getState: () => state,
setState: (updater) => {
const prev = state
const next = updater(prev)
if (Object.is(next, prev)) return
state = next
onChange?.({ newState: next, oldState: prev })
for (const listener of listeners) listener()
},
subscribe: (listener) => {
listeners.add(listener)
return () => listeners.delete(listener)
},
}
}Key design choices:
- Referential equality bail-out:
Object.is(next, prev)prevents unnecessary re-renders when the updater returns the same reference. - Synchronous notification: listeners fire immediately after state changes, ensuring the UI stays consistent.
- onChange hook: the
AppStateStorepasses anonChangecallback that propagates diffs to external systems (bridge, telemetry, CCR metadata).
AppState
AppState is the primary UI state tree, defined in src/state/AppStateStore.ts. It is wrapped in DeepImmutable<> to enforce read-only access at the type level.
Top-Level Shape
name parameter is provided.UI State Fields
The AppState also carries fields that control the terminal UI layout:
expandedView: which panel is expanded ('none' | 'tasks' | 'teammates')footerSelection: which footer pill is focused ('tasks' | 'tmux' | 'bagel' | 'teams' | 'bridge' | 'companion' | null)isBriefOnly: whether the assistant is in brief/compact output modeactiveOverlays: set of active overlay IDs for Escape key coordination
Bridge/Remote State
A significant portion of AppState tracks the always-on bridge connection for remote control:
replBridgeEnabled: boolean
replBridgeConnected: boolean
replBridgeSessionActive: boolean
replBridgeReconnecting: boolean
replBridgeConnectUrl: string | undefined
replBridgeSessionUrl: string | undefinedThese fields allow the UI to show connection status, session URLs, and error states for the bridge subsystem.
Bootstrap State
The bootstrap state in src/bootstrap/state.ts is a plain mutable singleton. It stores session-scoped values that do not drive React rendering but are read on hot paths.
Telemetry Counters
Bootstrap state holds OpenTelemetry instrumentation:
meter: Meter | null
sessionCounter: AttributedCounter | null
locCounter: AttributedCounter | null
costCounter: AttributedCounter | null
tokenCounter: AttributedCounter | null
activeTimeCounter: AttributedCounter | nullThese counters are null until telemetry is initialized, allowing the bootstrap module to remain import-order agnostic.
Session Tracking
Several fields track session lifecycle for features like plan mode, skill invocation, and cron scheduling:
hasExitedPlanMode/needsPlanModeExitAttachment: plan mode lifecycleinvokedSkills: map of skill invocations, keyed byagentId:skillName, preserved across compactionsessionCronTasks: ephemeral cron tasks that die with the processregisteredHooks: SDK callbacks and plugin native hooks
Selectors
Selectors in src/state/selectors.ts derive computed state from AppState. They are pure functions with no side effects.
getViewedTeammateTask
Returns the currently viewed in-process teammate task, or undefined if no teammate is being viewed or the task is not an in-process teammate type.
function getViewedTeammateTask(
appState: Pick<AppState, 'viewingAgentTaskId' | 'tasks'>,
): InProcessTeammateTaskState | undefinedgetActiveAgentForInput
Determines where user input should be routed. Returns a discriminated union:
type ActiveAgentForInput =
| { type: 'leader' } // Input goes to the leader
| { type: 'viewed'; task: InProcessTeammateTaskState } // Input goes to viewed agent
| { type: 'named_agent'; task: LocalAgentTaskState } // Input goes to named agentUsed by input routing logic to direct user messages to the correct agent in multi-agent scenarios.
Dual-State Design Rationale
The split between AppState (immutable, store-based) and bootstrap state (mutable singleton) is intentional. AppState drives React rendering and must follow immutable update patterns for correct reconciliation. Bootstrap state holds values read on hot paths (cost tracking, token counting, telemetry) where the overhead of store updates and listener notification would be wasteful. The two layers are bridged by the onChangeAppState callback, which syncs relevant diffs to external systems.
This design avoids two common pitfalls:
- Over-rendering: putting telemetry counters in AppState would trigger React re-renders on every API response.
- Stale reads: using the store for hot-path reads would require
getState()calls that add overhead in tight loops.