generated from coulomb/repo-seed
Implements the first live layer of the Custodian cognitive infrastructure: PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable Framework telemetry dashboard. - state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard) - 5 DB tables: topics, workstreams, tasks, decisions, progress_events - 11 MCP tools + 5 resources registered in .mcp.json - Observable dashboard: Overview, Workstreams, Decisions, Progress pages - CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual) - ~/.claude/CLAUDE.md: global cross-project reference to the hub - scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
51 lines
1.6 KiB
Python
51 lines
1.6 KiB
Python
import uuid
|
|
from datetime import datetime
|
|
|
|
from fastapi import APIRouter, Depends, status
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from api.database import get_session
|
|
from api.models.progress_event import ProgressEvent
|
|
from api.schemas.progress_event import ProgressEventCreate, ProgressEventRead
|
|
|
|
router = APIRouter(prefix="/progress", tags=["progress"])
|
|
|
|
|
|
@router.get("/", response_model=list[ProgressEventRead])
|
|
async def list_progress(
|
|
topic_id: uuid.UUID | None = None,
|
|
workstream_id: uuid.UUID | None = None,
|
|
task_id: uuid.UUID | None = None,
|
|
event_type: str | None = None,
|
|
since: datetime | None = None,
|
|
limit: int = 100,
|
|
session: AsyncSession = Depends(get_session),
|
|
) -> list[ProgressEvent]:
|
|
q = select(ProgressEvent)
|
|
if topic_id:
|
|
q = q.where(ProgressEvent.topic_id == topic_id)
|
|
if workstream_id:
|
|
q = q.where(ProgressEvent.workstream_id == workstream_id)
|
|
if task_id:
|
|
q = q.where(ProgressEvent.task_id == task_id)
|
|
if event_type:
|
|
q = q.where(ProgressEvent.event_type == event_type)
|
|
if since:
|
|
q = q.where(ProgressEvent.created_at >= since)
|
|
q = q.order_by(ProgressEvent.created_at.desc()).limit(limit)
|
|
result = await session.execute(q)
|
|
return list(result.scalars().all())
|
|
|
|
|
|
@router.post("/", response_model=ProgressEventRead, status_code=status.HTTP_201_CREATED)
|
|
async def append_progress(
|
|
body: ProgressEventCreate,
|
|
session: AsyncSession = Depends(get_session),
|
|
) -> ProgressEvent:
|
|
event = ProgressEvent(**body.model_dump())
|
|
session.add(event)
|
|
await session.commit()
|
|
await session.refresh(event)
|
|
return event
|