ui/goose2/ui_improvements/state_management/phase-2-selector-read-layer.md
Phase 2: Introduce A Selector-First Read Layer
Status
Goal
useShallow only where object or array selector results benefit from shallow comparison.Scope
ui/goose2/src/features/chat/stores/chatSelectors.tsui/goose2/src/features/chat/stores/chatSessionSelectors.tsui/goose2/src/features/agents/stores/agentSelectors.tsui/goose2/src/features/projects/stores/projectSelectors.tsui/goose2/src/features/providers/hooks/useProviderInventory.tsOut Of Scope
useShallow around primitive selectors.Execution Steps
Review and classify selectors from Phase 1.
Add selector helpers only for common simple reads.
[] or {} values directly from selectors; use module-level constants or derive outside the selector.selectMessagesBySession, selectSessionStateByIdselectSessions, selectActiveSessionId, selectHasHydratedSessions, selectSessionsLoadingselectPersonas, selectPersonasLoading, selectSelectedProviderselectProjects, selectProjectsLoadingPrefer pure derived helpers for derived values.
sessions + activeSessionId; do not store a second activeSession attribute unless there is a strong reason.projects.sessionStateById record during render.AppShell active/home session derivation should remain derived from selected session state for now; do not add duplicate store attributes.Revisit hooks that mix read state and command functions.
usePersonas() currently returns personas and isLoading as well as command functions.AgentsView already reads personas and personasLoading directly with store selectors and only uses usePersonas() for command functions.Introduce useShallow selectively.
useShallow to compensate for a broad store or overly broad component dependency.useProviderInventory during this phase. It currently reads entries and loading separately; only group them with useShallow if that improves clarity without introducing object churn.useProviderInventory as separate entries and loading selectors for now. Grouping them with useShallow would mostly reduce two store subscriptions to one, but would not narrow rerenders or simplify consumers.Refactor consumers to use selector helpers where they remove duplication.
AppShell.tsx and Sidebar.tsx; do not refactor every selector candidate at once.AppShell as a later component-decomposition candidate rather than trying to hide its orchestration breadth behind selector helpers.Sidebar derived session grouping as a possible pure helper extraction, not as store state.agentStore, add selectors only for stable repeated domain reads such as personas. Do not add selector helpers for persona editor UI state in this phase, because that state is a Phase 4 extraction candidate.Validation
rg "useShallow|zustand/shallow" ui/goose2/srcrg "use[A-Za-z0-9]+Store\\(\\)" ui/goose2/srccd ui/goose2 && pnpm test -- useChat usePersonas Sidebar useProviderInventorySuccess Criteria
useShallow appears only on object or array selectors where it adds value.