feat: M3 — 伏笔账本 + 节奏引擎 + 大纲(含并发记账 bugfix)

- 伏笔账本:纯函数状态机(OPEN/PARTIAL/CLOSED/OVERDUE) + ForeshadowLedger repo;验收后到期扫描(BackgroundTask 自建 session 置 OVERDUE);登记/状态变更端点
- 节奏 + 三审齐:foreshadow-analyst + pace-checker 并入 LangGraph 并行审(REVIEW_SPECS),collect 分列落 chapter_reviews(conflicts/foreshadow_sug/pace),review SSE 加 foreshadow/pace 事件
- 大纲:outliner Agent 产 OutlineResult(含 foreshadow_windows),POST /outline 逐章 upsert outline 表;GET /foreshadow?status= 看板
- 前端:伏笔四泳道看板(OVERDUE 琥珀) + 大纲编辑器(窗口徽标) + 节奏节拍图(▁▃▅) + 审稿页消费 foreshadow/pace SSE
- bugfix(T3.8):并行三审共用请求 session 记账触发 'Session is already flushing' → foreshadow/pace 静默丢失;SqlAlchemyLedgerSink.record 改 add-only(靠端点/事务 commit),加并发回归测试
- M3 E2E:真实 DB + mock 网关零 token 走通 埋设→进展→验收后扫描 OVERDUE→看板 + 大纲含窗口 + 三审齐 SSE/留痕;E2E 暴露并钉住上述 bug
- 门禁绿:mypy 111 / pytest 228(0 xfailed) / alembic 无漂移;前端 gen:api/lint/tsc/vitest 69/build
This commit is contained in:
Yaojia Wang
2026-06-18 14:21:17 +02:00
parent 68f194a043
commit 5fb7bfb1de
74 changed files with 6529 additions and 126 deletions

View File

@@ -0,0 +1,66 @@
"""伏笔登记 / 状态变更端点的请求/响应 schemaC3 扩 / 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)