forked from Rockachopa/Timmy-time-dashboard
feat: automatic error feedback loop with bug report tracker (#80)
Errors and uncaught exceptions are now automatically captured, deduplicated, persisted to a rotating log file, and filed as bug report tasks in the existing task queue — giving Timmy a sovereign, local issue tracker with zero new dependencies. - Add RotatingFileHandler writing errors to logs/errors.log (5MB rotate, 5 backups) - Add error capture module with stack-trace hashing and 5-min dedup window - Add FastAPI exception middleware + global exception handler - Instrument all background loops (briefing, thinking, task processor) with capture_error() - Extend task queue with bug_report task type and auto-approve rule - Fix auto-approve type matching (was ignoring task_type field entirely) - Add /bugs dashboard page and /api/bugs JSON endpoints - Add ERROR_CAPTURED and BUG_REPORT_CREATED event types for real-time feed - Add BUGS nav link to desktop and mobile navigation - Add 16 tests covering error capture, deduplication, and bug report routes Co-authored-by: Alexander Payne <apayne@MM.local> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
6545b7e26a
commit
aa3263bc3b
86
src/dashboard/routes/bugs.py
Normal file
86
src/dashboard/routes/bugs.py
Normal file
@@ -0,0 +1,86 @@
|
||||
"""Bug Report routes -- error feedback loop dashboard.
|
||||
|
||||
GET /bugs -- Bug reports dashboard page
|
||||
GET /api/bugs -- List bug reports (JSON)
|
||||
GET /api/bugs/stats -- Bug report statistics
|
||||
"""
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import HTMLResponse, JSONResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from swarm.task_queue.models import list_tasks
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(tags=["bugs"])
|
||||
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
|
||||
|
||||
|
||||
def _get_bug_reports(status: Optional[str] = None, limit: int = 50) -> list:
|
||||
"""Get bug report tasks from the task queue."""
|
||||
all_tasks = list_tasks(limit=limit)
|
||||
bugs = [t for t in all_tasks if t.task_type == "bug_report"]
|
||||
if status:
|
||||
bugs = [t for t in bugs if t.status.value == status]
|
||||
return bugs
|
||||
|
||||
|
||||
@router.get("/bugs", response_class=HTMLResponse)
|
||||
async def bugs_page(request: Request, status: Optional[str] = None):
|
||||
"""Bug reports dashboard page."""
|
||||
bugs = _get_bug_reports(status=status, limit=200)
|
||||
|
||||
# Count by status
|
||||
all_bugs = _get_bug_reports(limit=500)
|
||||
stats: dict[str, int] = {}
|
||||
for bug in all_bugs:
|
||||
s = bug.status.value
|
||||
stats[s] = stats.get(s, 0) + 1
|
||||
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"bugs.html",
|
||||
{
|
||||
"page_title": "Bug Reports",
|
||||
"bugs": bugs,
|
||||
"stats": stats,
|
||||
"total": len(all_bugs),
|
||||
"filter_status": status,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/api/bugs", response_class=JSONResponse)
|
||||
async def api_list_bugs(status: Optional[str] = None, limit: int = 50):
|
||||
"""List bug reports as JSON."""
|
||||
bugs = _get_bug_reports(status=status, limit=limit)
|
||||
return {
|
||||
"bugs": [
|
||||
{
|
||||
"id": b.id,
|
||||
"title": b.title,
|
||||
"description": b.description,
|
||||
"status": b.status.value,
|
||||
"priority": b.priority.value,
|
||||
"created_at": b.created_at,
|
||||
"result": b.result,
|
||||
}
|
||||
for b in bugs
|
||||
],
|
||||
"count": len(bugs),
|
||||
}
|
||||
|
||||
|
||||
@router.get("/api/bugs/stats", response_class=JSONResponse)
|
||||
async def api_bug_stats():
|
||||
"""Bug report statistics."""
|
||||
all_bugs = _get_bug_reports(limit=500)
|
||||
stats: dict[str, int] = {}
|
||||
for bug in all_bugs:
|
||||
s = bug.status.value
|
||||
stats[s] = stats.get(s, 0) + 1
|
||||
return {"stats": stats, "total": len(all_bugs)}
|
||||
Reference in New Issue
Block a user