"""伏笔登记 / 状态变更端点的请求/响应 schema(C3 扩 / ARCH §6.2, §7.2)。 snake_case;前端经 OpenAPI 生成 TS 类型消费。改字段 → 前端必须 `pnpm gen:api`。 登记是**作者显式动作**(不变量 #3:伏笔入库不经 AI 静默写库),到期扫描是验收后的 确定性纯函数置位(M3-d),二者都不在审稿/写章流水线里。 """ from __future__ import annotations from typing import Any from pydantic import BaseModel, Field class ForeshadowRegisterRequest(BaseModel): """POST /projects/:id/foreshadow:作者显式登记一条伏笔(status 落 OPEN)。""" code: str = Field(min_length=1, description="伏笔代号,`(project_id, code)` 唯一") title: str = Field(min_length=1, description="伏笔标题/一句话描述") planted_at: int | None = Field(default=None, description="埋设章号") content: str | None = Field(default=None, description="伏笔正文/线索") expected_close_from: int | None = Field(default=None, description="预期回收窗口起始章") expected_close_to: int | None = Field( default=None, description="预期回收窗口结束章(到期判据用)" ) importance: str | None = Field(default=None, description="重要度(自由文本,看板用)") class ForeshadowTransitionRequest(BaseModel): """PATCH /projects/:id/foreshadow/:code:状态机转移 和/或 追加一条进展。 两字段皆可选:`to_status` 走状态机(非法 → VALIDATION 信封);`progress_entry` append-only 追加到 progress JSONB。二者可同时给(先转移、后追加进展)。 """ to_status: str | None = Field( default=None, description="目标状态(OPEN/PARTIAL/CLOSED/OVERDUE)" ) progress_entry: dict[str, Any] | None = Field( default=None, description="追加一条进展记录(append-only,不覆盖历史)" ) class ForeshadowView(BaseModel): """伏笔账本视图(登记/状态变更/看板共用;snake_case)。形对齐 `ForeshadowLedgerView`。""" code: str title: str status: str planted_at: int | None = None content: str | None = None expected_close_from: int | None = None expected_close_to: int | None = None importance: str | None = None links: list[dict[str, Any]] = Field(default_factory=list) progress: list[dict[str, Any]] = Field(default_factory=list) class ForeshadowBoardResponse(BaseModel): """GET /projects/:id/foreshadow?status=:伏笔看板(四泳道 + OVERDUE 字段齐)。 `status` 缺省返回全部;按 `code` 升序(repo `list_by_status` 已排序)。前端按 `status` 分四泳道(OPEN/PARTIAL/CLOSED/OVERDUE),用 `expected_close_to` 标逾期。 """ foreshadow: list[ForeshadowView] = Field(default_factory=list)