diff --git a/docs/design/backlog-followups.md b/docs/design/backlog-followups.md new file mode 100644 index 0000000..40c1923 --- /dev/null +++ b/docs/design/backlog-followups.md @@ -0,0 +1,54 @@ +# 设计契约 · 续补三件(拆书入库 / 续写式链 / 模板库) + +> 分支 `feat/backlog-followups` · 实施前唯一契约源。Agent 先读本文 + CLAUDE.md 不变量。范围 = 用户选定「全部可建的续补」(排除多租户/市场/push/browse/K1)。 + +## Context +Scope B 已合并(链 UI + 4 生成器)。本批补三个**贴现有框架**的续补功能:把竞品对标的剩余可建项做完。守不变量 #1/#2/#3/#9;preview→ingest 经验收闸;单用户原型(owner_id stub)。 + +--- + +## F1 · 拆书落库成 rules(teardown → rules ingest) +现状:teardown 生成器是 preview-only。目标:可把拆书结论入库为项目 `rules`,供写章注入。 +- **@llm**(packages/agents):`teardown_spec.writes=["rules"]`(其余不动;只声明 tier #2)。 +- **@backend**(packages/skills + apps/api): + - `skill_permissions.KNOWN_TABLES` 加 `"rules"`(确认未在则加)。 + - `toolbox_registry` teardown entry 加 `ingest=IngestSpec(table="rules")`。 + - `routers/toolbox.py`:在通用 ingest dispatcher(现 `_ingest_world_entities`/`_ingest_outline`,:344-365 的 "其余表未实现" 分支)加 `_ingest_rules` handler——把 `BookTeardownResult`(themes/archetypes/structure/hooks) 转成 `rules` 行(scope=project 级,复用既有 `rules` repo/表;规则文本由结构化字段拼成可读条目)。`partition_writes` 白名单已据 spec.writes 放行;rules 无需 continuity 预检(meta 规则非设定卡)→ ingest 直接落库(仿 `_ingest_outline` 无 409 路径)。 +- **@frontend**(apps/web):teardown 现 output_kind 已可预览;因 registry 加了 ingest,`GeneratorRunner` 的入库分支应自动出现「入库为规则」按钮(复用既有 ingest UI)。确认渲染 + `pnpm gen:api`。 +- **@qa**:ingest E2E(teardown generate→ingest→`rules` 真落行;负向:预览仍不写库)。 +- DoD:后端门禁绿;teardown 可入 rules;不变量 #3(入库经白名单)守住。 + +## F2 · 续写式链(continue_volume chain) +现状:链只有 `draft_volume`(从 start 章按记忆写)。目标:新增 `continue_volume`——每章写作以**上一章 accepted 正文末尾**作前文引子(复用 `build_continuation_context`),比纯 digest 续写更顺。 +- **@llm**(packages/core/orchestrator/chain):`write_chapter` 节点支持「续写模式」——据 state 的 `chain_key`,`continue_volume` 时调 `build_continuation_context(prior_text=上一章 accepted 正文)`(经注入的 reader,仿现有 accept_op 注入;core 不 import apps/api);`draft_volume` 保持原行为。ChainState 加 `chain_key`(已有则复用)。 +- **@backend**(apps/api):`SUPPORTED_CHAINS`(chain.py:63)加 `"continue_volume"`;run 端点据 chain_key 选模式;chain_runner 注入「读上一章 accepted 正文」的 reader 给图。 +- **@frontend**(apps/web):`ChainStarter` 加链类型选择(draft_volume=从头写 / continue_volume=续写),传 chain_key。 +- **@qa**:E2E(continue_volume 两章:第二章请求上下文含第一章正文末尾;其余同链 E2E 范式,mock 网关零 token)。 +- DoD:两种链都可跑;续写模式前文注入经 E2E 断言;守不变量 #1/#5。 + +## F3 · 提示词/模板库(单用户本地版) +现状:无。目标:作者保存/复用提示词模板,可一键填入生成器的 brief/text。**不做分享/市场**(需多租户)。 +- **@db**(packages/db):新表 `prompt_templates`(`UuidPk`+`CreatedAt`+`Base` mixin;列:`owner_id`(stub)、`title:str`、`body:str`、`category:str|None`、`tool_key:str|None`(可选关联生成器))+ alembic 迁移。`alembic check` 无漂移。 +- **@backend**(packages/core/domain + apps/api):`TemplateRepo`/`SqlTemplateRepo`(list/create/delete,owner stub 过滤)+ `routers/templates.py`(`GET/POST/DELETE /templates`,POST 校验 title/body 非空→422)+ `schemas/templates.py` + main 注册。 +- **@frontend**(apps/web):`pnpm gen:api`;模板库页 `app/templates/page.tsx`(列表/新建/删除)+ nav 入口;生成器 Runner 可选「从模板填入」(把模板 body 填进 brief/text 输入)。 +- **@qa**:E2E(创建→列出→删除 真 pg;title 空→422)。 +- DoD:模板 CRUD 可用 + 可填入生成器;后端+前端门禁绿。 + +--- + +## Workflow 结构(顺序建造避免共享树写冲突 → 交叉评审 → 全门禁) +- Phase F1(1-2 agent)→ Phase F2(@llm+@backend 顺序 + @frontend)→ Phase F3(@db→@backend→@frontend,+@qa)→ 各 feature E2E。 +- 交叉评审(并行只读):python / fastapi / typescript / 不变量。 +- 全门禁(并行独立复跑):后端 ruff/format/mypy/alembic check/pytest · 前端 lint/tsc/vitest/build。 +- 我汇总 review;CRITICAL/HIGH → 派 fix agent 复绿再合并。 + +## 验证 +- 后端:`uv run ruff check . && uv run mypy packages apps && uv run alembic check && uv run pytest -q`(pg 在跑)。 +- 前端:`cd apps/web && pnpm gen:api && pnpm lint && pnpm typecheck && pnpm test && pnpm build`。 +- 实景(app 在 localhost:3000/8000,需 provider key):teardown 可入 rules;链可选 continue_volume;模板库可建/填入。 + +## 风险/取舍 +- F1 rules 入库:rules 表结构若与拆书结构化字段不匹配,则拍平为可读规则文本(一条 teardown=一/多条 rule),E2E 断真落行即可。 +- F2 续写 reader 跨层:端点读 chapter accepted 正文→注入图节点(仿 accept_op,core 不 import apps/api)。 +- F3 模板填入生成器:前端把模板 body 写进现有 brief/text 输入即可,不改生成器后端。 +- owner_id 全程 stub(单用户原型);分享/市场不做。