1550→              )}
  1551→

  1552→          ) : filter === 'all' ? (
  1553→            // Grouped by AI triage category
  1554→

  1555→              {categoryOrder.map(category => (
  1556→                <AITriageSection
  1557→                  key={category}
  1558→                  category={category}
  1559→                  conversations={groupedByCategory[category] || []}
  1560→                  onSelectConversation={(leadId) => navigate(`/inbox?lead=${leadId}`)}
  1561→                  renderConversationCard={(conversation) => (
  1562→                    <EnhancedConversationCard
  1563→                      conversation={conversation}
  1564→                      onClick={() => navigate(`/inbox?lead=${conversation.lead.id}`)}
  1565→                      onAction={handleQuickAction}
  1566→                      onLongPress={handleConversationLongPress}
  1567→                      showQuickActions={true}
  1568→                      currentFilter={filter}
  1569→                      isSelected={selectedLeadIds.has(conversation.lead.id)}
  1570→                      onToggleSelect={() => toggleSelection(conversation.lead.id)}
  1571→                      showCheckbox={selectedLeadIds.size > 0}
  1572→                    />
  1573→                  )}
  1574→                  defaultExpanded={category === 'buying_signal' || category === 'callback_request'}
  1575→                />
  1576→              ))}
  1577→

  1578→          ) : (
  1579→            // Flat list for filtered views
  1580→

  1581→              {filteredConversations.map((conversation) => (
  1582→                <EnhancedConversationCard
  1583→                  key={conversation.lead.id}
  1584→                  conversation={conversation}
  1585→                  onClick={() => navigate(`/inbox?lead=${conversation.lead.id}`)}
  1586→                  onAction={handleQuickAction}
  1587→                  onLongPress={handleConversationLongPress}
  1588→                  showQuickActions={true}
  1589→                  currentFilter={filter}
  1590→                  isSelected={selectedLeadIds.has(conversation.lead.id)}
  1591→                  onToggleSelect={() => toggleSelection(conversation.lead.id)}
  1592→                  showCheckbox={selectedLeadIds.size > 0}
  1593→                />
  1594→              ))}
  1595→

  1596→          )}
  1597→

  1598→

  1599→
  1600→      {/* Conversation View */}
  1601→      <div className={`flex-1 flex flex-col ${!activeLeadId ? 'hidden md:flex' : 'flex'}`}>
  1602→        {activeConversation ? (
  1603→          <ConversationView
  1604→            lead={activeConversation.lead}
  1605→            messages={activeConversation.messages}
  1606→            onSend={sendMessage}
  1607→            onBack={() => navigate('/inbox')}
  1608→            onCall={handleCall}
  1609→            onViewProfile={() => navigate(`/leads/${activeConversation.lead.id}`)}
  1610→            onSchedule={handleSchedule}
  1611→            onMarkHot={handleMarkHot}
  1612→            onAddNote={handleAddNote}
  1613→            sending={sending}
  1614→            triage={activeTriage}
  1615→          />
  1616→        ) : conversationLoading ? (
  1617→

  1618→

  1619→

  1620→        ) : (
  1621→

  1622→

  1623→              <MessageSquare className="w-10 h-10 text-slate-400" />
  1624→

  1625→
Select a conversation

  1626→
Messages are AI-triaged by intent and priority

  1627→

  1628→        )}
  1629→

  1630→
  1631→      {/* Mini Lead Popup - appears on long press of conversation card */}
  1632→      {miniLeadPopup && (
  1633→        <MiniLeadPopup
  1634→          lead={miniLeadPopup.conversation.lead}
  1635→          position={miniLeadPopup.position}
  1636→          onClose={() => setMiniLeadPopup(null)}
  1637→          onCall={() => startCall(miniLeadPopup.conversation.lead)}
  1638→          onSchedule={() => navigate(`/leads/${miniLeadPopup.conversation.lead.id}?action=callback`)}
  1639→          onMessage={() => navigate(`/inbox?lead=${miniLeadPopup.conversation.lead.id}`)}
  1640→          onViewProfile={() => navigate(`/leads/${miniLeadPopup.conversation.lead.id}`)}
  1641→        />
  1642→      )}
  1643→
  1644→      {/* New Message Modal */}
  1645→      {showNewMessageModal && (
  1646→        <NewMessageModal
  1647→          onClose={() => setShowNewMessageModal(false)}
  1648→          onSelectLead={(leadId) => {
  1649→            setShowNewMessageModal(false);

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


     1→import { useState, useEffect, useRef } from 'react';
     2→import { Phone, Calendar, Sparkles, MessageSquare, Briefcase, HardHat, Flame, Clock, HelpCircle, Send, ChevronRight, CheckCircle2, Zap, AlertTriangle, Mail, ListPlus, ExternalLink, Archive, Loader2, Square, CheckSquare } from 'lucide-react';
     3→import { useLongPress } from '../../hooks/useLongPress';
     4→import { Tooltip } from '../ui/Tooltip';
     5→import { sequencesApi } from '../../api/client';
     6→
     7→function formatPhone(phone) {
     8→  if (!phone) return '';
     9→  const local = phone.replace('+61', '0');
    10→  if (local.length === 10) {
    11→    return `${local.slice(0, 4)} ${local.slice(4, 7)} ${local.slice(7)}`;
    12→  }
    13→  return phone;
    14→}
    15→
    16→function timeAgo(dateString) {
    17→  if (!dateString) return '';
    18→  const date = new Date(dateString);
    19→  const now = new Date();
    20→  const seconds = Math.floor((now - date) / 1000);
    21→
    22→  if (seconds < 60) return 'now';
    23→  if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
    24→  if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
    25→  if (seconds < 604800) return `${Math.floor(seconds / 86400)}d`;
    26→  return date.toLocaleDateString('en-AU', { day: 'numeric', month: 'short' });
    27→}
    28→
    29→// Check if lead has recent high-intent buying signal (within 24 hours)
    30→function getRecentBuyingSignal(lead) {
    31→  if (!lead?.metadata?.buying_signals) return null;
    32→  const signals = lead.metadata.buying_signals;
    33→  const now = Date.now();
    34→  const dayAgo = now - 24 * 60 * 60 * 1000;
    35→
    36→  // Find most recent high or medium signal within 24h
    37→  const recentSignals = signals.filter(s => {
    38→    if (!s.detected_at) return false;
    39→    const signalTime = new Date(s.detected_at).getTime();
    40→    return signalTime > dayAgo;
    41→  });
    42→
    43→  // Prioritize high > medium
    44→  const highSignal = recentSignals.find(s => s.type === 'high');
    45→  if (highSignal) return { level: 'high', ...highSignal };
    46→
    47→  const mediumSignal = recentSignals.find(s => s.type === 'medium');
    48→  if (mediumSignal) return { level: 'medium', ...mediumSignal };
    49→
    50→  return null;
    51→}
    52→
    53→const TRIAGE_CONFIG = {
    54→  buying_signal: {
    55→    label: 'Buying Signal',
    56→    insight: 'BUYING SIGNAL DETECTED',
    57→    insightColor: 'text-red-600 bg-red-50',
    58→    icon: Flame,
    59→    actions: [
    60→      { key: 'call', label: 'Call Now', icon: Phone, primary: true, tooltip: 'Call lead now' },
    61→      { key: 'reply', label: 'Quick Reply', icon: Send, tooltip: 'Send a quick message' },
    62→    ],
    63→  },
    64→  callback_request: {
    65→    label: 'Callback Request',
    66→    insight: 'CALLBACK REQUESTED',
    67→    insightColor: 'text-amber-600 bg-amber-50',
    68→    icon: Clock,
    69→    actions: [
    70→      { key: 'schedule', label: 'Schedule', icon: Calendar, primary: true, tooltip: 'Schedule a callback' },
    71→      { key: 'other_time', label: 'Other Time', icon: Clock, tooltip: 'Suggest another time' },
    72→    ],
    73→  },
    74→  question: {
    75→    label: 'Question',
    76→    insight: 'QUESTION ASKED',
    77→    insightColor: 'text-blue-600 bg-blue-50',
    78→    icon: HelpCircle,
    79→    actions: [
    80→      { key: 'smart_reply', label: 'Smart Reply', icon: Sparkles, primary: true, tooltip: 'AI-generated reply suggestion' },
    81→      { key: 'custom', label: 'Custom Reply', icon: MessageSquare, tooltip: 'Write a custom message' },
    82→    ],
    83→  },
    84→  positive: {
    85→    label: 'Positive',
    86→    insight: 'POSITIVE RESPONSE',
    87→    insightColor: 'text-green-600 bg-green-50',
    88→    icon: MessageSquare,
    89→    actions: [
    90→      { key: 'reply', label: 'Reply', icon: Send, primary: true, tooltip: 'Send a reply' },
    91→      { key: 'call', label: 'Call', icon: Phone, tooltip: 'Call the lead' },
    92→    ],
    93→  },
    94→};
    95→
    96→// Sequence Dropdown Component
    97→function SequenceDropdown({ leadId, onEnroll, onClose }) {
    98→  const [sequences, setSequences] = useState([]);
    99→  const [enrollments, setEnrollments] = useState([]);
   100→  const [loading, setLoading] = useState(true);
   101→  const [enrolling, setEnrolling] = useState(null);
   102→  const dropdownRef = useRef(null);
   103→
   104→  useEffect(() => {
   105→    loadData();
   106→  }, [leadId]);
   107→
   108→  // Close on click outside
   109→  useEffect(() => {
   110→    const handleClickOutside = (e) => {
   111→      if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
   112→        onClose();
   113→      }
   114→    };
   115→    document.addEventListener('mousedown', handleClickOutside);
   116→    return () => document.removeEventListener('mousedown', handleClickOutside);
   117→  }, [onClose]);
   118→
   119→  const loadData = async () => {
   120→    try {
   121→      const [seqRes, enrollRes] = await Promise.all([
   122→        sequencesApi.list({ channel: 'sms' }),
   123→        sequencesApi.getLeadEnrollments(leadId)
   124→      ]);
   125→      setSequences(seqRes.sequences || []);
   126→      setEnrollments(enrollRes.enrollments || []);
   127→    } catch (error) {
   128→      console.error('Failed to load sequences:', error);
   129→    } finally {
   130→      setLoading(false);
   131→    }
   132→  };
   133→
   134→  const handleEnroll = async (sequenceId, sequenceName) => {
   135→    setEnrolling(sequenceId);
   136→    try {
   137→      await sequencesApi.enroll(sequenceId, leadId);
   138→      onEnroll(sequenceName);
   139→      onClose();
   140→    } catch (error) {
   141→      console.error('Failed to enroll:', error);
   142→      alert('Failed to enroll in sequence');
   143→    } finally {
   144→      setEnrolling(null);
   145→    }
   146→  };
   147→
   148→  const isEnrolled = (seqId) => {
   149→    return enrollments.some(e => e.sequence_id === seqId && e.status === 'active');
   150→  };

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


Now let me check the API client for sequence calls and look for issues:


Bash

grep -n "getLeadEnrollments\|enrollments/lead" /home/ccuser/rateright-growth/admin/src/api/client.js | head -20


grep -A 10 "getLeadEnrollments" /home/ccuser/rateright-growth/admin/src/api/client.js


389:  getLeadEnrollments: (leadId) => request(`/api/sequences/enrollments/lead/${leadId}`),


  getLeadEnrollments: (leadId) => request(`/api/sequences/enrollments/lead/${leadId}`),
  pauseEnrollment: (enrollmentId, reason) => request(`/api/sequences/enrollments/${enrollmentId}/pause`, {
    method: 'POST',
    body: JSON.stringify({ reason })
  }),
  resumeEnrollment: (enrollmentId) => request(`/api/sequences/enrollments/${enrollmentId}/resume`, {
    method: 'POST'
  }),
  skipStep: (enrollmentId) => request(`/api/sequences/enrollments/${enrollmentId}/skip`, {
    method: 'POST'
  }),


Now let me look at the sequence enrollment route more carefully and check for any obvious issues with the API:


grep -n "POST.*enroll\|GET.*enrollments" /home/ccuser/rateright-growth/src/routes/sequences.js | head -20


   340→router.get('/enrollments/lead/:leadId', async (req, res) => {
   341→  try {
   342→    const { leadId } = req.params;
   343→
   344→    const { data: enrollments, error } = await (supabaseAdmin || supabase)
   345→      .from('sequence_enrollments')
   346→      .select(`
   347→        *,
   348→        sequence:sequences!sequence_enrollments_sequence_id_fkey(id, name, channel)
   349→      `)
   350→      .eq('lead_id', leadId)
   351→      .order('enrolled_at', { ascending: false });
   352→
   353→    if (error) throw error;
   354→
   355→    res.json({ enrollments: enrollments || [] });
   356→  } catch (err) {
   357→    console.error('Failed to fetch lead enrollments:', err);
   358→    res.status(500).json({ error: 'Failed to fetch lead enrollments' });
   359→  }
   360→});
   361→
   362→/**
   363→ * POST /api/sequences/enrollments/:id/pause
   364→ * Pause an enrollment
   365→ */
   366→router.post('/enrollments/:id/pause', async (req, res) => {
   367→  try {
   368→    const { id } = req.params;
   369→    const { reason } = req.body;
   370→
   371→    const { data, error } = await (supabaseAdmin || supabase)
   372→      .from('sequence_enrollments')
   373→      .update({
   374→        status: 'paused',
   375→        pause_reason: reason || 'manual_pause'
   376→      })
   377→      .eq('id', id)
   378→      .eq('status', 'active')
   379→      .select('id');
   380→
   381→    if (error) throw error;
   382→
   383→    if (!data || data.length === 0) {
   384→      return res.status(404).json({ error: 'Enrollment not found or not active' });
   385→    }
   386→
   387→    res.json({ success: true });
   388→  } catch (err) {
   389→    console.error('Failed to pause enrollment:', err);

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>


101: * POST /api/sequences/:id/enroll
215: * GET /api/sequences/check-enrollments
254: * POST /api/sequences/:id/unenroll
293: * GET /api/sequences/enrollments
337: * GET /api/sequences/enrollments/lead/:leadId
363: * POST /api/sequences/enrollments/:id/pause
395: * POST /api/sequences/enrollments/:id/resume
442: * POST /api/sequences/enrollments/:id/skip
505: * POST /api/sequences/enrollments/:id/send-now
598: * POST /api/sequences/:id/bulk-enroll


Now let me check for the route ordering issue - in Express, route order matters:


head -350 /home/ccuser/rateright-growth/src/routes/sequences.js | grep -n "router\.\(get\|post\)" | head -20


grep -n "^router\." /home/ccuser/rateright-growth/src/routes/sequences.js | head -20


14:router.get('/', async (req, res) => {
70:router.get('/:id', async (req, res) => {
106:router.post('/:id/enroll', async (req, res) => {
220:router.get('/check-enrollments', async (req, res) => {
259:router.post('/:id/unenroll', async (req, res) => {
296:router.get('/enrollments/active', async (req, res) => {
340:router.get('/enrollments/lead/:leadId', async (req, res) => {


14:router.get('/', async (req, res) => {
70:router.get('/:id', async (req, res) => {
106:router.post('/:id/enroll', async (req, res) => {
220:router.get('/check-enrollments', async (req, res) => {
259:router.post('/:id/unenroll', async (req, res) => {
296:router.get('/enrollments/active', async (req, res) => {
340:router.get('/enrollments/lead/:leadId', async (req, res) => {
366:router.post('/enrollments/:id/pause', async (req, res) => {
398:router.post('/enrollments/:id/resume', async (req, res) => {
445:router.post('/enrollments/:id/skip', async (req, res) => {
508:router.post('/enrollments/:id/send-now', async (req, res) => {
534:router.post('/lead/:leadId/opt-out', async (req, res) => {
578:router.get('/lead/:leadId/opt-out', async (req, res) => {
606:router.post('/:id/bulk-enroll', async (req, res) => {


Excellent! I found a major bug. Let me verify it:


cat /home/ccuser/rateright-growth/src/routes/sequences.js | head -70 | tail -20


    }

    // Merge counts into sequences
    const sequencesWithCounts = (sequences || []).map(seq => ({
      ...seq,
      active_enrollments: enrollmentCounts[seq.id] || 0
    }));

    res.json({ sequences: sequencesWithCounts });
  } catch (err) {
    console.error('Failed to fetch sequences:', err);
    res.status(500).json({ error: 'Failed to fetch sequences' });
  }
});

/**
 * GET /api/sequences/:id
 * Get a specific sequence with all steps
 */
router.get('/:id', async (req, res) => {


Let me now create a comprehensive summary document by checking a few more details:


grep -A 5 "GET /api/sequences/check-enrollments" /home/ccuser/rateright-growth/src/routes/sequences.js


 * GET /api/sequences/check-enrollments
 * Check if leads have active enrollments (for UI warning before enrollment)
 *
 * Query params: lead_ids (comma-separated)
 */
router.get('/check-enrollments', async (req, res) => {


grep -n "totalSteps\|total_steps" /home/ccuser/rateright-growth/admin/src/components/inbox/LeadCardPreview.jsx


172:              {seq.sequence?.name || 'Sequence'} - Step {seq.current_step}/{seq.sequence?.total_steps || '?'}


Perfect! Now I have enough information. Let me create a comprehensive investigation summary.

## Investigation Summary: Messages Page & Sequence Assignment Bugs

Based on thorough code analysis, I've identified several bugs in the Messages page and sequence assignment functionality. Here's the detailed breakdown:

---

### **1. CRITICAL: Express Route Ordering Bug in Sequences API**

**Location:** `/home/ccuser/rateright-growth/src/routes/sequences.js`

**The Problem:**
```
Line 220: router.get('/check-enrollments', ...)  ← Specific route
Line 70:  router.get('/:id', ...)                ← Generic route catches it first!
```

The generic `/:id` route matches `/check-enrollments` before the specific `/check-enrollments` route gets a chance to execute. In Express, routes are evaluated in order, so the generic wildcard `:id` parameter intercepts the request intended for `check-enrollments`.

**Impact:**
- When the UI calls `GET /api/sequences/check-enrollments?lead_ids=xxx`, Express matches it to `GET /:id` instead
- This tries to fetch sequence with id `"check-enrollments"` (which doesn't exist)
- Returns 404 instead of checking if leads are already enrolled
- **Result:** UI cannot detect if a lead is already in a sequence before enrollment, potentially allowing duplicate enrollments

**Fix Required:**
Move the specific `/check-enrollments` route BEFORE the generic `/:id` route.

---

### **2. CRITICAL: Missing `total_steps` Field in Sequence Display**

**Locations:**
- `/home/ccuser/rateright-growth/admin/src/components/inbox/LeadCardPreview.jsx:172`
- `/home/ccuser/rateright-growth/admin/src/pages/Messages.jsx:605`

**The Problem:**
```javascript
// LeadCardPreview.jsx line 172:
{seq.sequence?.name || 'Sequence'} - Step {seq.current_step}/{seq.sequence?.total_steps || '?'}
```

When loading active sequences via `sequencesApi.getLeadEnrollments(lead.id)`, the response only includes `sequence(id, name, channel)` fields, NOT `total_steps`.

**Evidence:**
```javascript
// sequences.js line 346-348 (getLeadEnrollments endpoint):
.select(`
  *,
  sequence:sequences!sequence_enrollments_sequence_id_fkey(id, name, channel)  // ← No total_steps!
`)
```

**Impact:**
- Step counter always shows: "Step 3/?" (missing total)
- Poor UX - users can't see progress through sequence
- **Fix:** Add `total_steps` to the sequence selection, or count steps from sequence_steps table

---

### **3. HIGH: Messages Scroll Bug (Documented but Not Fixed)**

**Location:** `/home/ccuser/rateright-growth/admin/src/hooks/useLongPress.js` and `/home/ccuser/rateright-growth/admin/src/pages/Messages.jsx`

**The Problem:**
Scrolling through the message list can accidentally trigger a click/long-press on a conversation card, opening that conversation when the user just wanted to scroll.

**Existing Documentation:**
- Detailed fix plan exists in `/home/ccuser/rateright-growth/docs/messages-scroll-fix-plan.md`
- Two solutions documented:
  1. Position-based scroll detection (compare element rect on touchstart vs touchend)
  2. Enhanced touchmove tracking for scroll container

**Current Status:** Plan written but NOT implemented yet

---

### **4. HIGH: Back Button Navigation Bug (Documented but Not Fixed)**

**Location:** `/home/ccuser/rateright-growth/admin/src/pages/Messages.jsx`

**The Problem:**
When in a conversation view and the user presses the browser back button, they return to the dashboard instead of the message list.

**Root Cause:**
- `activeLeadId` is read from URL only on component mount
- No listener for URL changes from browser back button
- Missing bidirectional URL ↔ state sync

**Existing Documentation:**
Detailed fix in `/home/ccuser/rateright-growth/docs/messages-scroll-fix-plan.md` (lines 128-276)

**Required Fix:**
```javascript
// Add effect to sync URL → state (handles browser back)
useEffect(() => {
  const leadFromUrl = searchParams.get('lead') || null;
  if (leadFromUrl !== activeLeadId) {
    setActiveLeadId(leadFromUrl);  // URL changed, update state
  }
}, [searchParams]); // Intentionally omit activeLeadId to avoid loops
```

**Current Status:** Plan written but NOT implemented yet

---

### **5. MEDIUM: Messages Auto-Reply Issues (From Recent Investigation)**

**Location:** `/home/ccuser/rateright-growth/src/routes/webhooks.js` and `/home/ccuser/rateright-growth/src/utils/intent.js`

**Three Sub-Issues Identified:**

**Issue A: Auto-Reply Spam (Webhook Retry Duplication)**
- Twilio retries webhooks on timeout/5xx, triggering 2-3 duplicate auto-replies
- No deduplication check before sending
- **Impact:** Users receive 3 identical generic messages within 1 minute

**Issue B: Dumb Auto-Replies Ignore Buying Signals**
- `getAutoReply()` only takes `intent` parameter, ignores `buyingSignal` entirely
- Hot leads saying "we need workers" get generic "Thanks for your message!" auto-reply
- Should SKIP auto-reply on high-intent signals and escalate to human

**Issue C: Missing Notifications for Inbound Alerts**
- Slack webhook may not be configured in Railway
- No in-app push notifications for buying signals
- Alerts only in Slack (if configured), not in UI

**Documentation:**
Detailed analysis in `/home/ccuser/rateright-growth/docs/messages-investigation-report.md`

---

### **6. MEDIUM: Missing `total_steps` Calculation in GET Sequences**

**Location:** `/home/ccuser/rateright-growth/src/routes/sequences.js:14-64` (GET /)

**The Problem:**
The main sequences list endpoint doesn't include step count:

```javascript
.select(`
  *,
  steps:sequence_steps(id, name, step_order, action_type, delay_hours)
`)
```

It fetches steps but doesn't return `total_steps` in the response. The frontend needs to count them manually or make an extra calculation.

**Current Workaround:** Probably not implemented - UI likely just doesn't show total steps in list view

---

## Summary Table

| Bug | Severity | Component | Status | Impact |
|-----|----------|-----------|--------|--------|
| Route ordering (check-enrollments) | CRITICAL | API | Not fixed | Duplicate enrollments possible, no validation |
| Missing total_steps in sequence display | CRITICAL | Messages UI | Not fixed | Poor UX, can't see sequence progress |
| Scroll triggers click | HIGH | Messages | Documented, not fixed | Accidental conversation opens |
| Back button returns to dashboard | HIGH | Messages | Documented, not fixed | Bad navigation UX |
| Auto-reply spam (3x duplication) | HIGH | SMS Inbound | Not fixed | User embarrassment, trust issue |
| Auto-reply ignores buying signals | HIGH | SMS Inbound | Not fixed | Losing hot leads to generic responses |
| Missing buying signal notifications | MEDIUM | SMS Inbound | Not fixed | No user alerts for hot leads |

---

## Existing Documentation

**Fix Plans Available:**
- `/home/ccuser/rateright-growth/docs/messages-scroll-fix-plan.md` - Complete fixes for scroll & back button bugs
- `/home/ccuser/rateright-growth/docs/messages-investigation-report.md` - Complete analysis of auto-reply & notification issues
- `/home/ccuser/rateright-growth/docs/INVESTIGATION.md` - Dashboard stats bug (related)

**All plans include:**
- Root cause analysis
- Exact file locations and line numbers
- Detailed code changes
- Testing checklists
- Risk assessments and rollback plans