---
phase: 31-selector-optimization
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - jam-ui/src/store/features/mixersSlice.js
  - jam-ui/src/hooks/useMixerHelper.js
autonomous: true

must_haves:
  truths:
    - "Single mixer field change triggers 1-3 selector runs instead of 18+"
    - "useMixerHelper uses 6 composed selectors with shallowEqual"
    - "Composed selectors use createSelector to memoize grouped state objects (grouping IS derived data - creates new object from multiple inputs)"
  artifacts:
    - path: "jam-ui/src/store/features/mixersSlice.js"
      provides: "Composed memoized selectors for mixer state"
      exports: ["selectCoreMixerState", "selectTrackMixerState", "selectMixerLookupTables"]
      contains: "createSelector"
    - path: "jam-ui/src/hooks/useMixerHelper.js"
      provides: "Optimized hook using composed selectors"
      contains: "shallowEqual"
  key_links:
    - from: "jam-ui/src/hooks/useMixerHelper.js"
      to: "jam-ui/src/store/features/mixersSlice.js"
      via: "import composed selectors"
      pattern: "import.*selectCoreMixerState.*from.*mixersSlice"
    - from: "jam-ui/src/hooks/useMixerHelper.js"
      to: "react-redux"
      via: "shallowEqual comparison"
      pattern: "useSelector\\(.*shallowEqual\\)"
---

<objective>
Consolidate 18+ individual Redux selectors in useMixerHelper into 3-5 composed memoized selectors using createSelector from Redux Toolkit.

Purpose: Reduce selector overhead from 18+ independent subscriptions and equality checks per Redux action to 3-5 memoized selectors that only recompute when their specific inputs change. This eliminates unnecessary re-renders when unrelated mixer fields change.

Output: Composed selectors in mixersSlice.js and refactored useMixerHelper.js using shallowEqual comparison for object returns.
</objective>

<execution_context>
@/Users/nuwan/.claude/get-shit-done/workflows/execute-plan.md
@/Users/nuwan/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/31-selector-optimization/31-RESEARCH.md

# Key source files
@jam-ui/src/store/features/mixersSlice.js
@jam-ui/src/hooks/useMixerHelper.js
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create composed selectors in mixersSlice.js</name>
  <files>jam-ui/src/store/features/mixersSlice.js</files>
  <action>
Add createSelector from Redux Toolkit and create three composed memoized selectors that group related mixer state:

1. Import createSelector at top:
```javascript
import { createSlice, createSelector } from '@reduxjs/toolkit';
```

2. Add composed selectors after the existing simple selectors (near end of file, before the parameterized selectors):

```javascript
// Composed memoized selectors for useMixerHelper optimization
// These group related data that's typically used together

export const selectCoreMixerState = createSelector(
  [selectChatMixer, selectBroadcastMixer, selectRecordingMixer],
  (chatMixer, broadcastMixer, recordingMixer) => ({
    chatMixer,
    broadcastMixer,
    recordingMixer
  })
);

export const selectTrackMixerState = createSelector(
  [
    selectRecordingTrackMixers,
    selectBackingTrackMixers,
    selectJamTrackMixers,
    selectMetronomeTrackMixers,
    selectAdhocTrackMixers
  ],
  (recordingTrackMixers, backingTrackMixers, jamTrackMixers, metronomeTrackMixers, adhocTrackMixers) => ({
    recordingTrackMixers,
    backingTrackMixers,
    jamTrackMixers,
    metronomeTrackMixers,
    adhocTrackMixers
  })
);

export const selectMixerLookupTables = createSelector(
  [selectAllMixers, selectMixersByResourceId, selectMixersByTrackId],
  (allMixers, mixersByResourceId, mixersByTrackId) => ({
    allMixers,
    mixersByResourceId,
    mixersByTrackId
  })
);

export const selectMasterPersonalMixers = createSelector(
  [selectMasterMixers, selectPersonalMixers],
  (masterMixers, personalMixers) => ({
    masterMixers,
    personalMixers
  })
);

export const selectMixerMetadata = createSelector(
  [selectMetronome, selectMetronomeSettings, selectMediaSummary, selectNoAudioUsers, selectClientsWithAudioOverride, selectMixersReady],
  (metronome, metronomeSettings, mediaSummary, noAudioUsers, clientsWithAudioOverride, isReady) => ({
    metronome,
    metronomeSettings,
    mediaSummary,
    noAudioUsers,
    clientsWithAudioOverride,
    isReady
  })
);

export const selectSimulatedCategoryMixers = createSelector(
  [selectSimulatedMusicCategoryMixers, selectSimulatedChatCategoryMixers],
  (simulatedMusicCategoryMixers, simulatedChatCategoryMixers) => ({
    simulatedMusicCategoryMixers,
    simulatedChatCategoryMixers
  })
);
```

