Index workplan flavor and omit residuals from default views.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 32s

STATE-WP-0092: persist flavor from files, treat depends_on as the C-20
canonical edge, exclude flavor=residual from summary/next_steps/deps
unless include_residuals is set. Live primary still needs the alembic
revision applied.

Assistant: grok
Assistant-Session: 01a09dc1-b21e-77e1-919e-fcad2f82b267
This commit is contained in:
tegwick 2026-09-14 15:28:58 +02:00
parent ea11451e9c
commit ddc3338541
18 changed files with 852 additions and 25 deletions

View file

@ -9,7 +9,7 @@ from typing import Any
import yaml
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response, status
from sqlalchemy import select
from sqlalchemy import or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from api.database import get_session
@ -24,6 +24,11 @@ from api.schemas.workplan import (
)
from api.services.lifecycle import transition_workplan_status
from api.services.legacy_compat import retire_legacy_route
from api.work_record_flavor import (
RESIDUAL_FLAVOR,
WORK_RECORD_FLAVORS,
normalize_flavor,
)
from api.workplan_status import (
is_supported_workplan_status,
normalize_workplan_status,
@ -88,6 +93,8 @@ async def _list_workplans(
status_filter: str | None,
owner: str | None,
slug: str | None,
flavor: str | None = None,
include_residuals: bool = True,
session: AsyncSession,
) -> list[Workplan]:
q = select(Workplan)
@ -106,6 +113,19 @@ async def _list_workplans(
q = q.where(Workplan.owner == owner)
if slug:
q = q.where(Workplan.slug == slug)
wanted = normalize_flavor(flavor)
if wanted is not None:
if wanted not in WORK_RECORD_FLAVORS:
raise HTTPException(
status_code=422,
detail=(
f"Unknown work-record flavor {wanted!r}; "
f"expected one of {', '.join(WORK_RECORD_FLAVORS)}"
),
)
q = q.where(Workplan.flavor == wanted)
elif not include_residuals:
q = q.where(or_(Workplan.flavor.is_(None), Workplan.flavor != RESIDUAL_FLAVOR))
q = q.order_by(
Workplan.planning_priority.asc().nullslast(),
Workplan.planning_order.asc().nullslast(),
@ -365,6 +385,8 @@ async def list_workplans(
status: str | None = None,
owner: str | None = None,
slug: str | None = None,
flavor: str | None = Query(None),
include_residuals: bool = Query(True),
session: AsyncSession = Depends(get_session),
) -> list[Workplan]:
return await _list_workplans(
@ -374,6 +396,8 @@ async def list_workplans(
status_filter=status,
owner=owner,
slug=slug,
flavor=flavor,
include_residuals=include_residuals,
session=session,
)