# mb_clouseau - Claude Code Context

## Project Overview

**mb_clouseau** is a MusicBee trace/debug/metrics utility for plugin developers. It subscribes to all 40 MusicBee notification types and provides comprehensive logging, system metrics, and plugin introspection.

## Current Status: ASK MODE - Frozen for Release

**CODEBASE FROZEN**: Ask before making any changes. This project is in pre-release state.

All core features implemented and tested. Build with:

```bash
cd C:\Users\scott\source\repos\MBX\ReferenceCode\mb_clouseau
dotnet build -c Release
# Output: bin\Release\mb_clouseau.dll
```

## Architecture

```
mb_clouseau/plugin/
├── Plugin.cs                    # Entry point, event routing, inline log panel
├── MusicBeeInterface.cs         # MusicBee API definitions (44KB)
├── NLog.config                  # Logging configuration
├── settings.json                # Default settings template
├── Core/
│   ├── ClouseauSettings.cs      # Settings manager (JSON)
│   ├── LogManager.cs            # NLog wrapper with named loggers
│   └── StateManager.cs          # Session state, event counts
├── Logging/
│   ├── LogEntry.cs              # Structured log entry model
│   ├── EventLogger.cs           # Event handler with rich context for all 40 events
│   └── LogBuffer.cs             # Thread-safe ring buffer with filtering/export
├── Metrics/
│   ├── MetricsCollector.cs      # Orchestrator (5s collection, 30s logging)
│   ├── MetricsSnapshot.cs       # Snapshot data model
│   ├── SystemMetrics.cs         # CPU, memory, disk
│   ├── ProcessMetrics.cs        # MusicBee process stats
│   ├── ClrMetrics.cs            # GC, heap, threading
│   └── AudioMetrics.cs          # Audio device info
├── Introspection/
│   ├── PluginInfo.cs            # Plugin metadata model
│   ├── PluginDiscovery.cs       # Plugin folder scanner
│   ├── PluginMonitor.cs         # Track plugin registrations (Phase 2)
│   ├── ApiInterceptor.cs        # Hook MusicBee API calls (Phase 2)
│   └── StateDumper.cs           # Deep state snapshots
└── UI/
    ├── LogViewerPanel.cs        # Standalone log panel component
    ├── SettingsDialog.cs        # Full settings UI with tabs
    └── DashboardForm.cs         # Dashboard window with multiple tabs
```

## Features Implemented

### Core
- All 40 MusicBee notification types with rich context (30+ fields per event)
- NLog-based logging with multiple outputs (text, JSON, events, metrics, errors)
- Thread-safe ring buffer with filtering and export
- JSON settings with hot reload

### Metrics Collection
- System: CPU, memory, disk I/O, network
- Process: MusicBee memory, handles (GDI/USER/Kernel), threads
- CLR: GC counts, % time in GC, heap sizes, GC pressure detection
- Handle leak detection with source identification

### API Interception
- 60+ MusicBee API delegates wrapped
- Call timing with statistics (count, min/max/avg duration)
- Slow call detection (configurable thresholds)
- Anomaly detection (rapid-fire calls, high error rates)

### UI Components
- Dockable panel with live event display and filtering
- Pop-out log viewer with double-click details
- Dashboard (Overview, Events, Settings, Diagnostics, Plugins tabs)
- Settings dialog with tabbed configuration

### Export & Analysis
- JSON, CSV, Text export formats
- State dumps (complete application snapshot)
- Plugin discovery and inspection

## Key Integration Points

### Event Flow
```
ReceiveNotification() → EventLogger.HandleNotification() → LogBuffer.Add() → Panel Update
```

### Metrics Flow
```
MetricsCollector.Start() → Timer (5s) → Collect All → LogManager.Metrics
```

### API Interception
```
Plugin.InitializePlugin() → ApiInterceptor.WrapApiDelegates() → All API calls logged
```

## Testing

1. Copy files from `bin\Debug\` to MusicBee Plugins folder:
   - `mb_clouseau.dll`
   - `NLog.dll`
   - `Newtonsoft.Json.dll`
   - `System.Diagnostics.PerformanceCounter.dll`
2. Restart MusicBee
3. Enable plugin in Preferences > Plugins
4. View dockable panel: View > Arrange Panels > Clouseau
5. Access Dashboard: Tools > Clouseau Dashboard
6. Check logs: `%AppData%\MusicBee\mb_clouseau\logs\`

## Dependencies

```xml
<PackageReference Include="NLog" Version="5.2.8" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="8.0.0" />
<PackageReference Include="System.Management" Version="8.0.0" />
```

## Notification Types (40 total)

Categories: Core (3), Player (14), Queue (5), Tags (5), Library (5), Playlist (3), Sync (1), Download (1), UI (2), System (1)

See SPEC.md for complete enum reference.

## Debugging Case Study: Cross-Thread Panel Creation

This case study demonstrates how Clouseau's own logging capabilities helped diagnose a subtle UI bug.

### The Problem

The Clouseau dockable panel appeared blank on MusicBee startup. The panel header (with Clear, Dashboard, Settings buttons) was visible, but the ListBox body showing events was completely empty. Toggling the panel off and back on would make it work correctly.

### Initial Debugging Attempts

Several fixes were tried without success:
- Deferred control creation with `Application.Idle`
- `VisibleChanged`/`ParentChanged` event handlers with forced refresh
- `HandleCreated` event for timer initialization
- `BeginInvoke` for deferred updates
- Delayed timer refresh (1.5 seconds after startup)
- Switching from owner-draw to standard ListBox mode

None of these worked because they were treating symptoms, not the root cause.

### Using the Logs

The breakthrough came from checking Clouseau's own error log:

```
%AppData%\MusicBee\mb_clouseau\logs\errors.log
```

The log revealed the exact exception:

```
2026-01-01 03:38:13.0423|ERROR|Clouseau.UI|Failed to create dockable panel
System.ArgumentException: Controls created on one thread cannot be parented
to a control on a different thread.
   at System.Windows.Forms.Control.ControlCollection.Add(Control value)
   at MusicBeePlugin.Plugin.OnDockablePanelCreated(Control panel)