These selectors use createSelector which:
- Memoizes the result based on input selectors
- Only recomputes when any input selector returns a different reference
- Returns the same object reference when inputs haven't changed
  </action>
  <verify>
Run syntax check:
```bash
cd jam-ui && node -c src/store/features/mixersSlice.js
```

Verify createSelector import and exports:
```bash
grep -E "import.*createSelector.*@reduxjs/toolkit" jam-ui/src/store/features/mixersSlice.js
grep -E "export const select(CoreMixerState|TrackMixerState|MixerLookupTables)" jam-ui/src/store/features/mixersSlice.js
```
  </verify>
  <done>
mixersSlice.js exports 6 composed selectors using createSelector:
- selectCoreMixerState (chatMixer, broadcastMixer, recordingMixer)
- selectTrackMixerState (5 track mixer arrays)
- selectMixerLookupTables (allMixers, mixersByResourceId, mixersByTrackId)
- selectMasterPersonalMixers (masterMixers, personalMixers)
- selectMixerMetadata (metronome, settings, mediaSummary, noAudioUsers, etc.)
- selectSimulatedCategoryMixers (simulatedMusic, simulatedChat)
  </done>
</task>

<task type="auto">
  <name>Task 2: Refactor useMixerHelper to use composed selectors with shallowEqual</name>
  <files>jam-ui/src/hooks/useMixerHelper.js</files>
  <action>
Replace 21 individual useSelector calls (lines 72-92) with 6 composed selector calls using shallowEqual:

1. Update imports at top of file:

Replace the long list of individual selector imports with:
```javascript
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import { selectActiveSession, selectInSession } from '../store/features/activeSessionSlice';
import {
  // Composed selectors (use these instead of individual ones)
  selectCoreMixerState,
  selectTrackMixerState,
  selectMixerLookupTables,
  selectMasterPersonalMixers,
  selectMixerMetadata,
  selectSimulatedCategoryMixers,
  // Actions (still needed)
  setMasterMixers,
  setPersonalMixers,
  organizeMixers,
  updateMixer,
  setChatMixer,
  setBackingTracks as setBackingTracksAction,
  setJamTracks as setJamTracksAction,
  setRecordedTracks as setRecordedTracksAction,
  setMetronome as setMetronomeAction,
  setMediaSummary,
  setSimulatedMusicCategoryMixers as setSimulatedMusicAction,
  setSimulatedChatCategoryMixers as setSimulatedChatAction,
  setRecordingTrackMixers,
  setBackingTrackMixers,
  setJamTrackMixers,
  setMetronomeTrackMixers,
  setAdhocTrackMixers
} from '../store/features/mixersSlice';
```

2. Replace the 21 individual useSelector calls (lines 72-92) with composed selector calls:

OLD (remove these 21 lines - lines 72-92):
```javascript
const chatMixer = useSelector(selectChatMixer);
const broadcastMixer = useSelector(selectBroadcastMixer);
const recordingMixer = useSelector(selectRecordingMixer);
const recordingTrackMixers = useSelector(selectRecordingTrackMixers);
const backingTrackMixers = useSelector(selectBackingTrackMixers);
const jamTrackMixers = useSelector(selectJamTrackMixers);
const metronomeTrackMixers = useSelector(selectMetronomeTrackMixers);
const adhocTrackMixers = useSelector(selectAdhocTrackMixers);
const masterMixers = useSelector(selectMasterMixers);
const personalMixers = useSelector(selectPersonalMixers);
const allMixers = useSelector(selectAllMixers);
const mixersByResourceId = useSelector(selectMixersByResourceId);
const mixersByTrackId = useSelector(selectMixersByTrackId);
const metronome = useSelector(selectMetronome);
const metronomeSettings = useSelector(selectMetronomeSettings);
const mediaSummary = useSelector(selectMediaSummary);
const noAudioUsers = useSelector(selectNoAudioUsers);
const clientsWithAudioOverride = useSelector(selectClientsWithAudioOverride);
const simulatedMusicCategoryMixers = useSelector(selectSimulatedMusicCategoryMixers);
const simulatedChatCategoryMixers = useSelector(selectSimulatedChatCategoryMixers);
const isReadyRedux = useSelector(selectMixersReady);
```

