# Hotfix Design: v1.1.1-rc.2 - Metrics Dashboard Template Data Mismatch

## Problem Summary

Production deployment of v1.1.1-rc.1 exposed two critical issues in the metrics dashboard:

1. **Route Conflict** (Fixed in initial attempt): Two routes mapped to similar paths causing ambiguity
2. **Template/Data Mismatch** (Root cause): Template expects different data structure than monitoring module provides

### The Template/Data Mismatch

**Template Expects** (`metrics_dashboard.html` line 163):
```jinja2
{{ metrics.database.count|default(0) }}
{{ metrics.database.avg|default(0) }}
{{ metrics.database.min|default(0) }}
{{ metrics.database.max|default(0) }}
```

**Monitoring Module Returns**:
```python
{
    "by_type": {
        "database": {
            "count": 50,
            "avg_duration_ms": 12.5,
            "min_duration_ms": 2.0,
            "max_duration_ms": 45.0
        }
    }
}
```

Note the two mismatches:
1. **Nesting**: Template wants `metrics.database` but gets `metrics.by_type.database`
2. **Field Names**: Template wants `avg` but gets `avg_duration_ms`

## Solution: Route Adapter Pattern

Transform data at the presentation layer (route handler) to match template expectations.

### Implementation

Added a transformer function in `admin.py` that:
1. Flattens the nested structure (`by_type.database` → `database`)
2. Maps field names (`avg_duration_ms` → `avg`)
3. Provides safe defaults for missing data

```python
def transform_metrics_for_template(metrics_stats):
    """Transform metrics stats to match template structure"""
    transformed = {}

    # Map by_type to direct access with field name mapping
    for op_type in ['database', 'http', 'render']:
        if 'by_type' in metrics_stats and op_type in metrics_stats['by_type']:
            type_data = metrics_stats['by_type'][op_type]
            transformed[op_type] = {
                'count': type_data.get('count', 0),
                'avg': type_data.get('avg_duration_ms', 0),  # Note field name change
                'min': type_data.get('min_duration_ms', 0),
                'max': type_data.get('max_duration_ms', 0)
            }
        else:
            # Safe defaults
            transformed[op_type] = {'count': 0, 'avg': 0, 'min': 0, 'max': 0}

    # Keep other top-level stats
    transformed['total_count'] = metrics_stats.get('total_count', 0)
    transformed['max_size'] = metrics_stats.get('max_size', 1000)
    transformed['process_id'] = metrics_stats.get('process_id', 0)

    return transformed
```

### Why This Approach?

1. **Minimal Risk**: Only changes route handler, not core monitoring module
2. **Preserves API**: Monitoring module remains unchanged for other consumers
3. **No Template Changes**: Avoids modifying template and JavaScript
4. **Clear Separation**: Route acts as adapter between business logic and view

## Additional Fixes Applied

1. **Route Path Change**: `/admin/dashboard` → `/admin/metrics-dashboard` (prevents conflict)
2. **Defensive Imports**: Graceful handling of missing monitoring module
3. **Error Handling**: Safe defaults when metrics collection fails

## Testing and Validation

Created comprehensive test script validating:
- Data structure transformation works correctly
- All template fields accessible after transformation
- Safe defaults provided for missing data
- Field name mapping correct

All 32 admin route tests pass with 100% success rate.

## Files Modified

1. `/starpunk/routes/admin.py`:
   - Lines 218-260: Added transformer function
   - Line 263: Changed route path
   - Lines 285-314: Applied transformer and added error handling

2. `/starpunk/__init__.py`: Version bump to 1.1.1-rc.2

3. `/CHANGELOG.md`: Documented hotfix

## Production Impact

**Before**: 500 error with `'dict object' has no attribute 'database'`
**After**: Metrics dashboard loads correctly with properly structured data

This is a tactical bug fix, not an architectural change, and should be documented as such.