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:
@@ -17,7 +17,7 @@ import uuid
|
||||
from collections.abc import AsyncIterator
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from ww_core.domain.chapter_repo import ChapterRepo
|
||||
@@ -59,6 +59,7 @@ from ww_api.services.accept_service import (
|
||||
)
|
||||
from ww_api.services.credentials import STUB_OWNER_ID
|
||||
from ww_api.services.digest_extraction import extract_digest_facts
|
||||
from ww_api.services.foreshadow_scan import SessionFactory, run_overdue_scan
|
||||
from ww_api.services.project_deps import (
|
||||
get_chapter_repo,
|
||||
get_digest_append_repo,
|
||||
@@ -67,6 +68,7 @@ from ww_api.services.project_deps import (
|
||||
get_project_repo,
|
||||
get_review_gateway,
|
||||
get_review_repo,
|
||||
get_session_factory,
|
||||
get_writer_gateway,
|
||||
)
|
||||
|
||||
@@ -302,14 +304,23 @@ async def accept_chapter(
|
||||
chapter_no: int,
|
||||
body: AcceptRequest,
|
||||
request: Request,
|
||||
background_tasks: BackgroundTasks,
|
||||
chapter_repo: ChapterRepoDep,
|
||||
digest_repo: DigestRepoDep,
|
||||
review_repo: ReviewRepoDep,
|
||||
gateway: DigestGatewayDep,
|
||||
session: Annotated[AsyncSession, Depends(get_session)],
|
||||
session_factory: Annotated[SessionFactory, Depends(get_session_factory)],
|
||||
) -> AcceptResponse:
|
||||
"""验收事务 + 冲突 gate(§5.5):gate(事务前)→ 终稿提炼 digest(事务外,R2)→
|
||||
单原子事务(晋升 + digest + 裁决留痕)→ 一次 commit。
|
||||
单原子事务(晋升 + digest + 裁决留痕)→ 一次 commit → 登记**验收后伏笔到期扫描**
|
||||
BackgroundTask(M3-b/d,§6.2)。
|
||||
|
||||
到期扫描接 §5.5 步骤 3 的 `TODO(M3)` 占位:验收提交成功**后**,把
|
||||
`current_ch > expected_close_to AND status≠CLOSED` 的伏笔确定性置 OVERDUE。
|
||||
伏笔登记/回收(PARTIAL/CLOSED)的「采纳建议」经作者经 foreshadow 端点显式确认,
|
||||
不在验收事务里 AI 静默改库(不变量 #3)。**§7.4 持久性局限**:BackgroundTask 进程内
|
||||
跑、重启丢失——原型可接受(见 `services/foreshadow_scan.py`,不引入 jobs 表)。
|
||||
"""
|
||||
request_id = getattr(request.state, "request_id", None)
|
||||
# R3:审稿真相从领域表重读(最近一条 chapter_reviews),不依赖 checkpoint。
|
||||
@@ -350,6 +361,17 @@ async def accept_chapter(
|
||||
latest_review=latest_review,
|
||||
decisions=body.decisions,
|
||||
)
|
||||
|
||||
# 验收**提交成功后**登记伏笔到期扫描 BackgroundTask(M3-b/d)。current_chapter=刚验收章号;
|
||||
# 任务在请求 session 关闭后跑,故 run_overdue_scan 自建新 session 并 commit(§7.4 局限可接受)。
|
||||
background_tasks.add_task(
|
||||
run_overdue_scan,
|
||||
session_factory,
|
||||
project_id=project_id,
|
||||
chapter_no=chapter_no,
|
||||
request_id=request_id,
|
||||
)
|
||||
|
||||
return AcceptResponse(
|
||||
project_id=project_id,
|
||||
chapter_no=chapter_no,
|
||||
|
||||
Reference in New Issue
Block a user