NEW (replace with):
```javascript
// Composed selectors with shallowEqual - reduces 21 subscriptions to 6
const coreMixers = useSelector(selectCoreMixerState, shallowEqual);
const trackMixers = useSelector(selectTrackMixerState, shallowEqual);
const lookupTables = useSelector(selectMixerLookupTables, shallowEqual);
const masterPersonal = useSelector(selectMasterPersonalMixers, shallowEqual);
const metadata = useSelector(selectMixerMetadata, shallowEqual);
const simulatedMixers = useSelector(selectSimulatedCategoryMixers, shallowEqual);

// Destructure for backward compatibility with rest of hook
const { chatMixer, broadcastMixer, recordingMixer } = coreMixers;
const { recordingTrackMixers, backingTrackMixers, jamTrackMixers,
        metronomeTrackMixers, adhocTrackMixers } = trackMixers;
const { allMixers, mixersByResourceId, mixersByTrackId } = lookupTables;
const { masterMixers, personalMixers } = masterPersonal;
const { metronome, metronomeSettings, mediaSummary, noAudioUsers,
        clientsWithAudioOverride, isReady: isReadyRedux } = metadata;
const { simulatedMusicCategoryMixers, simulatedChatCategoryMixers } = simulatedMixers;
```

This preserves all existing variable names used throughout the hook while reducing selector subscriptions from 21 to 6.

IMPORTANT: Keep all other code in useMixerHelper.js unchanged - only modify imports and the selector calls section. The remaining useSelector calls (lines 95-97 for tracks, 100-101 for mix mode, 109-110 for session) stay as-is.
  </action>
  <verify>
Run syntax check:
```bash
cd jam-ui && node -c src/hooks/useMixerHelper.js
```

Verify shallowEqual usage (should appear 7 times: 1 import + 6 useSelector calls):
```bash
grep -c "shallowEqual" jam-ui/src/hooks/useMixerHelper.js
# Expected: 7
```

Verify composed selector imports:
```bash
grep "selectCoreMixerState" jam-ui/src/hooks/useMixerHelper.js
grep "selectTrackMixerState" jam-ui/src/hooks/useMixerHelper.js
grep "selectMixerLookupTables" jam-ui/src/hooks/useMixerHelper.js
```

Verify old individual selectors removed (lines 72-92 pattern should NOT exist):
```bash
grep -E "useSelector\(select(ChatMixer|BroadcastMixer|RecordingMixer|RecordingTrackMixers|BackingTrackMixers|JamTrackMixers|MetronomeTrackMixers|AdhocTrackMixers|MasterMixers|PersonalMixers|AllMixers|MixersByResourceId|MixersByTrackId|Metronome[^S]|MetronomeSettings|MediaSummary|NoAudioUsers|ClientsWithAudioOverride|SimulatedMusicCategoryMixers|SimulatedChatCategoryMixers|MixersReady)\)" jam-ui/src/hooks/useMixerHelper.js
# Expected: no matches (empty output)
```

Count total useSelector calls (should be exactly 13):
```bash
grep -c "= useSelector(" jam-ui/src/hooks/useMixerHelper.js
# Expected: 13 (6 composed + 3 track selectors + 2 mix mode + 2 session selectors)
```
  </verify>
  <done>
useMixerHelper.js refactored:
- 21 individual selectors (lines 72-92) removed
- 6 composed selectors with shallowEqual added
- Total useSelector calls reduced from 28 to 13
- All existing variable names preserved via destructuring for backward compatibility
  </done>
</task>

</tasks>

<verification>
After both tasks:

1. **Syntax validation:**
```bash
cd jam-ui && node -c src/store/features/mixersSlice.js && node -c src/hooks/useMixerHelper.js
```

2. **Build check:**
```bash
cd jam-ui && npm run build 2>&1 | head -50
```

3. **Selector count verification:**
- mixersSlice.js should export 6 new composed selectors
- useMixerHelper.js should have exactly 13 useSelector calls (6 composed + 7 other)
- Old individual selector pattern (lines 72-92) should be completely removed

4. **Pattern verification:**
```bash
grep -E "createSelector|shallowEqual" jam-ui/src/store/features/mixersSlice.js jam-ui/src/hooks/useMixerHelper.js
```
</verification>

<success_criteria>
- [ ] mixersSlice.js imports createSelector from @reduxjs/toolkit
- [ ] mixersSlice.js exports 6 composed selectors (selectCoreMixerState, selectTrackMixerState, selectMixerLookupTables, selectMasterPersonalMixers, selectMixerMetadata, selectSimulatedCategoryMixers)
- [ ] useMixerHelper.js imports shallowEqual from react-redux
- [ ] useMixerHelper.js uses 6 composed selectors with shallowEqual comparison
- [ ] Old individual selectors (lines 72-92) completely removed
- [ ] Total useSelector calls in useMixerHelper.js equals exactly 13
- [ ] All existing variable names preserved via destructuring
- [ ] Syntax validation passes for both files
- [ ] Build completes without errors
</success_criteria>

<output>
After completion, create `.planning/phases/31-selector-optimization/31-01-SUMMARY.md`
</output>
