1
0
This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Timmy-time-dashboard/src/swarm/task_queue/models.py
Trip T ea2dbdb4b5 fix: test DB isolation, Discord recovery, and over-mocked tests
Test data was bleeding into production tasks.db because
swarm.task_queue.models.DB_PATH (relative path) was never patched in
conftest.clean_database. Fixed by switching to absolute paths via
settings.repo_root and adding the missing module to the patching list.

Discord bot could leak orphaned clients on retry after ERROR state.
Added _cleanup_stale() to close stale client/task before each start()
attempt, with improved logging in the token watcher.

Rewrote test_paperclip_client.py to use httpx.MockTransport instead of
patching _get/_post/_delete — tests now exercise real HTTP status codes,
error handling, and JSON parsing. Added end-to-end test for
capture_error → create_task DB isolation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 20:33:59 -04:00

122 lines
3.8 KiB
Python

"""Bridge module: exposes create_task() for programmatic task creation.
Used by infrastructure.error_capture to auto-create bug report tasks
in the same SQLite database the dashboard routes use.
"""
import logging
import sqlite3
import uuid
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
logger = logging.getLogger(__name__)
# Use absolute path via settings.repo_root so tests can reliably redirect it
# and relative-path CWD differences don't cause DB leaks.
try:
from config import settings as _settings
DB_PATH = Path(_settings.repo_root) / "data" / "tasks.db"
except Exception:
DB_PATH = Path("data/tasks.db")
@dataclass
class TaskRecord:
"""Lightweight return value from create_task()."""
id: str
title: str
status: str
def _ensure_db() -> sqlite3.Connection:
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(str(DB_PATH))
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA busy_timeout=5000")
conn.execute("""
CREATE TABLE IF NOT EXISTS tasks (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
description TEXT DEFAULT '',
status TEXT DEFAULT 'pending_approval',
priority TEXT DEFAULT 'normal',
assigned_to TEXT DEFAULT '',
created_by TEXT DEFAULT 'operator',
result TEXT DEFAULT '',
created_at TEXT DEFAULT (datetime('now')),
completed_at TEXT
)
""")
conn.commit()
return conn
def create_task(
title: str,
description: str = "",
assigned_to: str = "default",
created_by: str = "system",
priority: str = "normal",
requires_approval: bool = True,
auto_approve: bool = False,
task_type: str = "",
) -> TaskRecord:
"""Insert a task into the SQLite task queue and return a TaskRecord.
Args:
title: Task title (e.g. "[BUG] ConnectionError: ...")
description: Markdown body with error details / stack trace
assigned_to: Agent or queue to assign to
created_by: Who created the task ("system", "operator", etc.)
priority: "low" | "normal" | "high" | "urgent"
requires_approval: If False and auto_approve, skip pending_approval
auto_approve: If True, set status to "approved" immediately
task_type: Optional tag (e.g. "bug_report")
Returns:
TaskRecord with the new task's id, title, and status.
"""
valid_priorities = {"low", "normal", "high", "urgent"}
if priority not in valid_priorities:
priority = "normal"
status = "approved" if (auto_approve and not requires_approval) else "pending_approval"
task_id = str(uuid.uuid4())
now = datetime.utcnow().isoformat()
# Store task_type in description header if provided
if task_type:
description = f"**Type:** {task_type}\n{description}"
db = _ensure_db()
try:
db.execute(
"INSERT INTO tasks (id, title, description, status, priority, assigned_to, created_by, created_at) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
(task_id, title, description, status, priority, assigned_to, created_by, now),
)
db.commit()
finally:
db.close()
logger.info("Task created: %s%s [%s]", task_id[:8], title[:60], status)
return TaskRecord(id=task_id, title=title, status=status)
def get_task_summary_for_briefing() -> dict:
"""Return a summary of task counts by status for the morning briefing."""
db = _ensure_db()
try:
rows = db.execute("SELECT status, COUNT(*) as cnt FROM tasks GROUP BY status").fetchall()
finally:
db.close()
summary = {r["status"]: r["cnt"] for r in rows}
summary["total"] = sum(summary.values())
return summary