```

### Root Cause

MusicBee calls `OnDockablePanelCreated` from a **background thread** during startup, but WinForms controls must be created and parented on the **UI thread**. The exception was being caught and logged, but the panel appeared blank because no controls were added.

### The Fix

```csharp
public int OnDockablePanelCreated(Control panel)
{
    // Initialize plugin (safe on any thread)
    if (!_initialized) InitializePlugin();

    // Marshal control creation to UI thread if needed
    if (panel.InvokeRequired)
    {
        LogManager.UI.Debug("Marshaling to UI thread");
        panel.Invoke(new Action(() => CreatePanelControls(panel)));
    }
    else
    {
        CreatePanelControls(panel);
    }

    return Convert.ToInt32(250 * dpiScaling);
}
```

### Key Takeaways

1. **Check the logs first**: Clouseau logs to `errors.log`, `events.log`, and `clouseau.log`. The error log captured the exact exception with stack trace.

2. **Log file locations**:
   - `%AppData%\MusicBee\mb_clouseau\logs\errors.log` - Exceptions and errors
   - `%AppData%\MusicBee\mb_clouseau\logs\clouseau.log` - General plugin activity
   - `%AppData%\MusicBee\mb_clouseau\logs\events.log` - MusicBee notification events

3. **Cross-thread exceptions in WinForms**: When a panel appears blank or controls don't show, check for thread affinity issues. Use `InvokeRequired` and `Invoke` to marshal to the UI thread.

4. **Silent failures**: The try/catch was logging the error but the blank panel gave no visual indication. Always check logs when UI doesn't behave as expected.

5. **Eat your own dogfood**: Using Clouseau to debug Clouseau proved the value of comprehensive logging for plugin development.

## WinForms UI Layout Lessons Learned

These patterns were discovered through trial and error. Follow them to avoid spinning on layout issues.

### Dock Order Matters

When using `Dock` properties, **the order you add controls determines layout**. WinForms processes docked controls in reverse order of addition.

**Rule: Add Fill control FIRST, then Top/Bottom controls LAST.**

```csharp
// CORRECT - Fill first, Top last
mainPanel.Controls.Add(listView);      // Dock = Fill (added first, fills remaining space)
mainPanel.Controls.Add(toolbar);       // Dock = Top (added last, takes space first)

// WRONG - causes Fill to cover Top
mainPanel.Controls.Add(toolbar);       // Dock = Top
mainPanel.Controls.Add(listView);      // Dock = Fill (covers the toolbar!)
```

### Use FlowLayoutPanel for Toolbars

**Never use absolute positioning (Location) for toolbar controls.** It leads to endless tweaking and overlap issues.

```csharp
// CORRECT - FlowLayoutPanel handles spacing automatically
var toolbar = new FlowLayoutPanel
{
    Height = 60,
    Dock = DockStyle.Top,
    WrapContents = false
};
// Controls flow left-to-right, use Margin for spacing

// WRONG - absolute positioning causes overlap headaches
var toolbar = new Panel { Height = 60, Dock = DockStyle.Top };
btnClear.Location = new Point(320, 10);  // Magic numbers, breaks easily
btnRefresh.Location = new Point(400, 10); // Overlaps if sizes change
```

### ListView/TreeView Headers Hidden

If a ListView or TreeView header row is hidden behind another control, it's a dock order problem. The Fill control was added after the Top control.

**Fix:** Reverse the `Controls.Add()` order.

### Resize Grippers

For resizable dialogs, use `StatusStrip` with `SizingGrip = true` instead of a plain Panel for the status bar:

```csharp
var statusStrip = new StatusStrip
{
    BackColor = DarkPanel,
    SizingGrip = true  // Adds resize gripper in corner
};
```

## Planned Features

- API Explorer: Interactive API testing UI
- Metrics visualization: Charts for CPU, memory, GC over time
- Remote log streaming
- Historical data comparison

---
*Last updated: 2026-01-01*
