Files
StarPunk/docs/design/v1.1.1/hotfix-v1.1.1-rc2-consolidated.md
Phil Skentelbery f10d0679da feat(tags): Add database schema and tags module (v1.3.0 Phase 1)
Implements tag/category system backend following microformats2 p-category specification.

Database changes:
- Migration 008: Add tags and note_tags tables
- Normalized tag storage (case-insensitive lookup, display name preserved)
- Indexes for performance

New module:
- starpunk/tags.py: Tag management functions
  - normalize_tag: Normalize tag strings
  - get_or_create_tag: Get or create tag records
  - add_tags_to_note: Associate tags with notes (replaces existing)
  - get_note_tags: Retrieve note tags (alphabetically ordered)
  - get_tag_by_name: Lookup tag by normalized name
  - get_notes_by_tag: Get all notes with specific tag
  - parse_tag_input: Parse comma-separated tag input

Model updates:
- Note.tags property (lazy-loaded, prefer pre-loading in routes)
- Note.to_dict() add include_tags parameter

CRUD updates:
- create_note() accepts tags parameter
- update_note() accepts tags parameter (None = no change, [] = remove all)

Micropub integration:
- Pass tags to create_note() (tags already extracted by extract_tags())
- Return tags in q=source response

Per design doc: docs/design/v1.3.0/microformats-tags-design.md

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-10 11:24:23 -07:00

3.9 KiB

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):

{{ metrics.database.count|default(0) }}
{{ metrics.database.avg|default(0) }}
{{ metrics.database.min|default(0) }}
{{ metrics.database.max|default(0) }}

Monitoring Module Returns:

{
    "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.databasedatabase)
  2. Maps field names (avg_duration_msavg)
  3. Provides safe defaults for missing data
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.