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:
@@ -20,6 +20,37 @@
|
||||
- 理由 / 取舍:保留既有「注入 client 便于测试」风格,结构化路径同样可注 fake。`ProviderResult.text` 对结构化路径置为 `parsed.model_dump_json()`(便于日志/留痕),程序消费走 `parsed`。
|
||||
- 影响:契约 C1(`run()` 填 `parsed`;见 contracts 变更日志)、`ProviderResult` 加 `parsed` 字段;T2.2 续审节点据此消费。
|
||||
|
||||
## [2026-06-18] 并行审记账并发安全选 add-only(去网关 ledger flush)— @llm (T3.8)
|
||||
- 背景:三审同 superstep 并行共用请求 session,`SqlAlchemyLedgerSink.record` 的 `await flush()` 是唯一让步点→第二/三审 flush 重入「Session is already flushing」→被 `run_review` 吞成 incomplete、foreshadow/pace 静默丢失(T3.7 暴露)。
|
||||
- 选择:`record` 改 **add-only**(去 `await flush()`,只 `session.add(row)`)。`session.add()` 同步不让步→并行协程不交错;持久化仍靠端点/事务末尾 `commit()`(自动 flush 待决行)。
|
||||
- 理由 / 取舍:最小改动、单 owner(只动 `ledger.py`),不牵动 apps/(@backend)。前提已校验:①并行审区间无 DB 查询触发 autoflush(review_context 取自 state、适配器不碰 session);②draft/review/accept/outline 端点末尾均已 commit。缓冲/独立 session 方案跨 owner、更重,不取。
|
||||
- 影响:C1 边界语义不变(commit 责任方未变);凡新增「并行用网关产 usage」路径,sink 必须保持 add-only、不得在并行段 await flush。新增并发回归测试 `test_ledger_concurrency.py`。
|
||||
|
||||
## [2026-06-18] 节拍图用定高 CSS 柱 + 文本 sparkline,不引图表库 — @frontend (T3.6)
|
||||
- 选择:`beat_map`(int 序列) 按本序列 min..max 线性归一到 7 档 ▁..▇(全相等→居中档,最低柱给 12% 可见高度),渲染定高 CSS 柱 + `aria-label="爽点节拍图 ▁▃▅…"`。静态无动画→天然满足 prefers-reduced-motion。
|
||||
- 理由:KISS,避免图表库依赖。影响:T3.7 节拍图 `role="img"` selector。
|
||||
|
||||
## [2026-06-18] 大纲 beats 存储形 + 写侧 repo 命名 — @backend (T3.5)
|
||||
- 背景:`outline.beats` DB 列是 JSONB dict,但 outliner 产 `list[str]`;读侧 `OutlineRepo`(C5/memory)已占名。
|
||||
- 选择:写侧 `SqlOutlineWriteRepo` 把 list 包成 `{"beats":[...]}` 落库(读侧同形 round-trip 一致),API 出参 `OutlineChapterView.beats` 解包成裸 `list[str]`。写侧命名加 `Write` 前缀避撞 C5 读侧(同 DigestAppendRepo/ForeshadowLedgerRepo 先例)。`volume` 由端点提供(schema 无逐章卷号,M3 默认全卷 1)。`get_outline_gateway` 复用 `build_gateway_for_tier(...,"analyst")`、独立缝便于测试 override。
|
||||
- 影响:C3 扩(outline 端点);T3.6 渲染 beats 裸 list + 窗口徽标;T3.7 断言 outline 行 beats 列形。
|
||||
|
||||
## [2026-06-18] 验收后伏笔到期扫描挂 BackgroundTask + 重复code/非法转移映射 VALIDATION(422) — @backend (T3.2)
|
||||
- 背景:§5.5 步骤3 `TODO(M3)` 要验收后置 OVERDUE;登记/状态端点需映射 DB 唯一冲突与非法状态机转移到错误信封。
|
||||
- 选择:(1) accept 端点 commit 成功后经 FastAPI BackgroundTasks 登记 `run_overdue_scan`(自建独立 session,因请求 session 已关闭);扫描抽纯 async 函数 + 可注入 `repo_factory`/`session_factory` 缝,单测直接 await 注 fake、不起后台线程。(2) 重复 code(IntegrityError)/`InvalidTransition` 都映射 `ErrorCode.VALIDATION`(422) 非 409——现有 409 码 `CONFLICT_UNRESOLVED` 语义=未决冲突,复用不符;未新增 shared 错误码。
|
||||
- 影响:C3 扩(foreshadow 登记/状态端点);§7.4 持久性局限(进程内/重启丢任务)原型接受、不引 jobs 表;T3.5 看板/outline 续接本 router;T3.7 真 pg 断言 OVERDUE。
|
||||
|
||||
## [2026-06-18] 伏笔写侧 Repository 加 `Ledger` 前缀避免与读侧同名 — @backend (T3.1)
|
||||
- 背景:读侧 `ForeshadowRepo`/`ForeshadowView`/`SqlForeshadowRepo`(`domain/repositories.py`+`memory/`)已被 assemble(C5 稳定)占用且 View 最小(无 importance/links/progress)。M3 写侧账本需更全 View + 写方法。
|
||||
- 选择:写侧命名 `ForeshadowLedgerRepo`/`SqlForeshadowLedgerRepo`/`ForeshadowLedgerView`,落 `domain/foreshadow_repo.py`;读侧不动。状态机纯函数落 `domain/foreshadow_state.py`(`ForeshadowStatus` StrEnum + `transition`/`is_overdue`/`apply_overdue_scan`/`InvalidTransition`)。
|
||||
- 理由:同 T2.3 `DigestAppendRepo` vs 读侧 `DigestRepo` 先例,读写分离免歧义、不破坏 C5。写方法只 flush 不 commit(commit 归 T3.2 扫描任务 / T3.5 端点)。
|
||||
- 影响:T3.2 验收后扫描 / T3.5 看板端点消费 `ForeshadowLedger*`。
|
||||
|
||||
## [2026-06-18] 大纲节点不接进 review 图、不做失败隔离 — @llm (T3.4)
|
||||
- 背景:续审在 `run_review` 内把网关失败隔离为 `incomplete`(§5.2 任一审不阻塞其余)。
|
||||
- 选择:大纲是**独立生成**(不在写章/审稿流水线),`run_outline` 裸函数、网关失败**直接上抛**由 T3.5 端点处理;带 `output_schema` 时 `parsed` 非 `OutlineResult` 抛 `ValueError`(C1 违约即显错,不返回空大纲)。
|
||||
- 影响:C6 扩(outliner spec + OutlineResult + run_outline 缝);T3.5 端点调 `run_outline` 持久化 `outline` 表。
|
||||
|
||||
## [2026-06-18] 审稿页终稿来源:页内可编辑 textarea(无 GET draft 端点)— @frontend (T2.6)
|
||||
- 背景:审稿/验收都需「终稿」文本,但当前无 `GET .../draft` 端点拉取已存草稿。
|
||||
- 选择:审稿页用可折叠 textarea 编辑终稿(`initialDraft=''`),重审传它为 `{draft}`(空→后端回退已存草稿),验收 `final_text`=它。
|
||||
|
||||
Reference in New Issue
Block a user