State Management
Relevant source files
The following files were used as context for generating this wiki page:
- apps/app/src/react-app/ARCHITECTURE.md
- apps/app/src/react-app/domains/settings/state/extensions-store.ts
- apps/app/src/react-app/domains/settings/state/debug-view-model.ts
- apps/app/src/react-app/shell/architecture-mismatch-gate.tsx
- evals/react-session-flows.md
- ee/apps/den-web/app/(den)/dashboard/_components/github-integration-screen.tsx/dashboard/_components/github-integration-screen.tsx)
State management in the OpenWork React application is structured around a domain-based architecture that prioritizes explicit ownership, local-first persistence, and clear data flow boundaries. The system is transitioning from a centralized "context bag" (formerly used in the Solid runtime) to a more modular approach using Zustand for global state, TanStack Query for server-side state, and domain-specific stores for feature-localized logic. Sources: apps/app/src/react-app/ARCHITECTURE.md:1-15, apps/app/src/react-app/ARCHITECTURE.md:58-65
Architecture and Core Principles
The state architecture is divided into three primary layers: Kernel (app-wide state), Domain (feature-scoped state), and Route (identity state derived from URLs). Sources: apps/app/src/react-app/ARCHITECTURE.md:4-15
State Layers and Ownership
- Kernel Store: The
react-app/kernel/store.ts(Zustand) serves as the primary replacement for global context. It handles core application settings and cross-domain synchronization. Sources: apps/app/src/react-app/ARCHITECTURE.md:58-60 - Domain Stores: Each product domain (e.g.,
session/,settings/,workspace/) owns its specific state. For example,extensions-store.tsmanages the lifecycle of skills and plugins. Sources: apps/app/src/react-app/ARCHITECTURE.md:21-35 - Route State: Workspace and session identities are derived directly from URL parameters (
workspaceId,sessionId) rather than mutable global state. This ensures that the UI state is always consistent with the active route. Sources: apps/app/src/react-app/ARCHITECTURE.md:76-85
Data Flow and Provider Composition
State is injected into the application through a strictly ordered composition of providers.
The diagram above shows the linear hierarchy of state providers that bootstrap the React runtime. Sources: apps/app/src/react-app/ARCHITECTURE.md:40-52
Domain-Specific State Management
Extensions Store
The ExtensionsStore manages the complex state of local and remote skills, MCP servers, and cloud-imported plugins. It utilizes useSyncExternalStore to provide a thread-safe snapshot of the extension ecosystem to the React UI. Sources: apps/app/src/react-app/domains/settings/state/extensions-store.ts:1044-1046
Key responsibilities include:
- Skill Lifecycle: Reading and writing local skills to
.opencode/skills/via desktop IPC. Sources: apps/app/src/react-app/domains/settings/state/extensions-store.ts:60-75 - Cloud Sync: Managing synchronization with "OpenWork Cloud" and "Den" (different-ai cloud) for organization-level skill hubs. Sources: apps/app/src/react-app/domains/settings/state/extensions-store.ts:405-415
- Plugin Discovery: Scanning repositories for Claude-compatible manifests (
plugin.json,marketplace.json). Sources: ee/apps/den-web/app/(den)/dashboard/_components/github-integration-screen.tsx:480-500
| Feature | Data Structure / Type | Description |
|---|---|---|
| Plugin List | PluginListEntry[] | Tracks installed plugins and their source (config, global, or project). |
| Cloud Skills | Record<string, CloudImportedSkill> | Map of skill IDs to their local installation metadata. |
| Hub Repos | HubSkillRepo[] | Configuration for GitHub-based skill repositories. |
| Audit Logs | OpenworkAuditEntry[] | Diagnostics for tracking changes to the workspace state. |
Sources: apps/app/src/react-app/domains/settings/state/extensions-store.ts:77-100, apps/app/src/react-app/domains/settings/pages/debug-view.tsx:55-70
Runtime and Debug Diagnostics
State management for runtime services (OpenCode engine, OpenWork server) is handled through the DebugViewModel. This module tracks the PID, version, and binary paths of sidecar processes. Sources: apps/app/src/react-app/domains/settings/state/debug-view-model.ts:114-125
The flow shows how diagnostics data is gathered from underlying services and presented through the debug surface. Sources: apps/app/src/react-app/domains/settings/pages/debug-view.tsx:160-185
Synchronization and Persistence
External Synchronization
OpenWork uses Server-Sent Events (SSE) to sync session state. The ReactSessionRuntime subscribes to the OpenCode event stream to populate local transcript caches. If this subscription is interrupted (e.g., during a route change), the state management layer must ensure the stream survives or resumes. Sources: evals/react-session-flows.md:70-85, evals/react-session-flows.md:325-340
Local Persistence
Persistence is primarily "local-first," with critical configuration stored in opencode.jsonc.
- LocalStorage: Used for non-critical UI state such as onboarding flags and recent search queries. Sources: apps/app/src/react-app/domains/settings/state/debug-view-model.ts:28-35
- Workspace Config:
extensions-store.tsperforms direct file mutations on.opencode/openwork.jsonto persist workspace-specific settings like cloud imports. Sources: apps/app/src/react-app/domains/settings/state/extensions-store.ts:262-280
State Mismatch Handling
The ArchitectureMismatchGate provides a specialized state layer that blocks app startup if the binary architecture does not match the system. It uses a custom reducer to track whether the check has been performed and what the result was. Sources: apps/app/src/react-app/shell/architecture-mismatch-gate.tsx:35-50
Conclusion
State management in OpenWork is transitioning toward a highly decoupled, domain-driven model. By shifting away from a single global context and toward specialized stores (Zustand) and route-driven identity, the system achieves better performance and more predictable data flows, particularly when handling complex local-first operations like sidecar process management and skill discovery. Sources: apps/app/src/react-app/ARCHITECTURE.md:21-30, apps/app/src/react-app/ARCHITECTURE.md:58-70