Index workplan flavor and omit residuals from default views.
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:
parent
ea11451e9c
commit
ddc3338541
18 changed files with 852 additions and 25 deletions
|
|
@ -30,6 +30,7 @@ class WorkstreamTotals(BaseModel):
|
|||
finished: int = 0
|
||||
archived: int = 0
|
||||
total: int = 0
|
||||
residual_open: int = 0
|
||||
|
||||
|
||||
class TaskTotals(BaseModel):
|
||||
|
|
@ -85,6 +86,7 @@ class StateSummary(BaseModel):
|
|||
blocked_tasks: list[TaskRead] = []
|
||||
recent_progress: list[ProgressEventRead]
|
||||
open_workplans: list[WorkstreamWithDeps] = []
|
||||
residual_open_workplans: list[WorkstreamWithDeps] = []
|
||||
next_steps: list[NextStep] = []
|
||||
domains: list[DomainSummary] = []
|
||||
contribution_counts: dict[str, int] = {}
|
||||
|
|
@ -98,6 +100,7 @@ class DashboardWorkplanRow(BaseModel):
|
|||
id: uuid.UUID
|
||||
title: str
|
||||
status: str
|
||||
flavor: str | None = None
|
||||
domain: str = "unknown"
|
||||
repo_label: str = "unassigned"
|
||||
workplan_filename: str | None = None
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from pydantic import BaseModel, ConfigDict, field_validator, model_validator
|
|||
from api.models.task import TaskPriority, TaskStatus
|
||||
from api.schemas.compat import WorkplanIdCompatMixin, WorkplanIdCreateMixin
|
||||
from api.task_status import normalize_task_status
|
||||
from api.work_record_flavor import WORK_RECORD_FLAVORS, normalize_flavor
|
||||
|
||||
|
||||
class TaskStatusMixin(BaseModel):
|
||||
|
|
@ -18,9 +19,23 @@ class TaskStatusMixin(BaseModel):
|
|||
return normalize_task_status(value)
|
||||
|
||||
|
||||
class TaskCreate(TaskStatusMixin, WorkplanIdCreateMixin):
|
||||
class TaskFlavorMixin(BaseModel):
|
||||
@field_validator("flavor", mode="before", check_fields=False)
|
||||
@classmethod
|
||||
def _normalize_flavor(cls, value):
|
||||
flavor = normalize_flavor(value)
|
||||
if flavor is not None and flavor not in WORK_RECORD_FLAVORS:
|
||||
raise ValueError(
|
||||
f"Unknown work-record flavor {flavor!r}; "
|
||||
f"expected one of {', '.join(WORK_RECORD_FLAVORS)}"
|
||||
)
|
||||
return flavor
|
||||
|
||||
|
||||
class TaskCreate(TaskStatusMixin, TaskFlavorMixin, WorkplanIdCreateMixin):
|
||||
id: uuid.UUID | None = None
|
||||
record_id: str | None = None
|
||||
flavor: str | None = None
|
||||
title: str
|
||||
description: str | None = None
|
||||
status: TaskStatus = TaskStatus.todo
|
||||
|
|
@ -39,8 +54,9 @@ class TaskCreate(TaskStatusMixin, WorkplanIdCreateMixin):
|
|||
return self
|
||||
|
||||
|
||||
class TaskUpdate(TaskStatusMixin):
|
||||
class TaskUpdate(TaskStatusMixin, TaskFlavorMixin):
|
||||
title: str | None = None
|
||||
flavor: str | None = None
|
||||
description: str | None = None
|
||||
status: TaskStatus | None = None
|
||||
priority: TaskPriority | None = None
|
||||
|
|
@ -102,6 +118,7 @@ class TaskRead(TaskStatusMixin, WorkplanIdCompatMixin):
|
|||
model_config = ConfigDict(from_attributes=True)
|
||||
id: uuid.UUID
|
||||
record_id: str | None = None
|
||||
flavor: str | None = None
|
||||
title: str
|
||||
description: str | None = None
|
||||
status: TaskStatus
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ from typing import Literal
|
|||
from pydantic import BaseModel, ConfigDict, field_validator
|
||||
|
||||
from api.schemas.workplan_dependency import WorkplanDepStub
|
||||
from api.work_record_flavor import (
|
||||
FLAVOR_PROMOTION_REASONS,
|
||||
WORK_RECORD_FLAVORS,
|
||||
normalize_flavor,
|
||||
normalize_promotion_reason,
|
||||
)
|
||||
from api.workplan_status import normalize_workplan_status
|
||||
|
||||
WorkplanStatus = Literal[
|
||||
|
|
@ -28,7 +34,42 @@ class WorkplanStatusMixin(BaseModel):
|
|||
return normalize_workplan_status(value)
|
||||
|
||||
|
||||
class WorkplanCreate(WorkplanStatusMixin):
|
||||
class WorkplanFlavorMixin(BaseModel):
|
||||
@field_validator("flavor", mode="before", check_fields=False)
|
||||
@classmethod
|
||||
def _normalise_flavor(cls, value):
|
||||
flavor = normalize_flavor(value)
|
||||
if flavor is not None and flavor not in WORK_RECORD_FLAVORS:
|
||||
raise ValueError(
|
||||
f"Unknown work-record flavor {flavor!r}; "
|
||||
f"expected one of {', '.join(WORK_RECORD_FLAVORS)}"
|
||||
)
|
||||
return flavor
|
||||
|
||||
@field_validator("flavor_promotion_reason", mode="before", check_fields=False)
|
||||
@classmethod
|
||||
def _normalise_promotion_reason(cls, value):
|
||||
reason = normalize_promotion_reason(value)
|
||||
if reason is not None and reason not in FLAVOR_PROMOTION_REASONS:
|
||||
raise ValueError(
|
||||
f"Unknown flavor_promotion_reason {reason!r}; "
|
||||
f"expected one of {', '.join(FLAVOR_PROMOTION_REASONS)}"
|
||||
)
|
||||
return reason
|
||||
|
||||
@field_validator("flavor_promoted_from", mode="before", check_fields=False)
|
||||
@classmethod
|
||||
def _normalise_promoted_from(cls, value):
|
||||
flavor = normalize_flavor(value)
|
||||
if flavor is not None and flavor not in WORK_RECORD_FLAVORS:
|
||||
raise ValueError(
|
||||
f"Unknown flavor_promoted_from {flavor!r}; "
|
||||
f"expected one of {', '.join(WORK_RECORD_FLAVORS)}"
|
||||
)
|
||||
return flavor
|
||||
|
||||
|
||||
class WorkplanCreate(WorkplanStatusMixin, WorkplanFlavorMixin):
|
||||
id: uuid.UUID | None = None
|
||||
repo_id: uuid.UUID
|
||||
topic_id: uuid.UUID | None = None
|
||||
|
|
@ -40,6 +81,9 @@ class WorkplanCreate(WorkplanStatusMixin):
|
|||
due_date: date | None = None
|
||||
planning_priority: str | None = None
|
||||
planning_order: int | None = None
|
||||
flavor: str | None = None
|
||||
flavor_promotion_reason: str | None = None
|
||||
flavor_promoted_from: str | None = None
|
||||
execution_state: ExecutionState = "manual"
|
||||
launch_mode: LaunchMode = "manual"
|
||||
concurrency_mode: ConcurrencyMode = "sequential"
|
||||
|
|
@ -49,7 +93,7 @@ class WorkplanCreate(WorkplanStatusMixin):
|
|||
repo_goal_id: uuid.UUID | None = None
|
||||
|
||||
|
||||
class WorkplanUpdate(WorkplanStatusMixin):
|
||||
class WorkplanUpdate(WorkplanStatusMixin, WorkplanFlavorMixin):
|
||||
title: str | None = None
|
||||
description: str | None = None
|
||||
status: WorkplanStatus | None = None
|
||||
|
|
@ -57,6 +101,9 @@ class WorkplanUpdate(WorkplanStatusMixin):
|
|||
due_date: date | None = None
|
||||
planning_priority: str | None = None
|
||||
planning_order: int | None = None
|
||||
flavor: str | None = None
|
||||
flavor_promotion_reason: str | None = None
|
||||
flavor_promoted_from: str | None = None
|
||||
execution_state: ExecutionState | None = None
|
||||
launch_mode: LaunchMode | None = None
|
||||
concurrency_mode: ConcurrencyMode | None = None
|
||||
|
|
@ -95,6 +142,9 @@ class WorkplanRead(WorkplanStatusMixin):
|
|||
due_date: date | None = None
|
||||
planning_priority: str | None = None
|
||||
planning_order: int | None = None
|
||||
flavor: str | None = None
|
||||
flavor_promotion_reason: str | None = None
|
||||
flavor_promoted_from: str | None = None
|
||||
execution_state: ExecutionState = "manual"
|
||||
launch_mode: LaunchMode = "manual"
|
||||
concurrency_mode: ConcurrencyMode = "sequential"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue