Complete workplan state model cleanup

This commit is contained in:
2026-05-18 01:31:36 +02:00
parent 98b2cb6484
commit d6522a9a40
42 changed files with 789 additions and 310 deletions

View File

@@ -5,6 +5,7 @@ import time
from pathlib import Path
from typing import Any
import yaml
from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
@@ -16,9 +17,13 @@ from api.models.workstream import Workstream
from api.schemas.workstream import (
WorkstreamCreate,
WorkstreamRead,
WorkstreamStatus,
WorkstreamUpdate,
)
from api.workplan_status import (
is_supported_workstream_status,
normalize_workstream_status,
ready_review_status,
)
router = APIRouter(prefix="/workstreams", tags=["workstreams"])
@@ -53,17 +58,10 @@ def _frontmatter(path: Path) -> dict[str, Any]:
if end == -1:
return {}
data: dict[str, Any] = {}
for raw_line in text[4:end].splitlines():
line = raw_line.strip()
if not line or line.startswith("#") or ":" not in line:
continue
key, value = line.split(":", 1)
value = value.strip()
if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
value = value[1:-1]
data[key.strip()] = value
return data
try:
return yaml.safe_load(text[4:end].strip()) or {}
except yaml.YAMLError:
return {}
@router.get("/", response_model=list[WorkstreamRead])
@@ -71,7 +69,7 @@ async def list_workstreams(
topic_id: uuid.UUID | None = None,
repo_id: uuid.UUID | None = None,
repo_goal_id: uuid.UUID | None = None,
status: WorkstreamStatus | None = None,
status: str | None = None,
owner: str | None = None,
slug: str | None = None,
session: AsyncSession = Depends(get_session),
@@ -84,7 +82,10 @@ async def list_workstreams(
if repo_goal_id:
q = q.where(Workstream.repo_goal_id == repo_goal_id)
if status:
q = q.where(Workstream.status == status)
normalised_status = normalize_workstream_status(status)
if not is_supported_workstream_status(status):
raise HTTPException(status_code=422, detail=f"Unsupported workstream status '{status}'")
q = q.where(Workstream.status == normalised_status)
if owner:
q = q.where(Workstream.owner == owner)
if slug:
@@ -127,11 +128,24 @@ async def workplan_index(
workstream_id = data.get("state_hub_workstream_id")
if not workstream_id:
continue
file_status = normalize_workstream_status(data.get("status", ""))
review = (
ready_review_status(
root,
data.get("reviewed_against_commit"),
data.get("context_paths"),
)
if file_status == "ready"
else None
)
index[str(workstream_id)] = {
"filename": path.name,
"relative_path": str(path.relative_to(root)),
"repo_slug": repo.slug,
"archived": archived,
"status": file_status or None,
"needs_review": bool(review and review.needs_review),
"health_labels": ["needs_review"] if review and review.needs_review else [],
}
_INDEX_CACHE = {"workstreams": index}
_INDEX_CACHE_AT = time.monotonic()
@@ -176,7 +190,7 @@ async def update_workstream(
await session.commit()
await session.refresh(ws)
if prev_status != "completed" and ws.status == "completed":
if normalize_workstream_status(prev_status) != "finished" and ws.status == "finished":
subject = "org.statehub.workstream.completed"
envelope = EventEnvelope.new(
subject,