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:
370
apps/web/lib/api/schema.d.ts
vendored
370
apps/web/lib/api/schema.d.ts
vendored
@@ -169,7 +169,14 @@ export interface paths {
|
||||
/**
|
||||
* Accept Chapter
|
||||
* @description 验收事务 + 冲突 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 表)。
|
||||
*/
|
||||
post: operations["accept_chapter_projects__project_id__chapters__chapter_no__accept_post"];
|
||||
delete?: never;
|
||||
@@ -178,6 +185,75 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/projects/{project_id}/foreshadow": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/**
|
||||
* List Foreshadow
|
||||
* @description 伏笔看板:按 `status` 过滤(缺省=全部),按 code 升序。
|
||||
*
|
||||
* `status` 非法(不在 OPEN/PARTIAL/CLOSED/OVERDUE)→ VALIDATION 信封。四泳道前端
|
||||
* 据 `status` 分组;OVERDUE 泳道 + 逾期标记用 `expected_close_to`(看板字段已齐)。
|
||||
*/
|
||||
get: operations["list_foreshadow_projects__project_id__foreshadow_get"];
|
||||
put?: never;
|
||||
/**
|
||||
* Register Foreshadow
|
||||
* @description 登记一条伏笔(status=OPEN)。重复 `code` → DB 唯一约束冲突 → VALIDATION 信封。
|
||||
*/
|
||||
post: operations["register_foreshadow_projects__project_id__foreshadow_post"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/projects/{project_id}/foreshadow/{code}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
/**
|
||||
* Update Foreshadow
|
||||
* @description 状态机转移 和/或 追加进展。非法转移 → VALIDATION 信封;不存在 → NOT_FOUND。
|
||||
*
|
||||
* `to_status` 与 `progress_entry` 皆可选;两者都缺 → VALIDATION(无操作)。先转移、后追加。
|
||||
*/
|
||||
patch: operations["update_foreshadow_projects__project_id__foreshadow__code__patch"];
|
||||
trace?: never;
|
||||
};
|
||||
"/projects/{project_id}/outline": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/**
|
||||
* Generate Outline
|
||||
* @description 生成大纲并逐章持久化。无凭据 → LLM_UNAVAILABLE;项目不存在 → NOT_FOUND。
|
||||
*/
|
||||
post: operations["generate_outline_projects__project_id__outline_post"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/settings/providers": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -338,11 +414,164 @@ export interface components {
|
||||
/** Text */
|
||||
text: string;
|
||||
};
|
||||
/**
|
||||
* ForeshadowBoardResponse
|
||||
* @description GET /projects/:id/foreshadow?status=:伏笔看板(四泳道 + OVERDUE 字段齐)。
|
||||
*
|
||||
* `status` 缺省返回全部;按 `code` 升序(repo `list_by_status` 已排序)。前端按
|
||||
* `status` 分四泳道(OPEN/PARTIAL/CLOSED/OVERDUE),用 `expected_close_to` 标逾期。
|
||||
*/
|
||||
ForeshadowBoardResponse: {
|
||||
/** Foreshadow */
|
||||
foreshadow?: components["schemas"]["ForeshadowView"][];
|
||||
};
|
||||
/**
|
||||
* ForeshadowRegisterRequest
|
||||
* @description POST /projects/:id/foreshadow:作者显式登记一条伏笔(status 落 OPEN)。
|
||||
*/
|
||||
ForeshadowRegisterRequest: {
|
||||
/**
|
||||
* Code
|
||||
* @description 伏笔代号,`(project_id, code)` 唯一
|
||||
*/
|
||||
code: string;
|
||||
/**
|
||||
* Title
|
||||
* @description 伏笔标题/一句话描述
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* Planted At
|
||||
* @description 埋设章号
|
||||
*/
|
||||
planted_at?: number | null;
|
||||
/**
|
||||
* Content
|
||||
* @description 伏笔正文/线索
|
||||
*/
|
||||
content?: string | null;
|
||||
/**
|
||||
* Expected Close From
|
||||
* @description 预期回收窗口起始章
|
||||
*/
|
||||
expected_close_from?: number | null;
|
||||
/**
|
||||
* Expected Close To
|
||||
* @description 预期回收窗口结束章(到期判据用)
|
||||
*/
|
||||
expected_close_to?: number | null;
|
||||
/**
|
||||
* Importance
|
||||
* @description 重要度(自由文本,看板用)
|
||||
*/
|
||||
importance?: string | null;
|
||||
};
|
||||
/**
|
||||
* ForeshadowTransitionRequest
|
||||
* @description PATCH /projects/:id/foreshadow/:code:状态机转移 和/或 追加一条进展。
|
||||
*
|
||||
* 两字段皆可选:`to_status` 走状态机(非法 → VALIDATION 信封);`progress_entry`
|
||||
* append-only 追加到 progress JSONB。二者可同时给(先转移、后追加进展)。
|
||||
*/
|
||||
ForeshadowTransitionRequest: {
|
||||
/**
|
||||
* To Status
|
||||
* @description 目标状态(OPEN/PARTIAL/CLOSED/OVERDUE)
|
||||
*/
|
||||
to_status?: string | null;
|
||||
/**
|
||||
* Progress Entry
|
||||
* @description 追加一条进展记录(append-only,不覆盖历史)
|
||||
*/
|
||||
progress_entry?: {
|
||||
[key: string]: unknown;
|
||||
} | null;
|
||||
};
|
||||
/**
|
||||
* ForeshadowView
|
||||
* @description 伏笔账本视图(登记/状态变更/看板共用;snake_case)。形对齐 `ForeshadowLedgerView`。
|
||||
*/
|
||||
ForeshadowView: {
|
||||
/** Code */
|
||||
code: string;
|
||||
/** Title */
|
||||
title: string;
|
||||
/** Status */
|
||||
status: string;
|
||||
/** Planted At */
|
||||
planted_at?: number | null;
|
||||
/** Content */
|
||||
content?: string | null;
|
||||
/** Expected Close From */
|
||||
expected_close_from?: number | null;
|
||||
/** Expected Close To */
|
||||
expected_close_to?: number | null;
|
||||
/** Importance */
|
||||
importance?: string | null;
|
||||
/** Links */
|
||||
links?: {
|
||||
[key: string]: unknown;
|
||||
}[];
|
||||
/** Progress */
|
||||
progress?: {
|
||||
[key: string]: unknown;
|
||||
}[];
|
||||
};
|
||||
/**
|
||||
* ForeshadowWindowView
|
||||
* @description 单章关联的伏笔回收窗口(snake_case;形对齐 `ww_agents.ForeshadowWindow`)。
|
||||
*/
|
||||
ForeshadowWindowView: {
|
||||
/** Code */
|
||||
code: string;
|
||||
/** Plant Chapter */
|
||||
plant_chapter?: number | null;
|
||||
/** Expected Close From */
|
||||
expected_close_from?: number | null;
|
||||
/** Expected Close To */
|
||||
expected_close_to?: number | null;
|
||||
};
|
||||
/** HTTPValidationError */
|
||||
HTTPValidationError: {
|
||||
/** Detail */
|
||||
detail?: components["schemas"]["ValidationError"][];
|
||||
};
|
||||
/**
|
||||
* OutlineChapterView
|
||||
* @description 单章大纲视图:章号 + 卷 + 节拍 + 伏笔窗口。
|
||||
*/
|
||||
OutlineChapterView: {
|
||||
/** No */
|
||||
no: number;
|
||||
/** Volume */
|
||||
volume: number;
|
||||
/** Beats */
|
||||
beats?: string[];
|
||||
/** Foreshadow Windows */
|
||||
foreshadow_windows?: components["schemas"]["ForeshadowWindowView"][];
|
||||
};
|
||||
/**
|
||||
* OutlineGenerateRequest
|
||||
* @description POST /projects/:id/outline:触发大纲生成 + 持久化。
|
||||
*
|
||||
* M3 简单分卷:`volume` 缺省=1(schema 无逐章卷号;本端点把整批章节落到同一卷)。
|
||||
*/
|
||||
OutlineGenerateRequest: {
|
||||
/**
|
||||
* Volume
|
||||
* @description 本批大纲所属卷号(M3 默认全卷 1)
|
||||
* @default 1
|
||||
*/
|
||||
volume: number;
|
||||
};
|
||||
/**
|
||||
* OutlineResponse
|
||||
* @description 大纲生成响应:逐章已持久化的大纲(含窗口,供前端 T3.6)。
|
||||
*/
|
||||
OutlineResponse: {
|
||||
/** Chapters */
|
||||
chapters?: components["schemas"]["OutlineChapterView"][];
|
||||
};
|
||||
/**
|
||||
* ProjectCreateRequest
|
||||
* @description POST /projects:立项向导字段(owner_id 由后端补 stub,不入参)。
|
||||
@@ -898,6 +1127,145 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
list_foreshadow_projects__project_id__foreshadow_get: {
|
||||
parameters: {
|
||||
query?: {
|
||||
status?: string | null;
|
||||
};
|
||||
header?: never;
|
||||
path: {
|
||||
project_id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ForeshadowBoardResponse"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
register_foreshadow_projects__project_id__foreshadow_post: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
project_id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["ForeshadowRegisterRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
201: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ForeshadowView"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
update_foreshadow_projects__project_id__foreshadow__code__patch: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
project_id: string;
|
||||
code: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["ForeshadowTransitionRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ForeshadowView"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
generate_outline_projects__project_id__outline_post: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
project_id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["OutlineGenerateRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["OutlineResponse"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
list_providers_settings_providers_get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { serverApiBase } from "./config";
|
||||
import type {
|
||||
ForeshadowBoardResponse,
|
||||
ProjectListResponse,
|
||||
ProjectResponse,
|
||||
ProvidersResponse,
|
||||
@@ -35,3 +36,10 @@ export async function fetchReviews(
|
||||
`/projects/${projectId}/chapters/${chapterNo}/reviews`,
|
||||
);
|
||||
}
|
||||
|
||||
// 伏笔看板(全量,前端按 status 分四泳道)。
|
||||
export async function fetchForeshadow(
|
||||
projectId: string,
|
||||
): Promise<ForeshadowBoardResponse> {
|
||||
return getJson<ForeshadowBoardResponse>(`/projects/${projectId}/foreshadow`);
|
||||
}
|
||||
|
||||
@@ -25,3 +25,18 @@ export type ReviewHistoryResponse =
|
||||
export type ConflictDecision = components["schemas"]["ConflictDecision"];
|
||||
export type AcceptRequest = components["schemas"]["AcceptRequest"];
|
||||
export type AcceptResponse = components["schemas"]["AcceptResponse"];
|
||||
|
||||
// M3 伏笔 + 大纲(C3 扩 T3.2/T3.5)。
|
||||
export type ForeshadowView = components["schemas"]["ForeshadowView"];
|
||||
export type ForeshadowBoardResponse =
|
||||
components["schemas"]["ForeshadowBoardResponse"];
|
||||
export type ForeshadowRegisterRequest =
|
||||
components["schemas"]["ForeshadowRegisterRequest"];
|
||||
export type ForeshadowTransitionRequest =
|
||||
components["schemas"]["ForeshadowTransitionRequest"];
|
||||
export type ForeshadowWindowView =
|
||||
components["schemas"]["ForeshadowWindowView"];
|
||||
export type OutlineChapterView = components["schemas"]["OutlineChapterView"];
|
||||
export type OutlineGenerateRequest =
|
||||
components["schemas"]["OutlineGenerateRequest"];
|
||||
export type OutlineResponse = components["schemas"]["OutlineResponse"];
|
||||
|
||||
Reference in New Issue
Block a user