# PROGRESS — 多 Agent 协同任务台账 > 多 agent 共享的**唯一协同入口**。开工前读这里 + `memory/`;认领任务、尊重依赖、完成即更新。 > 完整任务定义见 `DEV_PLAN.md`;本台账只展开**当前阶段**,进入新阶段时再把该阶段任务拉进来。 > 协同规则见 `CLAUDE.md` →「多 Agent 协同」。 **状态图例**:⬜ 待办 · 🔵 进行中(注明 @skill + 日期) · ✅ 完成 · ⛔ 阻塞(注明等什么) --- ## 当前阶段:QA-CR · 全项目 Code Review 整改批 > **背景**:[2026-06-26] 对全仓做了一次**七维度 Code Review**(后端 Python 规范 / LangGraph 最佳实践 / 后端 FastAPI 架构 / 前端编程规范 / 前端架构 / 应用安全 / 依赖时效),6 个专科 reviewer 并行 + 依赖自查。绿门禁下仍揪出 **2 CRITICAL + 13 HIGH + 一批 MEDIUM/LOW**——再次证伪「测试过=没问题」。架构本身成熟、不变量纪律强(provider 中立 / HITL accept 单写 / checkpoint 仅控制流 / SecretStr 全覆盖);**确认无虞**:SSRF(`base_url` 仅来自硬编码 `_PROVIDER_BASE_URLS`,无用户 URL 入网)/ SQL 注入(全 ORM 参数化)/ XSS(无 `dangerouslySetInnerHTML`/`eval`)/ 错误信封不泄栈密钥 / Kimi 用 RFC 8628 device flow 无 CSRF 面。 > **整改 DoD**:CRITICAL + HIGH 全部 ✅ 且各自补回归测试;MEDIUM 择批清理;门禁全绿(后端 ruff/format/mypy/pytest + alembic 无漂移;前端 lint/tsc/vitest/build)。**守 §Definition of Done**:每项 TDD 先红后绿、完成即小步 commit。 > **落地顺序**:① 本周 CR-C1/C2 + 两资源泄漏(H1/H2)+ Fernet key(H8);② 合并前 计费两项(H3)+ request_id(H4)+ max_length(H9)+ 前端三 HIGH(H10/H11/H12);③ MEDIUM/LOW 技术债批。 > **依赖纪律**:CR-C1(升 Next)须 `pnpm install` 重锁 + `pnpm build` 验证、**升级后轮换全部应用密钥**(与 CR-H8 Fernet key 联动)。CR-H4/H9 改 schema → 若动 OpenAPI 形参须 `pnpm gen:api` + `memory/contracts.md` 记一笔。无 DB 迁移预期。 | 任务 | 状态 | 负责 | 依赖 | 备注 | |---|---|---|---|---| | CR-C1 🔴 升级 Next.js 修 RCE(CVE-2025-66478)+ React 补丁 + 轮换密钥 | ⬜ | @frontend | — | `apps/web/package.json` next **15.1.3→15.1.9**(或 15.5.7/16.x),react/react-dom 19.0.0→补丁版(上游 CVE-2025-55182);App Router + RSC 应用正受影响。`pnpm install` 重锁 + `pnpm build` 验;**升级后轮换全部应用密钥**(联动 CR-H8)。Next15 LTS 2026-10-21 EOL,规划 16.x 迁移 | | CR-C2 🔴 多章链设 `recursion_limit` | ⬜ | @backend | — | `services/chain_runner.py:210` 默认 25 supersteps、每章 4 节点→超 ~6 章即 `GraphRecursionError` 整 job 失败,「批量产一卷(K章)」在目标规模不可用。`config` 加 `recursion_limit=(last-start+1)*4+10`;补超默认上限的 mock-gateway 回归测试 | | CR-H1 🟠 Kimi OAuth 轮询不占 DB 会话 | ⬜ | @backend | — | `services/job_runner.py:75`+`routers/kimi_oauth.py:85` 轮询全程(最坏 >200s)占连接池连接、饿死其他请求;轮询循环无需 DB,仅最终 `upsert_oauth_credential` 开短会话 | | CR-H2 🟠 修 `httpx.AsyncClient` 泄漏 | ⬜ | @backend | — | `routers/kimi_oauth.py:73` `Depends(_default_http_client)` 非 generator 依赖、永不 `aclose()`;改 `async def _http_client(): async with httpx.AsyncClient(...) as http: yield http` | | CR-H3 🟠 Anthropic 结构化输出记真实 usage | ⬜ | @llm | — | `adapters/anthropic.py:161` 硬编码 `usage=0`→`usage_ledger` 全记 0 成本、腐蚀计费(违 ARCH §4.8);用 `create_with_completion` 取原始响应 usage | | CR-H4 🟠 `request_id` 贯通网关日志 | ⬜ | @llm | — | `gateway.py:183` 所有 `llm_call`/`llm_provider_failed` 无 request_id;`LlmRequest` 加 `request_id` 字段并从 API 层透传(违 ARCH §9.3 端到端追踪不变量) | | CR-H5 🟠 `thinking` 字段:实现或删除 | ⬜ | @llm | — | `types.py:41` 声明但所有 adapter 忽略,`thinking=True` 静默无效、无报错;按 YAGNI 删除或实现转发 + 测试 | | CR-H6 🟠 流式路径加 per-provider 重试 | ⬜ | @llm | — | `gateway.py:268` 流式仅 1 次尝试,首 token 前一次瞬时 429 烧光 fallback 链(与非流式 `_complete_with_retry` 不对称);用 `_retrying()` 包裹首 token 前尝试或显式文档化 | | CR-H7 🟠 `TOOLBOX` 注册表不可变 | ⬜ | @backend | — | `packages/skills/ww_skills/toolbox_registry.py:70` 裸 dict 可变;仿同库 `SPECS` 用 `Final[Mapping]=MappingProxyType(...)`(违不可变不变量) | | CR-H8 🟠 `.env.example` 移除可用 Fernet key | ⬜ | @devops | — | 已确认提交真实可用 key(注释「可直接用于本地」),换非功能占位符 + 突出轮换提示;联动 CR-C1 轮换 | | CR-H9 🟠 API 文本输入加 `max_length` | ⬜ | @backend | — | `schemas/{providers,generation,projects}.py` `api_key`/`brief`/`final_text`/`segment` 仅 `min_length=1`(DoS/成本失控);加上限(api_key≤512、brief≤1e4、final_text≤2e5)+ ASGI 请求体大小限 | | CR-H10 🟠 前端 `errorCode` 去重 | ⬜ | @frontend | — | `lib/style/{useRefine,useStyleLearn}.ts` 各自重复定义,统一 import 自 `lib/generation/cards.ts:132`(DRY 漂移隐患) | | CR-H11 🟠 Drawer 关闭还原焦点 | ⬜ | @frontend | — | `components/Drawer.tsx:41` 关闭后焦点掉到 body(违 WCAG 2.4.3);传 `triggerRef`、关闭时 `.focus()` | | CR-H12 🟠 RefineView 取消在途 refine | ⬜ | @frontend | — | `components/style/RefineView.tsx:33` 无 AbortController,段切换时旧响应可覆盖新响应;`useRefine` 暴露 abort 在 effect cleanup 调用,去 blanket exhaustive-deps disable | | CR-M1 🟡 后端架构整改批 | ⬜ | @backend | — | `outline.py:162` volume 下推 DB 过滤;`generation.py:326`/`toolbox.py:431` N+1 INSERT→`bulk_create`;`jobs.py:28` 裸 ORM→repo;`provider_deps.py:91` 去私有 `_client`(加 `probe_connection()`);列表端点加分页。⚠️`assemble.py:155` 8 串行查询:**AsyncSession 不支持同一 session 并发,`gather` 须每查询独立 session**,不可照搬 | | CR-M2 🟡 网关/adapters 整改批 | ⬜ | @llm | — | `gateway.py:90` `is_open()` 副作用违 CQS;`pricing.py` 未知 model 静默记 0 → 加 warning;adapter `str(exc)` 入异常消息可能带 vendor 响应体→只留类名+status | | CR-M3 🟡 LangGraph 链健壮性 | ⬜ | @llm | — | `chain_runner.py:124` accept commit 后 `run_overdue_scan` 非幂等且失败无法 resume→部分进度空洞(幂等化或纳入事务);`chain_runner.py:159` 改用公开 `get_state().next` 替私有 `__interrupt__`;`chain/nodes.py:213` 链内 4 审串行→并行/复用 review 子图 | | CR-M4 🟡 前端 key/a11y/cleanup 批 | ⬜ | @frontend | — | 可编辑列表去 index-key(`CharacterGenerator.tsx:135`/`GeneratorRunner.tsx:178`/`ForeshadowCard.tsx:68`);SSE `KNOWN_EVENTS` 补 `stop`(核对 `memory/contracts.md`);ARIA 补全(AppShell nav label / NavDrawer aria-controls / ForeshadowCard `
` 结构);ProjectWizard 步骤焦点;`ReviewReport.tsx:301` setTimeout 卸载清理 | | CR-M5 🟡 前端 TS 边界类型收紧 | ⬜ | @frontend | — | `lib/api/server.ts:34` `parseJsonBody` 加最小结构校验;`useAccept.ts:60` `ApiErrorEnvelope` 从生成 schema 派生;`applyFix.ts` `hasApplicableFix` 改类型谓词去 `as string` | | CR-L1 🟢 LOW 清理批 | ⬜ | @backend·@llm·@frontend | — | `chain.py:256` 静默回退加 warning;SSE 端点补 OpenAPI `text/event-stream` 声明;冗余 `as`/`.valueOf()` cast 清理;测试脚本 `print`→logging | | CR-D1 🟡 抬高后端依赖下界(可选升级评估) | ⬜ | @devops | — | `pyproject.toml` 下界过旧(`langgraph>=0.2.40`/`anthropic>=0.34`),抬到接近 lockfile 实锁版本保可复现;后端实锁版本均当前。可选评估 vitest 3.x / tailwind 4.x | > **维度小结**:后端 FastAPI 架构 0C/2H/7M·Python 规范 0C/4H/6M·LangGraph 1C/2H/5M·前端 React 0C/2H/9M·前端 TS 0C/1H/8L·安全 0C/2H/4M·依赖 1C。后端依赖全部当前大版本线;前端唯一关键缺口=Next RCE。 --- ## 进行中:写作工作台重构(writing-first)· 分支 `feat/writing-first-workspace` > **背景**:[2026-07-07] 用户对写作体验提 8 点反馈,产品主张升级为 **writing-first 工作台**——进来即写、所有工具/AI 动作从编辑器内按需调用并回填正文、元数据由内容渐进生成。多 agent 工作流(扎根代码→Web+GitHub 调研→三方设计→四镜头对抗评审→定稿)产出 DEV_PLAN(`scratchpad/writing-first-workspace-DEVPLAN.md`,**ephemeral 未入库**;灵感/取舍见 `memory/selectable-write-prompts.md`)。 > **已拍板决策**:进来即写=**延迟落库**(首次击键才建占位项目 + 列表过滤空稿);可选提示词 MVP=**纯前端** directive→volatile(不改 `DraftStreamRequest` 契约、不动 `write_craft.md` 字节 → 金标准零回归);预设 MVP=先只上内置;续写=多候选卡点选插入。 > **纪律**:全程 TDD 小步提交,回填全守 HITL(AI 绝不 auto-replace,点选/接受才落草稿);`/refine`、`/rewrite` 只读不写库(不变量 #3)。**门禁全绿**:前端 vitest **636** / lint / tsc / build / coverage **95.36%**;后端 ruff / mypy **224** / pytest(含 `test_rewrite_node`)/ alembic 无漂移。**WFW-0~8 全部交付**(WFW-5/6/7 多 agent 并行 → commit `3dd7d28`;WFW-8 整章重写跨栈 → `6ed9c89`+`542c2ab`,契约 C-Rewrite 见 `memory/contracts.md`)。**未 push、未开 PR**(等用户示意)。 | 任务 | 状态 | 负责 | 依赖 | 反馈# | 备注 | |---|---|---|---|---|---| | WFW-0 进来即写(延迟落库) | ✅ | @frontend | — | #5 | `8389572` `/write`(DraftComposer) + `lib/start/useDeferredDraft`(懒建占位项目+种首章草稿,双重幂等,空进空出不落库) + 落地页 CTA「直接开始写」主/「先立项」辅 | | WFW-1 润色 / 再沟通 | ✅ | @frontend | — | #1#2 | `2f0b841` `useRefine`(同步/refine,版本栈,再沟通=上一版产出+新意见再回炉) + `refineApply`(接受回填按内容重锚,原文变则提示重选) + RefinePanel 内联卡 + Editor 上报选区 | | WFW-2 续写(多候选) | ✅ | @frontend | WFW-0 | #2 | `9195cf3` `useContinue`(continue 生成器 with_prior_chapter 读前文,多候选累加) + ContinuePanel 候选卡点选插入章末 + flush 后读最新草稿 | | WFW-3 内联工具箱 + 金手指露出 | ✅ | @frontend | — | #4#8 | `04f4785` `useToolboxTools`(客户端拉描述符) + InlineToolbox(列非 legacy 生成器含金手指) + `GeneratorRunner` 加 `onInsertText`(文本产物插入正文) | | WFW-4 AI 写作三槽(文风/剧情/自定义) | ✅ | @frontend | — | #3 | `44b0b8a` `directive.ts` 加 `PLOT_PRESETS` + `composeDirective` 合并;DirectivePanel 拆文风/剧情两组 chips + 自定义。纯前端零契约,`write_craft.md` 字节不变 | | WFW-5 内容感知书名 | ✅ | @frontend | — | #6 | `3dd7d28` 纯前端零后端 MVP:`buildManuscriptExcerpt`(首尾摘录+硬上界) + GeneratorRunner「用当前正文」按钮把摘录填 brief,book-title 据正文反推。**后续更佳版**:后端富化 book-title.md + `with_manuscript` 策略(未做) | | WFW-6 侧栏 IA 收敛(ContextDrawer) | ✅ | @frontend | — | #7 | `3dd7d28` 加法式 ContextDrawer 右侧速查抽屉 + 底栏「速查」按钮;设定库/大纲**完整只读速查**,伏笔/规则/文风**摘要+跳全宽页**(后续可补真实数据)。`CONTEXT_DRAWER_ENABLED` 一键回滚、旧侧栏/全宽页路由全保留 | | WFW-7 首次 AI 写作前极轻 genre 采集 + 无大纲降级 | ✅ | @frontend | WFW-0 | — | `3dd7d28` 无题材首次写章前弹 GenrePicker(临时并入本次 directive 不落库) + chapters 空时「尚无大纲,将按设定自由生成」提示,杜绝无题材 slop | | WFW-8 整章级「再沟通/重写」(对话式迭代) | ✅ | @backend·@llm·@frontend | — | 新#1 | `6ed9c89`(后端)+`542c2ab`(前端)。后端 SSE `POST .../rewrite`(`rewrite.md` 教条 + `rewrite_node` + `RewriteStreamRequest`,复用 assemble 记忆注入,只读不写库、工具非写节点、守 #3/#7/#9);前端 `useChapterRewrite`(版本栈)+`ChapterRewritePanel`+底栏「整章重写」:给意见→流式重写整章→多轮迭代→接受某版才替换正文(HITL)。契约 C-Rewrite。用户反馈「整章不满意要跟 AI 交流」 | | WFW-9 M1 AI 反问澄清(refine 侧,路线A 两阶段) | ✅ | @backend·@llm·@frontend | — | 新#2 | `1652ad9`(后端)+`e30c933`(前端)。多agent并行(后端整片‖ChoiceChips)+主线集成。非流式预检 `POST .../refine/clarify`→`ClarifyDecision`(结构化,analyst,只读,失败回退放行)+clarify_refine spec/教条(SPECS#24);既有 refine 未改。前端 `useClarify`+`ChoiceChips`+`clarify.ts`(foldClarifications/门控)+RefinePanel 门控接线:意见<10字或点「帮我理清方向」→AI反问选项→折答案进instruction→走既有refine。契约 C-Clarify。**M2 rewrite 侧两阶段 + M3 UX/E2E 待办**。调研计划见 scratchpad/clarify-dialogue-DEVPLAN.md(ephemeral) | --- ## 已封板:T6 · 创作工具箱(通用生成器框架)✅ > **背景**:对标竞品「创作工具箱」——把每个「XX 生成器」实现为声明式 skill(`AgentSpec` 的设计本意),用一条**通用执行路径**驱动。用户已定:① 走**通用 skill 框架**(非逐个硬编码)② 封面/图像本期跳过 ③ 文本生成器全做(上架已有 3 + 新建 8)。完整设计见 `~/.claude/plans/creation-toolbox-generators.md`。 > **P1 目标(DoD)**:`GeneratorTool` 描述符 + `TOOLBOX` 注册表 + 通用 `GET /skills/toolbox` & `POST /projects/{id}/skills/{tool_key}/generate` 端点 + **1 个最简生成器(脑洞)端到端打通**(mock 网关 E2E 零 token)。证实「加生成器 = 加一份声明」这条路。 > **执行波次**:A(@llm 脑洞 spec + IdeaListResult schema + brief_only context 策略 + 通用 run 路径) → B(@backend GeneratorTool/TOOLBOX 注册表 + 通用 toolbox/generate 端点 + response schema + 接线 build_gateway_for_tier) → C(@qa 脑洞 generate E2E:真 DB + mock 网关,断言 unknown key 404 / 无 provider 503 / 仅记 ledger 不写业务表)。 > **契约纪律**:B 改 OpenAPI → `memory/contracts.md` 记 C 扩 + `PROGRESS.md` 记一笔(前端 P2 才 `pnpm gen:api`)。守不变量 #2(只声明 tier)/#3(预览不写库)。**预计无 DB 迁移**(descriptor 在代码、复用既有表)。 | 任务 | 状态 | 负责 | 依赖 | 契约 | 备注 | |---|---|---|---|---|---| | T6.1 脑洞 spec + IdeaListResult schema + brief_only context + 通用 run 路径 | ✅ | @llm | — | 新 schema/spec | 完成:`schemas.py` 加 `Idea`/`IdeaListResult`;`specs.py` 加 `brainstorm_spec`(light, reads=[projects], writes=[]);`generation_node.py` 加 `build_brief_context` + **通用 `run_generator`**(据 `spec.output_schema` 校验 parsed,不绑定具体类型,任一声明式生成器共用);两包 ruff/format/mypy 干净、新增 13 测 + 全包 252 passed 无回归。**@backend T6.2 可起:从 `ww_agents` 取 `brainstorm_spec`/`IdeaListResult`、从 `ww_core.orchestrator` 取 `run_generator`/`build_brief_context`** | | T6.2 GeneratorTool 描述符 + TOOLBOX 注册表 | ✅ | @backend | T6.1 | C3扩稳定 | `packages/skills/ww_skills/toolbox.py`(描述符类型,frozen) + `toolbox_registry.py`(`TOOLBOX` 11 条 + `get_tool`)。25+7 测 | | T6.3 通用端点 toolbox/generate(+ingest) | ✅ | @backend | T6.2 | C3扩稳定 | `routers/toolbox.py` 3 端点 + `schemas/toolbox.py` + `services/toolbox_context.py`(纯 dispatch) + `get_tier_gateway_builder` 注入缝。generate 预览仅 commit ledger;ingest 复用 continuity 409 + partition_writes 白名单。未知 key 404 / 无 provider 503 | | T6.4 E2E(toolbox 全路径) | ✅ | @qa | T6.3 | — | `tests/test_t6_toolbox_e2e.py` 5 用例真 pg + mock 网关零 token:列表 11 key、brainstorm 预览仅 ledger 零业务写(#3)、glossary ingest 409→ack 放行真落 world_entities、unknown 404、无 provider 503、token 不泄漏。无端点 bug | | T6.5 7 新生成器 spec+schema + with_outline_chapter | ✅ | @llm | — | 新 schema/spec | `schemas.py` +7 输出 schema、`specs.py` +7 spec(book-title/blurb/name/golden-finger/glossary/opening/fine-outline,只声明 tier #2)、`generation_node.py` +`build_outline_chapter_context`。309 passed | | T6.6 工具箱落地页 + 声明驱动 Runner | ✅ | @frontend | T6.3 | — | `app/projects/[id]/toolbox/page.tsx`(RSC) + `components/toolbox/{ToolboxPage,ToolCard,GeneratorRunner}` + `lib/toolbox/*`(纯函数) + LeftNav「工具箱」+ ⌘K `nav-toolbox`/`action-gen-*`。legacy 3 跳现页。vitest 155→279/build OK | | P2 收尾(限流/noopener/Committable) | ✅ | @backend+@frontend | — | — | 限流→`decisions.md` 记延后(单用户原型);noopener 与 Committable 经查本分支早已修;`provider_deps.py` 残留 `type:ignore` 为 SDK 私有属性探活,load-bearing 保留 | **T6 出口(DoD)✅ 达成**:通用执行器驱动全部 8 个新生成器 + 工具箱落地页声明驱动;后端门禁绿(ruff/format/mypy **195**/alembic **无漂移**/pytest **583**);前端门禁绿(gen:api/lint/tsc/vitest **279**/build);E2E mock 网关零 token 全路径走通。**实景 browse 复验(工具箱卡片栅格→脑洞预览→词条入库)待做。** 后续可选 P5(封面图像轨道 / 自定义 skill 运行期 schema / 收敛已有 3 竖井)。 --- ## 已封板:Prompt 管理重构(方案A)+ 全量 prompt 重写 ✅ > **背景**:把 21 个内置 agent 的 `system_prompt` 散文从 `specs.py` Python 常量外置为 `.md` 文件,并统一内置/用户 skill 的只读解析入口。设计真源 `docs/design/prompt-management.md`;契约 `memory/contracts.md` C6-ext;决策/坑见 `memory/{decisions,gotchas}.md`(2026-06-24)。**多 agent 工作流交付**:TDD + 并行开发 + 交叉验证评审。 | 任务 | 状态 | 负责 | 备注 | |---|---|---|---| | 方案A 步骤1–3:`spec_model`/`prompt_loader`/`prompts/*.md`×21/`schema_catalog`/`SPECS`/`REVIEW_RESERVED_NAMES` | ✅ | @llm | 金标准 sha256 取运行时值;`SPECS[name] is *_spec` 同一实例;三集合 name 恒等 | | 方案A 步骤4–5:`SpecResolver`(内置纯内存零 DB)+ SkillRegistry 入库守卫前移 + `toolbox_registry` 桥接 `SPECS` | ✅ | @backend | 守卫锚 `REVIEW_RESERVED_NAMES ∪ set(SPECS)`;删 12 生成器 `*_spec` 直接 import | | 打包/CI:`.gitattributes eol=lf` + wheel `artifacts` 带 `prompts/*.md` + `agents-wheel-smoke` 冒烟;`structlog` 补声明 | ✅ | @devops | 裸装 `import ww_agents; assert SPECS` 通过;`.gitattributes` 须完整嵌套路径 | | 集成回归:#12 编排器无回归 + #15 apps/api import-smoke(`*_spec is SPECS[name]`)+ #16 CI 冒烟 | ✅ | @qa | 内容无关,长青 | | 全量重写 21 prompt 内容(任务对齐 + prompt 工程最佳实践,schema 契约/不变量保持) | ✅ | @llm/@qa | 激进重写;金标准 fixture 重生成;护栏全验(无占位/无 model 名/中文/≤1 尾 LF/四审只读) | **出口(DoD)✅**:后端门禁绿 ruff/format clean · **mypy 209** · **pytest 744 passed**;缓存断点前块字节稳定(不变量 #9,金标准守);零 schema 变更、前端零影响(无 `pnpm gen:api`)。已合并 `develop`(外置 wave `4f561da`、重写 wave `eb356c4`)。**后续可选**:编排器 + apps/api 3 路由切 `SpecResolver` 后删 `*_spec` 导出。 --- ## 已封板:K1 · Kimi Code OAuth(订阅 plan 接入)✅ > **背景**:用户要求接入 Kimi Code 订阅 plan(device-flow OAuth)。**已知情接受 ToS/封号风险**——研究确认:用订阅 plan 调用需伪造官方客户端 `User-Agent` + `X-Msh-*` 头,Kimi ToS 视为违规、可能封停会员(见变更日志研究结论)。用户明确选择此路径(非合规的 API Key 路径)。与既有 API-key провайдер `kimi` **分离**,新增独立 provider `kimi-code` 以便切换。 > **集成契约(研究确认,实现时对照 `github.com/picassio/pi-kimi-coder` 校验)**: > - device flow:`POST https://auth.kimi.com/api/oauth/device_authorization`(client_id `17e5f671-d194-4dfb-9706-5516cb48c098`,env `KIMI_CLIENT_ID` 可覆盖,scope `kimi-code`)→ 返回 user_code/verification_uri/device_code/interval;轮询 `POST https://auth.kimi.com/api/oauth/token`(grant device_code,处理 authorization_pending/slow_down)。refresh:同 token 端点 grant refresh_token。 > - 推理:base `https://api.kimi.com/coding/v1`(OpenAI 兼容),model `kimi-for-coding`,`Authorization: Bearer ` + **必需伪造头**:`User-Agent`(如 `KimiCLI/1.x`) + `X-Msh-Platform`/`X-Msh-Version`/`X-Msh-Device-Name`/`X-Msh-Device-Model`/`X-Msh-Os-Version`/`X-Msh-Device-Id`(持久 UUID)。缺头→403。 > - token:access ~15min / refresh ~30d → **建网关时按需刷新**(临近过期刷新并持久化)。存储:`provider_credentials` 加 `auth_type`(api_key|oauth) + `oauth_enc`(Fernet 加密 JSON `{access_token,refresh_token,expires_at}`)。 > **执行波次**:A(@db 加列+迁移 ‖ @llm Kimi coding 适配器) → B(@backend OAuth device 服务 + 端点 + store + `_build_provider_adapter` 接线) → C(@frontend 设置页连接/轮询/断开 + 路由档位) → D(@qa E2E device-flow mock + 头断言)。 > **测试纪律**:device flow 注入 fake httpx(零联网);适配器断言伪造头集;token 刷新逻辑单测;E2E mock auth+coding 两端点。 | 任务 | 状态 | 负责 | 依赖 | 契约 | 备注 | |---|---|---|---|---|---| | K1.1 provider_credentials 加 auth_type + oauth_enc 列 | ✅ | @db | — | C2扩 | 完成:models 加 auth_type/oauth_enc + api_key_enc 改 nullable;迁移 `1f011c42bd4d`;alembic check 无漂移。⚠️ K1.3 @backend 须把 `StoredCredential.api_key_enc` 改 `bytes\|None`(见下方请求 + memory)| | K1.2 Kimi coding 适配器(伪造头 + OAuth bearer) | ✅ | @llm | — | C1扩 | 完成:`adapters/kimi_code.py`(KimiCodeAdapter 子类化 OpenAICompatAdapter + 伪造头 + 稳定 device_id);`build_adapter` 加 `kimi-code` 分支;UA 验证为 `KimiCLI/1.5`(pi-kimi-coder 仅设 UA,X-Msh-* 为契约附带,见 C1扩 校正);11 测全绿 | | K1.3 OAuth device 服务 + 端点 + store + 接线 | ✅ | @backend | K1.1,K1.2 | C3扩 | 完成:`services/kimi_oauth.py`(start/poll/refresh,注入 httpx Protocol)+3 端点(start 202→jobs 后台轮询/disconnect/status,复用 run_job)+`StoredCredential` 收口(api_key_enc→nullable+auth_type/oauth_enc,K1.1 的 2 mypy red 已消)+store `upsert_oauth_credential`/`delete_credential`+`_build_provider_adapter` OAuth 按需刷新(300s 缓冲)。**token 不进 job 结果/日志/响应**。全仓门禁绿(ruff/format/mypy **154**/alembic 无漂移/pytest **430**)。OpenAPI 加 3 端点→**@frontend K1.4 须 `pnpm gen:api`** | | K1.4 设置页 Kimi Code 连接流 | ✅ | @frontend | K1.3 | — | 完成:`gen:api` 纳入 3 OAuth 端点;`KimiCodeOauth` 连接区(显 user_code+开授权页+轮询 job→已连接/断开)+ 可编辑档位路由(含 `kimi-code:kimi-for-coding`);纯逻辑 `lib/settings/{kimiOauth,providers}` + 25 新 vitest;前端门禁全绿(gen:api/lint/typecheck/vitest 155/build)。运行时验真 auth.kimi.com 留 K1.5 | | K1.5 K1 E2E | ✅ | @qa | K1.4 | — | `tests/test_k1_kimi_oauth_e2e.py`(4 用例,真 pg):device-flow(pending×2→成功)→加密 token 真落 pg(无明文+回环)→建网关断言 KimiCodeAdapter coding base+伪造头+bearer→refresh-on-build 更新存量 token(DB 真源)→disconnect 清除;token 全程不进 job 结果/状态/start 响应。全仓门禁绿(ruff/format/mypy **154**/alembic 无漂移/pytest **434**)。**K1 完成,可封板** | **K1 出口(DoD)✅ 全部达成**:作者在设置页用 Kimi Code OAuth 登录(device flow,显 user_code + 开授权页 + 轮询 job)→ token Fernet 加密入库(`oauth_enc`,全程不进 job 结果/日志/响应)→ 把档位路由到 `kimi-code:kimi-for-coding` → 建网关时构 `KimiCodeAdapter`(coding base + 伪造 UA/X-Msh-* 头 + bearer)→ token 临近过期建网关时自动刷新并回写。后端门禁绿(ruff/format 干净 / mypy **154** / alembic **无漂移** / pytest **434**);前端门禁绿(gen:api/lint/tsc/vitest **155**/build);E2E(`tests/test_k1_kimi_oauth_e2e.py` 4 用例)device-flow mock 零联网走通,DB 真源逐项断言(含 token 不泄漏 + refresh-on-build + disconnect 清除)。**K1 完成。** **⚠️ 仍需用户用真账号对真 auth.kimi.com/api.kimi.com 做一次联网冒烟(device-flow 需伪造官方客户端 UA → ToS 视为违规、可能封停会员,用户已知情自担);E2E/单测仅验逻辑、不联网。** --- ## 状态:M1–M5 全部完成(含 M5 三项收尾余项已清理) > **当前无活跃阶段。** Phase 0–5(M1–M5)已全部交付,M5 的三项非阻塞收尾余项(①Codex 读端点、②Anthropic/Gemini 真实适配器选择、③`packages/skills` 纳入 workspace)亦已清理。各阶段详情见下方「已归档阶段」+「变更日志」。 > 最终门禁(本次全绿):后端 ruff/format 干净、mypy **144 文件**、alembic **无漂移**、pytest **400 passed**;前端 lint/typecheck 干净、vitest **130 passed**、build OK。 > **下一步若继续**:DEV_PLAN §4 的 P2/后续(向量检索 / 真任务队列 / 全书一致性扫描 / 社区市场),进入时把对应任务拉入本表。 > **仍开放的非阻塞备注**:@devops 复核根 `uv.lock`(@backend 改了 `packages/skills/pyproject.toml` deps 后已 `uv sync` 重建 ww-skills);Anthropic/Gemini 真实回退已接线(`build_adapter` 工厂 + 按 provider 选适配器)但尚未对真 SDK 联网验证(单测/E2E 用 OpenAI 兼容 mock)。 --- ## 已归档阶段
Phase 5 · 生成 + 多 provider + 扩展 (M5) — ✅ 全部完成(2026-06-19) T5.1 worldbuilder + character-gen 节点(C6✅扩;群像防雷同注入已生成+已有 + 入库前 continuity 校验缝 `precheck_generated_cards`)@llm · T5.4 网关多 provider 韧性(C1✅扩;Anthropic/Gemini 适配器 + 回退链 + `CircuitBreaker` + tenacity 退避 + 能力协商/降级 `served_by.degraded`,无 OpenAPI 形变)@llm · T5.5 Skill 运行时 + 规则(C8 + C3✅扩;`SkillRegistry.load` 越权声明拒绝 + 表权限沙箱 filter_reads/partition_writes + `POST /rules`)@backend · T5.2 生成/入库 API(C3✅扩;world/characters generate 预览 + POST characters 入库双 gate:continuity 预检 409 + partition_writes 白名单 + schema→JSONB 形变 + GET rules/skills;`build_gateway_for_tier` 接线 chain_from_routing 回退)@backend · T5.3+T5.6 角色生成器 + 世界观设计器 + 设定库 Codex + 规则页 + 技能库 + 命令面板(⌘K)@frontend · T5.7 M5 E2E + 切 provider(真 Gateway deepseek 失败→openai 回退、DB ledger 记实际服务方、能力降级)@qa。 出口达成:后端门禁绿(ruff/format 干净 / mypy 142 / pytest 391 / alembic 无漂移,复用 T0.2 既有表零建表迁移);前端门禁绿(gen:api / lint / tsc / vitest 125 / build);E2E 真 DB + mock 多 provider 零 token 走通。**M5 完成 → M1–M5 全部交付。** 三非阻塞余项(①Codex 读端点、②Anthropic/Gemini 真实适配器选择、③`packages/skills` 纳入 workspace)随后于同日全部清理(见变更日志 follow-up #1–#4)。
Phase 4 · 文风 (M4) — ✅ 全部完成(2026-06-19) T4.2 style-auditor 双轨 + 第四审(C6✅扩/C4✅扩;`style_extract_spec`(analyst,16 维指纹带证据,不进 review 图) + `style_drift_spec`(name="style",light,第四审并入 REVIEW_SPECS) + `refiner_spec`(writer 纯文本),REVIEW_SPECS 四审齐 + collect/SSE 加 style)@llm · T4.1 jobs 长任务基建(C3.5✅;`JobRepo`/`SqlJobRepo` + `run_job` 独立 session + lifespan zombie reaper)@backend · T4.3 API style + refine(C3✅扩;`POST /style` 202+jobs BackgroundTask、`GET /style` 最新指纹、`POST /refine` 同步 diff 不写库)@backend · T4.4 文风页 + 漂移 + 回炉 + job 轮询(`useJobPoll` 状态机 + StyleUpload/FingerprintView + 审稿页第四审 StylePanel + RefineView diff 采纳合入)@frontend · T4.5 M4 E2E(学文风→202 job→后台跑完→指纹真落 pg version+1 + 第四审 SSE/`chapter_reviews.style` 真填 + 回炉不写库)@qa。 出口达成:后端门禁绿(ruff/format 干净 / mypy 115 / pytest 296 / alembic 无漂移,全程零建表迁移,复用 T0.2 预留的 `style_fingerprint`/`chapter_reviews.style`/`jobs`);前端门禁绿(gen:api / lint / tsc / vitest 96 / build);E2E 真 DB + mock 网关零 token 走通 学文风→第四审打分→回炉,DB 真源逐项断言。@docs 已把 `GET /style` 回写 ARCH §7.2/PRODUCT_SPEC §6。
Phase 3 · 伏笔 + 节奏 (M3) — ✅ 全部完成(2026-06-18) T3.1 foreshadow Repository + 纯函数状态机(OPEN/PARTIAL/CLOSED/OVERDUE 全转移 + 到期判据 + `ForeshadowLedger*` 写侧 repo)@backend · T3.4 outliner Agent + 伏笔窗口(C6✅扩;`outliner_spec` + `OutlineResult`(含 foreshadow_windows) + `run_outline` 只读节点)@llm · T3.3 foreshadow-analyst + pace-checker 节点(C6✅扩/C4✅扩;并入 REVIEW_SPECS 三审齐 + collect 分列 + SSE foreshadow/pace 事件)@llm · T3.2 到期扫描 BackgroundTask + 登记/状态接口(验收后 `run_overdue_scan` 自建 session 置 OVERDUE + `POST/PATCH .../foreshadow`,重复/非法→VALIDATION 422)@backend · T3.5 API outline + foreshadow(C3✅扩;`GET .../foreshadow?status=` 看板 + `POST .../outline` upsert outline 表含窗口)@backend · T3.6 伏笔看板 + 大纲编辑器 + 节奏报告(四泳道 + beats+窗口徽标 + 审稿页节拍图▁▃▅)@frontend · T3.7 M3 E2E(伏笔埋设→进展→验收后扫描 OVERDUE + 大纲含窗口 + 三审 SSE,strict-xfail 钉住并发记账 bug)@qa · T3.8 [bugfix] 并行审记账并发安全(`SqlAlchemyLedgerSink.record` 改 add-only 去 `await flush`,解 AsyncSession 并发 flush 冲突,xfail 转 XPASS 后解钉)@llm。 出口达成:后端门禁绿(ruff / mypy 111 / pytest 228 passed(0 xfailed/0 failed)/ alembic 无漂移,`foreshadow` 表已 T0.2 建齐无新建表);前端门禁绿(gen:api / lint / tsc / vitest 69 / build);E2E 真 DB + mock 网关零 token 走通,DB 真源逐项断言。T3.7 暴露并发记账 bug → T3.8 修复 → 解钉 xfail。
Phase 2 · 一致性 + 验收 (M2) — ✅ 全部完成(2026-06-18) T2.1 续审 AgentSpec+结构化输出契约+网关 instructor 接线(C6✅稳定,改 C1:`run()` 填 `parsed`)@llm · T2.2 LangGraph 并行审+collect 留痕+review SSE(C4✅扩)@llm · T2.3 摘要/审稿留痕 Repository+章节 version(@backend,表已 T0.2 建)· T2.4 验收事务+冲突 gate(单原子事务 + R2 事务外提炼 digest)@backend · T2.5 review API+历史(C3✅扩)@backend · T2.6 审稿报告页+冲突裁决+验收 gate @frontend · T2.7 M2 E2E(真 DB+多档位 mock 网关零 token)@qa。 出口达成:后端门禁绿(ruff / mypy 90 / pytest 114 / alembic 无漂移);前端门禁绿(gen:api / lint / tsc / vitest 45 / build);E2E 写→审→裁决→验收→摘要入库闭环、DB 真源逐项断言。设计决策 R1–R6 全兑现(见 `memory/decisions`)。
Phase 1 · 写章闭环骨架 (M1) — ✅ 全部完成(2026-06-18) T1.1 LLM 网关接口+DeepSeek 适配器+usage_ledger 记账(C1✅稳定)@llm · T1.2 记忆 assemble 确定性选择(C5✅稳定)@backend · T1.3 LangGraph 写章节点+Postgres checkpointer+SSE(C4✅稳定)@llm · T1.4 立项+写章 draft(SSE)+PUT 自动保存(C3✅稳定)@backend · T1.5 AppShell+作品库+5 步立项向导 @frontend · T1.6 写作工作台(三栏+流式打字机+注入透明面板+自动保存)@frontend · T1.7 提供商凭据管理(Fernet 加密+测试连接)@backend · T1.8 设置页(档位路由+凭据行)@frontend · T1.9 M1 E2E(真实 DB+mock 网关零 token;暴露并修复 ledger 提交 bug)@qa。 出口达成:后端门禁绿(ruff / mypy 68 / pytest 57 / alembic 无漂移);前端门禁绿(gen:api / lint / tsc / vitest 24 / build);E2E 闭环走通。 M2 起可补的 M1 占位:本章助手/注入透明面板、向导主角/世界观字段、档位路由可写。
Phase 0 · 基建与栈修订 — ✅ 全部完成(2026-06-18) T0.1 monorepo 骨架 ✅ @devops · T0.2 16 MVP 表迁移(无漂移,users stub)✅ @db · T0.3 FastAPI 骨架 + config + 错误信封(带 request_id) + OpenAPI + `GET /jobs/:id` + structlog ✅ @backend · T0.4 前端骨架 + 纸感 token + OpenAPI→TS gen:api ✅ @frontend · T0.5 CI(含 pg service + alembic 漂移校验)✅ @devops · T0.6 架构栈修订回写规格 ✅ @docs。 出口达成:backend 门禁绿、frontend 门禁绿、API 实跑 200 带 `x-request-id`。
--- ## 变更日志(append-only,最新在最上) > 格式:`- [YYYY-MM-DD] @skill 完成/进展 Txx — 一句话结果 + 影响的契约/文件` - [2026-07-06] @backend/@llm/@frontend — **✅ 灵感⑦ 群像按定位配比·缩减版 + §0 归属纠偏(develop 直提)**。角色群像生成支持「按定位配比」下发。**契约变更** `CharacterGenerateRequest` 加 `role_mix:dict[str,int]|None`——`model_validator(mode="after")` 定义与既有 `count` 的关系:在场时校验(非空、每项≥1、总和≤`MAX_GENERATE_COUNT`=12)并把 `count` 归一为各值之和(构造期归一化,忽略客户端 count);缺省退回单一 `role`+`count`。**§0 归属纠偏**:`build_character_gen_context`+`run_character_gen`(`ww_core/orchestrator/generation_node.py`)确为 **@llm 域**——加末位默认 `role_mix=None` 关键字参,在场时注入「角色定位配比」块(保插入序、确定性、无时间戳,守 #6/#7 assemble 纯函数;覆盖单一 role 行);`routers/generation.py` 穿 `role_mix=body.role_mix`。`character-gen.md` 加「按配比严格分配 role、各定位之和=总数」纪律 → **regen `prompt_hashes.json`**(仅 character-gen 哈希变,**不进 SPECS→不 bump 计数**)。**前端** `cards.ts` 加 `sanitizeRoleMix`(去空/去重/clamp≥1/按 12 截断)+`roleMixTotal`+`buildCharacterGenerateRequest` 第 4 参 roleMix;`useCharacterGen` `CharacterGenInput.roleMix` 穿参;新 `RoleMixEditor.tsx` + `CharacterGenerator` SegmentedControl 单一/配比双模。**TDD**:schema 校验 7 例(总和≤12/空 dict/非正值拒绝)+ context 注入 2 例 + run 穿参 1 例 + 前端 cards 构造/clamp 5 例 + hook renderHook 1 例。**门禁全绿**:后端 ruff/format 干净 · mypy **222** · pytest **858**(+11)· 无迁移(未动模型,未跑 alembic);前端 gen:api/lint/typecheck 干净 · vitest **543**(+6)· coverage 95%(cards.ts 100%)· build OK。记 contracts C3。守不变量 #2/#6/#7/#9。**无 DB 迁移**。 - [2026-07-06] @llm/@db/@backend/@frontend — **✅ 灵感③ 人物塑造 advisory 审(develop 直提)**。写→审链新增第五审「人物塑造」:**advisory 单维——仅建议、不阻断验收**(不进 conflicts、不碰 `assert_conflicts_resolved`、**不入 `REVIEW_RESERVED_NAMES`**,D2);只做人物塑造、不做氛围(氛围噪声大折进 style/pace)。**新 SPEC** `characterization_spec`(`tier="analyst"` 不写 model,`reads=("characters",)` 以人物卡 motive/appearance 为客观锚点,`writes=()` 只读,`prompts/characterization.md` **显式忽略文本长度与辞藻华丽度**、引用逐字片段+置信度对抗 LLM-judge 冗长/辞藻偏见)+ schema `CharacterizationReview{issues:[…character/aspect/where/quote/diagnosis/suggestion/confidence]}`(全字段默认值守解析韧性)+ 注册 `SCHEMA_CATALOG["characterization"]`;**计数三处 22→23**(`specs.py` assert / `test_prompt_loader.py` len / `schema_catalog.py` docstring)+ **regen `prompt_hashes.json`**(仅加一条,无其余漂移)。**DB** `chapter_reviews` 加 1 列 `characterization`(**JSONB nullable**,迁移 `f6a7b8c9d0e1`,nullable 免 backfill)。**契约变更** `ReviewHistoryItem`+`ReviewView` 加 `characterization`;接线链 `graph.py REVIEW_SPECS` 末位追加 + `chain/nodes.py ReviewRecordRepo` + `collect.py {CHARACTERIZATION/extract_characterization/ReviewRecorder/collect_reviews}` + `review_repo.py {Protocol/SqlReviewRepo/_to_view}` + `sse.py {EVENT_CHARACTERIZATION/characterization_event/_section_result_events}`(normalize 按 sorted 键自动纳入,字典序排最前不打乱既有四审断言)。**前端** `lib/review/{sse.ts,history.ts,useReviewStream.ts}` + 新 `CharacterizationPanel.tsx`(advisory 只展示、**无裁决 UI**、按置信度降序 + 低置信度折叠控注意力预算)+ `ReviewReport.tsx` 挂面板 + 双 seed。**门禁全绿**:后端 ruff/format 干净 · mypy **221** · pytest **848**(+核心 char 审 12 + agents spec 11 + api accept 2 等)· alembic 无漂移;前端 gen:api/lint/typecheck 干净 · vitest **536**(+10 含 `test_characterization_does_not_block_accept` 对应前端 reducer/normalize/parse)· coverage 95% · build OK。记 contracts C3+C4。守不变量 #2/#3/#9。依赖 ⑧(motive/appearance 已入 CharacterCard 契约,作客观锚点)。 - [2026-07-06] @backend/@llm/@frontend — **✅ 灵感⑤ AI 立项方案生成+预填(develop 直提)**。向导阶段(project 未建)一键推演结构化书级蓝图预填立项向导。**新 SPEC** `project_plan_spec`(`tier="analyst"` 不写 model,`reads=()`/`writes=()` 纯预览,`prompts/project-plan.md`)+ schema `ProjectPlanResult`(书名候选 list + 时空背景/叙事结构/故事核心/结局设计/基调,**全字段默认值守解析韧性**)+ 注册 `SCHEMA_CATALOG["project-plan"]`;**计数三处 21→22**(`specs.py` assert / `test_prompt_loader.py` len / `schema_catalog.py` docstring)+ **regen `prompt_hashes.json`**(仅加一条)。**新端点** `POST /skills/project-plan/generate`(**不带 project 前缀**绕开 toolbox 404)← `ProjectPlanGenerateRequest`(全字段 `max_length` CR-H9,`brief` 用 `str|None` 令 codegen 可选)→ `ProjectPlanView`;**种子门控**缺 genre/logline→422,无凭据→503。复用 `build_brief_context`+`run_generator`(`run_generator`/`_build_request` 的 `project_id` 放宽 `uuid.UUID|None`——向导无 project,ledger.project_id 本 nullable);新缝 `get_project_plan_gateway`(analyst)。**零迁移**。**前端** `wizard.ts` 加 `mapPlanResultToWizardForm`(书名/结构/基调→对应字段;时空背景/结局设计→折进 premise **不静默丢**;结局取向/视角预设枚举不臆造)+ `applyPlanPatch`(**仅回填空字段、保护已编辑**)+ `canGeneratePlan`/`toPlanSeedRequest` + `useProjectPlan` hook(renderHook 测试)+ `ProjectWizard.tsx` 第 2 步 `PlanAssistant`(生成→预览→按字段回填)。**门禁全绿**:后端 ruff/format 干净 · mypy **218** · pytest **814**(+新端点 6 + agents spec 8 测)· alembic 无漂移(零 ORM 变更);前端 gen:api/lint/typecheck 干净 · vitest **526**(+28)· coverage 95%(wizard/generation 100%)· build OK。记 contracts C3 新端点。守不变量 #2/#3/#9。依赖 ④(已交付)。 - [2026-06-26] @orchestrator — **全项目 Code Review(七维度)完成 → `QA-CR` 整改批立项**。6 个专科 reviewer 并行(后端 FastAPI 架构 / Python 规范 / LangGraph 最佳实践 / 前端 React 架构 / 前端 TS 类型安全 / 应用安全)+ 依赖时效自查;212 py + 168 tsx 全覆盖。绿门禁下仍揪出 **2 CRITICAL + 13 HIGH + 一批 MEDIUM/LOW**:**C1** Next.js 15.1.3 RCE(CVE-2025-66478「React2Shell」,App Router+RSC 受影响,升 15.1.9/15.5.7/16.x + 轮换密钥);**C2** 多章链缺 `recursion_limit`(默认 25 supersteps、每章 4 节点→超 ~6 章即崩,批量产卷不可用)。HIGH 含 Kimi OAuth 占 DB 会话 / httpx 泄漏 / Anthropic 计费记 0 / request_id 未贯通 / `.env.example` 提交可用 Fernet key / 文本输入无 `max_length` / 前端 errorCode 重复 / Drawer 焦点 / RefineView 未取消。**确认无虞**:SSRF(base_url 仅硬编码)/SQL 注入(全 ORM)/XSS(无 dangerouslySetInnerHTML)/错误信封不泄密/Kimi device-flow 无 CSRF。已登记 `QA-CR` 任务表(CR-C1/C2 + CR-H1..H12 + CR-M1..M5 + CR-L1 + CR-D1)按 owner 分派 @frontend/@backend/@llm/@devops,落地顺序见当前阶段 background。**本批为评审立项,尚无代码改动。** - [2026-06-23] @orchestrator — **🎉 多章工作流链(Chain Workflow)交付 + 多 agent 交叉评审 + CRITICAL 修复 + 合并 develop(merge `513bf71`)**。对标星月「一键多章」+ 差异化「每章过四审、遇冲突才停人」。设计契约 `docs/design/chain-workflow.md`(commit `5ee9799`)。**多 agent 逐学科建造**(契约先行、目录单写者):**C1 @llm**(`7f3eaab`) LangGraph **cyclic 链图** `orchestrator/chain/{state,nodes,graph}.py`(`build_chain_graph`:write→review→decide→accept 循环 + **interrupt-on-conflict** + resume;删死代码 `build_write_graph`,`write_node` 本体留用;7 单测 mock 网关+fake session+MemorySaver)·**C2 @backend**(`29349dc`) 服务/3 端点/schema/checkpointer 接线(见下条)·**C3 @db**(`548b7ab`) langgraph 检查点 4 表迁移 `d3e4f5a6b7c8`(`autocommit_block` 避开 `CREATE INDEX CONCURRENTLY` 与 alembic 事务死锁;`env.py` `include_object` 豁免这 4 表防漂移误报)·**C4 @qa**(`061792d`) E2E `tests/test_chain_workflow_e2e.py` 真 pg + mock 网关零 token(无冲突两章全自动 + 冲突→interrupt→awaiting→resume→accept)。**3 路对抗交叉评审**(python/fastapi/不变量)在**绿门禁下仍揪出 1 CRITICAL + 7 HIGH**——证伪"测试过=没问题":CRITICAL=链把**绑请求 session 的 gateway** 传进 BackgroundTask,后台跑时 session 已关→write/review 的 `usage_ledger.record()` 写进死 session **静默丢失**(链计费全丢,违 #1;E2E 清了 ledger 却没断言其存在故漏掉)。**修复**(`d1ea83b`):网关改 `chain_gateway_builder` 按节点 session 重建(仿 `digest_gateway_builder`/`style.py`);日志脱敏(`_classify_job_error`+`exc_type`,去 `str(exc)`,§5);resume 原子 claim `JobRepo.claim_awaiting_to_running`(防并发双跑同 thread_id,非 awaiting→409)+ 所有权校验(`JobView.project_id`,跨项目→404);`ChainResumeAccepted`(去 start/count=0 哨兵);删死导入 `extract_conflicts`;**补回归断言**(链 `usage_ledger` 必有行 + `chapter_reviews` 每章一行)。**出口(DoD)✅**:后端门禁绿 ruff/format 干净 · mypy **194 Success** · alembic **无漂移**(业务表零迁移,复用 `jobs.status=awaiting_input` + `result.awaiting_chapter`;唯一新 DDL=langgraph 检查点表)· pytest **608 passed**(+6 回归测)。首次真正启用 LangGraph checkpointer/interrupt。守不变量 #1/#3/#4/#5/#9。**剩余下一步(未做)**:① 前端链 UI(`pnpm gen:api` 纳入 run/resume + 发起页/进度轮询/awaiting 裁决续跑面板,复用 `ConflictAdjudication`);② §12 文档回写 `ARCHITECTURE.md §5.2`(补 cyclic 链图 + 更正现状:写章直连流式/单章 accept 为事务/链图首启 checkpointer);③ 未 push 远端。可选:续写/扩写节点入链;UGC 链市场需多租户(deferred)。 - [2026-06-23] @backend — **✅ Chain Workflow C2(多章链 服务+端点+schema+checkpointer 接线,分支 `feat/chain-workflow`)**。承 C1(@llm)链图(`build_chain_graph` §3.3),落地服务/端点壳:① **3 端点**`routers/chain.py`(`POST .../chains/{key}/run`→202 `ChainRunAccepted`;`POST .../chains/runs/{job_id}/resume`→202;`GET /jobs/{id}` 复用)。校验:count 1..50→422、未知 chain_key→404、项目不存在→404、resume job 不存在→404、resume 非 awaiting→**409 新码 `CONFLICT`**、无凭据→503。② **schema**`schemas/chain.py`(`ChainRunRequest/ChainRunAccepted/ChainResumeRequest`,`ConflictDecision` 复用)。③ **服务**`services/chain_runner.py`(`run_chain_job` 仿 `run_job` 壳自建独立 session 驱动链图:set_running→ainvoke→据 `__interrupt__` 置 `awaiting_input` 或 `done`/`failed`;`build_accept_op` 在 apps/api 装配验收事务闭包注入图节点,守 #3/#4;token 不入 result/日志)+ `services/chain_deps.py`(`get_checkpointer_factory`:运行时 `AsyncPostgresSaver` 上下文 / 测试 MemorySaver)。④ **零迁移**(§7):复用 `jobs`,新增 `status="awaiting_input"` + `JobRepo.set_awaiting`,awaiting 章经 `result.awaiting_chapter`;新错误码 `ErrorCode.CONFLICT`(409)。⑤ **新网关缝**`project_deps.build_chain_gateway`/`get_chain_gateway`(按请求 tier writer/analyst/light 分派——单档网关恒返该档会错路由 review/digest,见 gotcha)+ `get_digest_gateway_builder`。**单测**`apps/api/tests/test_chain.py` 12 用例(mock 网关 + MemorySaver + fake session/accept_op,无 DB/无网络/无真 LLM):run→202、resume→202、未知 key 404、count 越界(>50/0) 422、resume 非 awaiting 409、项目/ job 不存在 404、run_chain_job 无冲突→done+written 满、冲突→awaiting→resume→done、错误脱敏、accept_op 冲突缺判→CONFLICT_UNRESOLVED。**全仓门禁绿**:ruff/format 干净、mypy **193 Success**、alembic **无漂移**(无 ORM 变更)、pytest **600 passed**。守不变量 #1/#3/#4/#5/#9。记 contracts C-Chain 稳定 + 2 decision + 3 gotcha。**唯一新增 DDL(langgraph 检查点表)= C3 迁移;前端链 UI = 契约稳定后 @frontend follow-up(本期非目标)。** - [2026-06-22] @orchestrator — **🎉 T6 创作工具箱(通用生成器框架)全工具箱交付 + 独立复核(分支 `feat/t6-creation-toolbox`)**。多 agent 并行波次:Wave 0(@llm T6.5 ‖ @backend 描述符类型 ‖ P2 收尾)→ Wave A(@backend T6.2+T6.3 TOOLBOX+端点合龙,契约稳定点)→ Wave B(@frontend T6.6 ‖ @qa T6.4)→ Wave C 收口,目录单写者零写冲突、契约先行、零返工。**@llm T6.5**:`ww_agents` +7 输出 schema + 7 spec(book-title/blurb/name/golden-finger/glossary/opening/fine-outline,只声明 tier #2)+ `build_outline_chapter_context`(with_outline_chapter 策略)。**@frontend T6.6**:`gen:api` 纳入 3 端点;`app/projects/[id]/toolbox/page.tsx`(RSC) + `components/toolbox/{ToolboxPage,ToolCard,GeneratorRunner}` + `lib/toolbox/*`(声明驱动纯函数,按 `input_fields` 渲染表单、按 `output_kind` 渲染预览、可入库者复用 `ConflictAdjudication` 走 409→ack)+ LeftNav「工具箱」+ ⌘K `nav-toolbox`/`action-gen-*`;legacy 3 跳现页不回归。**@qa T6.4**:`tests/test_t6_toolbox_e2e.py` 5 用例真 pg + mock 网关零 token,**未发现端点 bug**。**P2 收尾**:限流→`decisions.md` 记延后(单用户原型);noopener/Committable 经查本分支早已修;`provider_deps.py` 残留 `type:ignore` 为 SDK 私有属性探活、load-bearing 保留。**最终独立复跑全仓门禁绿**:后端 ruff/format 干净、mypy **195 Success**、alembic **无漂移**(descriptor 在代码、复用既有表,零建表迁移)、pytest **583 passed**;前端 gen:api/lint/tsc/vitest **279**/build OK。守不变量 #2(只声明 tier)/#3(预览不写库、入库经验收 gate)/#9(system_prompt 进缓存前块)。**"加生成器=加一份声明" 经实证(8 个新生成器复用同一通用执行器 + 同一前端组件)。** **实景 browse 复验(工具箱卡片栅格→脑洞预览→词条入库)待做。** 后续可选 P5:封面图像轨道 / 自定义 skill 运行期 schema / 收敛已有 3 竖井。 - [2026-06-20] UX计划 B0(可控版)+F1(控件) — **✅ 本章注入透明 可控版(分支 `feat/ux-b0-controllable-injection`,提交 `0d473e7` 后端 + `6449c31` 前端,未合 develop)**。在 B0 读端点之上加作者微调,守不变量 #6「确定性选择」的作者兜底。**后端**(@backend/@db):`PUT /projects/{id}/chapters/{no}/injection` ← `InjectionOverrideRequest{pinned,excluded,recent_n(1..20→422)}` → `InjectionResponse`(新增回显 `pinned`/`excluded`,`selected[].reasons` 可含新值 **`author_pin`**);`select_relevant_entities` 加 `pinned/excluded` frozenset 入参(pin 强制纳入加 author_pin / excluded 强制剔除且**优先于 pin**);`assemble` 加 `override` 关键字参(recent_n 覆盖回看章数);**GET injection + PUT + draft 流式端点同读同一覆盖**(看到的=写章用的)。持久化:**新表 `chapter_injection`**(迁移 `ad2c4c663daf`,唯一 `(project_id,chapter_no)`;复用 outline 行污染 beats 已否决)+ 新 `domain/injection_repo.py`(`InjectionOverride`/`EntityRef`/`SqlInjectionOverrideRepo`,upsert 只 flush 端点 commit)。**前端**(@frontend):`gen:api` 纳入 PUT + 新 schema;`lib/workbench/injection.ts` override 纯函数(`currentOverride`/`withPinToggled`/`withExcluded`/`withRestored`/`withRecentN`/`isPinned`,钳 1..20);`useInjection` 加 `saving` 态 + `togglePin/exclude/restore/setRecentN`(PUT 后以服务端确定结果为准、写失败可读文案不改本地态);`ChapterAssistant` 每实体 📌置顶/✕排除 + 「近 N 章」步进器 + 「已排除」恢复区。**全仓门禁绿**:后端 ruff/format/mypy **163**/alembic 无漂移/pytest **476**;前端 lint/tsc/vitest **183**/build。记 contracts C3 再扩。**实景 browse 复验待做。** UX 计划余项:R1/R2/R3 审稿人体学 + Tier4。 - [2026-06-20] UX计划 M1+M2 — **✅ 移动端可用(纯前端,分支 `feat/ux-m1-m2-mobile` 提交 9b893a6)**。救活手机端(390px)写→审主路径。**M1 导航响应式抽屉**:抽出纯逻辑 `lib/nav/items.ts`(`GLOBAL_NAV_ITEMS`/`projectNavItems`/`isNavItemActive` + `ActiveNav` 类型,6 vitest),桌面静态侧栏与移动抽屉共用避免漂移;新增通用 `Drawer`(遮罩点击/Esc 关闭/打开聚焦面板,复用 `CommandPalette` a11y 模式,`lg:hidden`)+ `NavDrawer`(汉堡 `aria-label=打开导航` + 左抽屉)+ `NavItems`;`LeftNav` 改 `hidden lg:block`,`AppShell` 顶栏加汉堡。**M2 工具条不溢出 + 移动可达**:`Workbench` 工具条改 `flex-wrap`(390px 不再横切「尚未保存」);`ChapterList`/`ChapterAssistant` 抽出 `ChapterListContent`/`AssistantContent`,`Workbench` 经左/右 `Drawer` 在 `,"scope":"kimi-code"}`,模块常量 `KIMI_CODE_SCOPE`);scope 只放 device-auth,token 交换/刷新不带。文件:`apps/api/ww_api/services/kimi_oauth.py` + 单测 `test_kimi_oauth_service.py`(`test_start_device_authorization_parses_response_and_sends_scope` 断 `data["scope"]=="kimi-code"`)。修正 C3扩 contract + decision「省略 scope」表述 + 加 gotcha。无 OpenAPI 形变,前端无需 re-gen。门禁绿:ruff/format 干净、mypy 干净、pytest **101 passed**(apps/api)。 - [2026-06-19] @orchestrator — **🎉 K1 完成 + 独立复核(Kimi Code OAuth 订阅 plan 接入)**。波次 A(K1.1 @db 加列+迁移 ‖ K1.2 @llm KimiCodeAdapter)→B(K1.3 @backend OAuth device 服务+端点+store+接线)→C(K1.4 @frontend 连接流+可编辑路由)→D(K1.5 @qa E2E) 全绿,契约先行、零返工、无新 bug。研究先行(device flow at auth.kimi.com、client_id、伪造 UA/X-Msh-*、coding base、kimi-for-coding、access~15m/refresh~30d)+ 对照 pi-kimi-coder/ooojustin/lemon07r 校验(省略 scope 匹配 kimi-cli v1.41.0+)。最终独立复跑确认:后端 ruff/format 干净、mypy **154 文件**、alembic **无漂移**(首个新迁移=provider_credentials 加 auth_type/oauth_enc)、pytest **434 passed**;前端 vitest **155**/build。能力闭环:设置页 device-flow 登录→token Fernet 加密入库(`oauth_enc`,不泄漏)→路由 `kimi-code:kimi-for-coding`→网关构 KimiCodeAdapter(伪造头+bearer)→临过期自动刷新。**封号风险用户已知情接受**;与既有 API-key provider `kimi` 分离可切换。**唯一未尽**:对真 auth.kimi.com/api.kimi.com 的真账号联网冒烟(含封号风险,用户自担)——代码/E2E 仅验逻辑零联网。 - [2026-06-19] @qa — **K1.5 ✅ K1 E2E(Kimi Code OAuth device-flow 全 mock → 真 pg)**。新增 `tests/test_k1_kimi_oauth_e2e.py`(4 用例,零联网,真 pg):① `POST .../oauth/start`→202 `{job_id,user_code,verification_uri,interval}`,fake httpx 跑 device-auth + token(pending×2→成功) 全程;后台 `run_job` 轮询跑在**真 e2e_sm session**(override `get_session_factory`→e2e_sm + monkeypatch 模块级 `kimi_oauth._default_http_client`→同一 scripted fake + `kimi_oauth.asyncio.sleep`→no-op;端点 device-auth 走 `_default_http_client` dep 同样指 fake)→ `GET /jobs/{id}` done,result `{connected:true,provider:"kimi-code"}`;**DB 真源**:`provider_credentials` 行 `auth_type="oauth"`/`oauth_enc` 非空/`api_key_enc` null,密文不含明文 token + 解密回环 access/refresh;`GET .../oauth/status` connected 无 token。② 存 oauth + `tier_routing` writer→`kimi-code:kimi-for-coding` → `build_gateway_for_tier` → `KimiCodeAdapter`,其 `_client` 带 coding base `https://api.kimi.com/coding/v1` + UA `KimiCLI/1.5` + 全 X-Msh-* + `api_key`(=bearer)=存量 access token。③ 临近过期 oauth 包 → `_build_provider_adapter` 触发刷新(fake `project_deps.kimi_refresh` + 字符串 target monkeypatch `project_deps.httpx.AsyncClient`)→ 适配器用新 token + `oauth_enc`(DB 真源)更新为新包。④ `POST .../oauth/disconnect`→凭据行清除(DB 真源无 oauth)。**token 全程负向断言:不进 job 结果/状态响应/start 响应/密文明文**。全仓门禁绿:ruff/format 干净、mypy **154 Success**、alembic 无漂移、pytest **434 passed**(430→+4)。memory 记 1 gotcha。**K1(Kimi Code OAuth 订阅 plan 接入)全 5 子任务 ✅,可封板。⚠️ 仍未对真 auth.kimi.com/api.kimi.com 联网验证(含封号风险,用户自担)。** - [2026-06-19] @frontend — **K1.4 ✅ 设置页 Kimi Code OAuth 连接流 + 可编辑档位路由**。`pnpm gen:api` 纳入 K1.3 的 3 端点(OAuthStart/Disconnect/StatusResponse),`lib/api/types.ts` 加 3 别名。纯逻辑(node-env vitest):`lib/settings/kimiOauth.ts`(`connectPhase` 状态机 idle/awaiting/connected/error + `toDeviceDisplay`/`authOpenUrl`/`toConnectionStatus`/`jobConnected`/`formatExpiresAt`,16 测)+ `lib/settings/providers.ts` 扩(`KNOWN_PROVIDERS` 加 `kimi-code`(auth:oauth, model:kimi-for-coding) + `API_KEY_PROVIDERS`/`TIERS`/`defaultModelFor` + 路由草稿纯函数 `toRoutingDrafts`/`applyProviderChange`/`draftsToRoutingInput`,9 测)。组件:`useKimiOauth` hook(**复用 `useJobPoll`**:connect=POST start→展示 user_code+`window.open` 授权页+轮询 job→done 拉 status;disconnect=POST→复位)+ `KimiCodeOauth.tsx`(连接区,user_code 用 `` 可读可选可聚焦 + motion-safe)。`ProvidersSettings.tsx` 改:档位路由从只读→**可编辑**(per-tier select provider + model input + 保存 PUT),API-key 凭据行只列 `API_KEY_PROVIDERS`(OAuth 提供商有独立连接区,无 key 输入),挂入 `KimiCodeOauth`。`server.ts` 加 `fetchKimiOauthStatus`,`page.tsx` `Promise.all` 拉 providers+oauth 状态传 `kimiOauth` prop(进页即显连接态)。**job 结果无 token,只读 connected**。前端门禁全绿:gen:api / lint / typecheck / vitest **155 passed**(130→+25)/ build。memory 记 1 gotcha。**⚠️ 运行时对真 auth.kimi.com 设备授权 + 真 api.kimi.com 调用验证留 K1.5/手动(含封号风险,用户自担)。** - [2026-06-19] @backend — **K1.3 ✅ Kimi Code OAuth device-flow 服务 + 端点 + store + 网关接线(C3 扩稳定)**。新增 `apps/api/ww_api/services/kimi_oauth.py`(`start_device_authorization`/`poll_token`/`refresh`,注入 `AsyncHttpClient` Protocol 零联网;`TokenSet`+`encrypt_oauth_bundle`/`decrypt_oauth_bundle` 复用 Fernet;`needs_refresh` 300s 缓冲;client_id env `KIMI_CLIENT_ID` 可覆盖、device authorization 省略 scope)+ `routers/kimi_oauth.py`(`POST .../oauth/start`→202 `{job_id,user_code,verification_uri,...}`+ BackgroundTask `run_job` 后台循环轮询→成功存加密 oauth_enc+job done(结果只 `{connected,provider}`);`POST .../oauth/disconnect`→`{disconnected}`;`GET .../oauth/status`→`{connected,expires_at}` 无 token)+ `schemas/kimi_oauth.py`(3 schema)+ 注册 main.py。`services/credentials.py`:`StoredCredential` 加 `auth_type`/`oauth_enc`、`api_key_enc`→`bytes|None`(**K1.1 的 2 处 mypy red 消除**),`SqlCredentialStore` 读新列 + `upsert_oauth_credential`/`delete_credential`,`upsert_credential` 显式置 api_key 态。`services/project_deps.py`:`_build_provider_adapter` 对 `kimi-code`/`auth_type=oauth` 走 `_resolve_kimi_code_token`(解密→临近过期则经 httpx 刷新+持久化→access token 喂 `build_adapter("kimi-code",…)`);`provider_deps.py` `_PROVIDER_BASE_URLS` 加 `kimi-code`。**token 全程不进 job 结果/日志/响应**(仅 Fernet 密文存 oauth_enc)。TDD 先红后绿:`tests/test_kimi_oauth_service.py`(11) + `test_kimi_oauth_endpoints.py`(5) + `test_kimi_code_wiring.py`(3)(fake httpx 零联网)。**全仓门禁绿**:ruff/format 干净、mypy **154 源文件 Success**、alembic **无漂移**、pytest **430 passed**(400→+30)。OpenAPI 新增 3 端点 + 3 schema → **@frontend K1.4 须 `pnpm gen:api`**。memory 记 C3 扩(K1.3)+ 1 decision + 2 gotcha。**⚠️ 真 auth.kimi.com/api.kimi.com 联网验证(含封号风险)留 K1.5,用户自担。** - [2026-06-19] @llm — **K1.2 ✅ Kimi Code 订阅 plan 适配器(C1 扩稳定)**。`packages/llm_gateway/ww_llm_gateway/adapters/kimi_code.py`:`KimiCodeAdapter` **子类化 `OpenAICompatAdapter`**(coding 端点 OpenAI 兼容,复用 complete/stream/结构化/瞬时错误包装/capabilities),provider 固定 `kimi-code`(与 API-key `kimi` 分离);`build_kimi_code_client(access_token, base_url?)` 构 `AsyncOpenAI(api_key=access_token, base_url="https://api.kimi.com/coding/v1", default_headers=kimi_code_headers())`——access_token 当 bearer,伪造头随客户端发。`kimi_code_headers()`:`User-Agent` + 全 `X-Msh-*`(Platform/Version/Device-Name/Device-Model/Os-Version/Device-Id)。`kimi_device_id()`:env `KIMI_DEVICE_ID` 优先,否则确定性 `uuid5` 缺省(**绝不每次随机**,稳定且来源可测/可配)。model `kimi-for-coding` **不硬编码**(经 `.complete(req, model)` 传入,路由关注点)。`factory.build_adapter` 加 `provider=="kimi-code"` 分支;包 `__init__` 导出全部符号。**契约校正(已对照 `github.com/picassio/pi-kimi-coder` `extensions/index.ts`)**:参考实现对 coding API **仅**设 `User-Agent: "KimiCLI/1.5"`(精确值,已采用)、**未**设 X-Msh-* 头——与 PROGRESS 契约「缺 X-Msh→403」不符;本实现以验证到的 UA 为准 + 附带 X-Msh-* 客户端标识头(真客户端会发、额外标识无害、最大化避免 403);**真 api.kimi.com 联网验证头集是否足够留 K1.3/K1.5(含封号风险,用户自担)**。TDD 先红后绿;`tests/test_kimi_code_adapter.py`(8) + `tests/test_kimi_code_factory.py`(3),不联网(断 base_url + default_headers + 稳定 device_id + 工厂分派)。门禁绿(隔离 `MYPY_CACHE_DIR=.mypy_cache_k12` 已清理):ruff/format 干净、mypy **27 源文件 Success**、pytest **58 passed**(47→+11)。**@backend 行动(K1.3)**:`_build_provider_adapter` 对 provider `kimi-code` 调 `build_adapter("kimi-code", api_key=<刷新后的 access_token>, base_url=…)`;token 刷新/获取归 K1.3。**无 OpenAPI 形变**,前端无需 re-gen。memory 记 C1 扩(K1.2)契约 + 1 gotcha。 - [2026-06-19] @db — **K1.1 ✅ `provider_credentials` 加 OAuth 列(C2 扩稳定)**。`packages/db/ww_db/models.py` `ProviderCredential`:① 加 `auth_type: Mapped[str]`(`Text, nullable=False, server_default="'api_key'"`,值 api_key|oauth);② 加 `oauth_enc: Mapped[bytes|None]`(`LargeBinary, nullable=True`,持 Fernet 加密 JSON `{access_token,refresh_token,expires_at}`);③ `api_key_enc` 改 `nullable=True`(`Mapped[bytes|None]`,OAuth-only 行无 api_key、既有 api_key 行保值);`UniqueConstraint(owner_id,project_id,provider)` 不变。**首个新迁移**(M1–M5 全零建表)`migrations/versions/1f011c42bd4d_provider_credentials_oauth_columns.py`(down_revision=`220ca2e3d53f`):up=add_column auth_type/oauth_enc + alter api_key_enc→nullable,down 逆向;autogenerate 仅这 3 op 无 noise,手修模板重复 import + 改本仓 ruff 样式(见 gotcha)。`upgrade head` 已应用、`alembic check` **无漂移**。门禁:ruff/format 干净、pytest **400 passed**、alembic 无漂移;**mypy 余 2 处 `arg-type` 错(均在 @backend `apps/api/ww_api/services/credentials.py`,因 api_key_enc 改 nullable 打穿,K1.3 吸收,见下方 @backend 请求)**。memory 记 C2 扩 + 2 gotcha。 - **[请求 → @backend,K1.3]**:K1.1 把 `provider_credentials.api_key_enc` 改 nullable(`bytes|None`),打穿到你拥有的 `apps/api/ww_api/services/credentials.py` mypy 报 2 处 `arg-type`(`StoredCredential.api_key_enc: bytes` ↳ 现 `bytes|None`,dataclass line 28 + store line 75/97)。@db 不越权改 apps/api。K1.3 接 OAuth store 时把 `StoredCredential.api_key_enc` 改 `bytes | None`(并加 `auth_type`/`oauth_enc` 字段 + OAuth 读写路径),mypy 即全绿。oauth_enc 持 Fernet 加密 JSON `{access_token,refresh_token,expires_at}`(key=`settings.credential_enc_key`,同 api_key_enc)。 - [2026-06-19] @orchestrator — **🎉 M1–M5 全部交付 + M5 余项收尾完成**。五个里程碑(Phase 0–5)全部 ✅;M5 三项非阻塞余项 + 本次台账归档共四项 follow-up 已清:**#1** Codex 读端点(`GET /projects/:id/characters` + `GET .../world_entities`,@backend)+ 前端接线(CodexPage 拉全量真源,去「会话内回显」局限,@frontend);**#2** `build_adapter` provider→适配器工厂 + anthropic mypy 修复(@llm),`build_gateway_for_tier` 按 provider 选适配器启用 Anthropic/Gemini 真实回退(@backend);**#3** `packages/skills` 纳入 uv workspace + skill impl(registry/permissions)从 `ww_core.domain` 迁入 `ww_skills`(@backend,@devops 加 SDK);**#4** 本次把 M5/M4/M3 三段折叠进「已归档阶段」、顶部改短状态行(@orchestrator)。最终门禁全绿:后端 ruff/format 干净、mypy **144 文件**、alembic **无漂移**、pytest **400 passed**;前端 lint/typecheck 干净、vitest **130 passed**、build OK。台账已归档,当前无活跃阶段。 - [2026-06-19] @frontend — **M5 follow-up #1 ✅ Codex 展示真源(解余项①)**(消费 @backend C3 扩 Codex 读端点,无契约变更)。先 `pnpm gen:api` 纳入 `GET /projects/:id/characters`(`CharacterListResponse{characters?}`)+ `GET .../world_entities`(`WorldEntityListResponse{world_entities?}`)+ `lib/api/types` 加 2 别名。`lib/api/server` 加 `fetchCharacters`/`fetchWorldEntities`(200 空列表非 404,仿 `fetchForeshadow`);codex route(Server Component)`Promise.all` 拉 项目+人物+世界观全量 → 传 `CodexPage` 初始数据。`CodexPage` 人物 tab 渲染**跨会话持久化真源**(刷新不再空),本会话新入库卡经纯函数 `mergeCharacterCards`(按 name 去重、持久化优先、不重复)合并补显(generate→ingest 后仍即时见新行);世界观 tab 渲染入库真源(本期无世界观入库端点,故只真源 + 生成器预览,无 session 合并);时间线维持 P2 占位。纯逻辑 `lib/generation/cards.{mergeCharacterCards,mergeWorldEntities}` + 5 node-env 单测。前端门禁全绿:gen:api/lint/tsc/**vitest 130**(125→+5)/build。**Codex「会话内回显」局限已去除**(解 gotcha + PROGRESS 余项①)。memory 把 Codex 缺口 gotcha 标 RESOLVED。 - [2026-06-19] @backend — **M5 follow-up R1 + R3 ✅**(三 fix,与并行 @llm `llm_gateway` 改动 disjoint)。**R1-Codex 读端点**(解余项①):加 `GET /projects/:id/characters`(`CharacterListResponse`)+ `GET /projects/:id/world_entities`(`WorldEntityListResponse`),复用 C5 读侧 `SqlCharacterRepo`/`SqlWorldEntityRepo`(经 `get_memory_repos`,不动 assemble)+ 反向 JSONB 解包(`{"items":[...]}`→list/`{"text":...}`→str/`{"rules":[...]}`→list),register main;C3 扩稳定。**R3-skill 迁包**(解余项③):`skill_registry.py`+`skill_permissions.py` 从 `ww_core.domain` **迁入** `packages/skills/ww_skills/`(`ww_skills/__init__` 改直接导出,删门面);importer(`generation.py`/`project_deps.py`/`test_generation.py`)改 `from ww_skills import …`;skill 单测移 `packages/skills/tests/`(13 测);删 ww_core.domain 两模块+__init__ 导出;`packages/skills/pyproject.toml` deps 校准(去 `ww-core`、补 `ww-llm-gateway`+`sqlalchemy`),`uv sync` 重建。**R1#2-build_adapter 接线**(解余项②的 apps 侧;@llm 的 `build_adapter` 工厂 + @devops 的 anthropic/google-genai SDK 均已落地,import 实解非 transient):`build_gateway_for_tier` 的 `_build_provider_adapter`(原 `_build_openai_compat_adapter`)改调 `ww_llm_gateway.build_adapter(provider, api_key=…, base_url=…)`——Anthropic/Gemini 传 base_url=None 走原生适配器,去掉直接 `AsyncOpenAI`/`OpenAICompatAdapter` import。门禁绿(隔离 `MYPY_CACHE_DIR=.mypy_cache_r1backend` 已清理):ruff/format 净、mypy **101 文件 0 error**、pytest `packages/core packages/skills apps/api` **246 passed**(226→+20:+10 迁移 skill 测 +3 Codex 读端点测 + 其余)。两新端点入 OpenAPI(验证 get 存在)。memory 登记 C3 扩(Codex 读端点)+ C8 实现位置更新 + build_adapter 接线 + 3 decisions。**@frontend 行动**:`cd apps/web && pnpm gen:api` 纳入 `GET characters`/`GET world_entities`,CodexPage 初始列表拉全量去掉「会话内」局限(解 gotcha Codex 缺口)。**@devops 行动**:复核根 `uv.lock`(@backend 改了 `packages/skills/pyproject.toml` deps,已 `uv sync` 重建 ww-skills)。**M5 三非阻塞余项①②③ 至此全部清除。** - [2026-06-19] @llm — **R2 follow-up #2 ✅ 真多 provider 适配器选择**(C1 扩 follow-up #2 稳定;接 @devops 已加 `anthropic`+`google-genai` SDK 之后)。① **修真 SDK 装上后的 mypy 报错**:`instructor.from_anthropic(self._client)` 重载只收 SDK 具体 `AsyncAnthropic|...`、`AnthropicClient` Protocol 不匹配 → adapter 内 `cast("AsyncAnthropic", self._client)`(`TYPE_CHECKING` import,零运行时硬依赖)选中 AsyncInstructor 重载。gemini 走 `response_schema` 非 instructor,**无**该类错。② **新增 provider→适配器工厂** `factory.build_adapter(provider, *, api_key, base_url=None) -> ProviderAdapter`(包 `__init__` 导出):`anthropic`→`AnthropicAdapter`、`{gemini,google}`→`GeminiAdapter`、其余→`OpenAICompatAdapter(provider, AsyncOpenAI(...))`;从 api_key 构真实客户端(factory 处 `cast` 跨 Protocol read-only-attr 约束,adapter 仍声明 Protocol 形参、测试替身缝完好)。新增 `tests/test_build_adapter_factory.py` 6 测(按 provider 名断分派,不联网)。**门禁绿**(隔离 cache):ruff/format 净、mypy **24 源文件 0 错**、pytest **47 passed**(41→+6);全仓 `mypy packages apps` **144 文件 Success**(anthropic.py 报错确认消除、gemini 无同类错)。无 OpenAPI 形变(served_by 不出 API),前端无需 re-gen。**@backend 可在 `build_gateway_for_tier` 用 `build_adapter` 替换「一律 OpenAICompatAdapter」启用 anthropic/gemini 真实回退。** 记 1 gotcha(Protocol vs 真 SDK cast 边界)。 - [2026-06-19] @orchestrator — **🎉 M5 完成 + 独立复核 ✅**(Wave C:T5.3+T5.6 ✅ + Wave D:T5.7 ✅)。波次 A(T5.1‖T5.4‖T5.5)→B(T5.2)→C(T5.3+T5.6)→D(T5.7) 全绿,契约先行、零返工、无新 bug。最终独立复跑确认:后端 ruff/format 干净、mypy **142 文件**、**alembic 无漂移**(M5 复用 T0.2 既有 world_entities/characters/rules/skills 表,全程零建表迁移)、pytest **391 passed**;前端 tsc 干净、**vitest 125 passed**。**M5(生成+多provider+扩展)出口 DoD 全达成**:worldbuilder/character-gen 生成→预览→入库(continuity 409 gate + partition_writes 白名单 + schema→JSONB 形变);网关多 provider 回退/熔断/能力降级(真 Gateway 回退链,E2E 验主失败→fallback 服务 + 记账记实际 provider);Skill registry + 表权限沙箱 + 规则页/技能库/命令面板(⌘K)。**M1–M5 全部交付。** 非阻塞余项:① Codex 读端点缺口(无 GET characters/world_entities,现以生成+会话内回显管理);② Anthropic/Gemini 真实回退待 @devops 加 SDK + 按 provider 选适配器类(E2E 用 OpenAI 兼容 mock 验证回退);③ `packages/skills` 纳入 uv workspace(现 impl 落 ww_core.domain + façade)。建议下一步:清理 PROGRESS 把 M3/M4/M5 折叠归档,或处理上述余项。 - [2026-06-19] @qa — **M5 Wave D:T5.7✅ M5 E2E + 切 provider**(真 pg + mock 多 provider,零 token;无契约变更)。`tests/test_m5_e2e.py` 3 用例:**① 群像生成→入库闭环**(mock 网关按 `req.output_schema` 路由 CharacterGenResult/WorldGenResult/ContinuityReview):`POST /world/generate` 预览 + `POST /characters/generate` 预览 3 卡 → `POST /characters` 入库**双 gate**——precheck 有冲突且未 `acknowledge_conflicts` → **409 CONFLICT_UNRESOLVED**+`details.conflicts`(并断言此刻 characters 表无行,守不变量 #3)→ 带 `acknowledge_conflicts=true` 重发 201;**DB 真源**断言 characters 行真落 pg + **schema→JSONB 形变**(traits/speech_tics→`{"items":[...]}`、arc→`{"text":...}`、tags/relations 直落 list),`rejected_tables` 空(partition_writes 合法路径),4 条 ledger。**② 切 provider 回退**:HTTP 角色生成跑在**真 `Gateway`**(非 stub)deepseek(每次 `complete` 抛 `TransientProviderError` 耗尽重试)→openai 回退链上,端点仍 200;`served_by` 不出 API,故经 **DB 真源 `usage_ledger`** 断言实际服务方 = **openai(fallback)≠deepseek(primary)**(ARCH §4.5 记账记实际服务方)。**③ 能力降级**:`structured_output=False` 唯一 provider 直测 `Gateway.run` → `served_by.degraded=True`(§4.4)。坑:生成网关是**请求 scope 依赖**(`get_worldbuilder_gateway`/`get_character_gen_gateway`/`get_precheck_gateway`),直接 `app.dependency_overrides` 注假网关即可,**无需** monkeypatch `build_gateway_for_tier`(与 M4 学文风后台自建网关不同)。全仓门禁绿:ruff/format 净、mypy **142 文件**、**alembic 无漂移**、pytest **391 passed**(388→+3)。**未发现 REAL bug**。**M5 出口 DoD 全达成** → orchestrator 可封 M5。 - [2026-06-19] @frontend — **M5 Wave C:T5.3+T5.6✅ 生成 + 设定库 + 规则 + 技能 + 命令面板**(消费 C3 扩 T5.2 + C8/T5.5,无契约变更)。先 `pnpm gen:api` 纳入 5 端点(world/characters generate + characters ingest + GET rules + GET skills)+ `lib/api/types` 加 12 别名。**T5.3**:① **角色生成器**(UX §6.6)`components/generation/CharacterGenerator`:一句话 brief + 数量(1–12) + 定位 → `POST /characters/generate` 预览卡 → `CharacterCardItem`(勾选入库 + 就地编辑名/弧光,默认全选)→ `POST /characters` 入库;**409 CONFLICT_UNRESOLVED 裁决流**:`useCharacterGen` 捕 409 → `extractIngestConflicts` 收窄 `details.conflicts`(复用 ReviewConflict 形) → `ConflictAdjudication` 面板展示 continuity 冲突 → 作者「已知悉强制入库」= 带 `acknowledge_conflicts=true` 重发 / 「取消改卡」。② **世界观设计器** `WorldGenerator`:`POST /world/generate` 预览 type/name/rules(本期无入库端点,仅预览参考)。③ **设定库 Codex**(UX §6.5)`codex/CodexPage` + `app/projects/[id]/codex`:人物/世界观/时间线三 tab(时间线 P2 占位);`?gen=character|world` 命令面板跳转直开对应 tab。**T5.6**:④ **规则页** `rules/RulesPage` + `app/.../rules`:四级规则列表(`groupByLevel`) + 新增(`useRules` 乐观追加+回滚,仿 useForeshadow)。⑤ **技能库** `skills/SkillsPage`(Server Component 纯读)+ `app/.../skills`:`GET /skills` 按 scope 分组只读注册表(name/tier/reads/writes)。⑥ **命令面板 ⌘K** `command/{CommandPalette,CommandPaletteMount}` + `lib/command/palette`:⌘K/Ctrl+K 开关、模糊过滤(`filterCommands`)、上下循环高亮(`moveHighlight`)、Esc/Enter,a11y(role=dialog/listbox/option + 自动聚焦 + prefers-reduced-motion);挂 RootLayout 对全局生效,读 pathname 注入项目上下文。⑦ LeftNav `ActiveNav` 加 codex/rules/skills(替换原 disabled 设定库占位)。纯逻辑全单测(node-env):`lib/{generation/cards,rules/rules,skills/skills,command/palette}.test`。前端门禁全绿:gen:api/lint/tsc/**vitest 125**(96→+29:cards 12 + rules 5 + skills 3 + palette 9)/build(codex/rules/skills 路由齐)。memory 记 2 gotcha + 1 gap(Codex 读端点缺口)。**M5 前端交付完成** → 下一步 **Wave D:T5.7(@qa) M5 E2E + 切 provider**。 - [2026-06-19] @orchestrator — **M5 Wave B 收口 ✅**(T5.2,C3 扩稳定)。5 端点入 OpenAPI:world/characters generate 预览 + characters 入库(双 gate:continuity 预检 409 + partition_writes 白名单 + schema→DB 形变)+ GET rules/skills;`build_gateway_for_tier` 接线回退链(OpenAI 兼容齐,Anthropic/Gemini 真实切换余量 deferred 给 T5.7 + @devops SDK)。全仓门禁绿:ruff/format 干净、mypy **142 文件**、**alembic 无漂移**、pytest **388 passed**(371→+17)。下一步 **Wave C:T5.3+T5.6(@frontend 单 agent 顺序做)**——先 `pnpm gen:api`,做 角色生成器 + 世界观设计器 + 设定库 Codex + 规则页 + 技能库 + 命令面板(⌘K),入库流处理 409 裁决重发。 - [2026-06-19] @backend — **M5 Wave B:T5.2✅ 生成/入库 API**(契约 **C3 扩稳定**;消费 Wave A 的 C6扩/C8/C1扩,与已合并 Wave A 无写冲突)。`routers/generation.py`(+ `skills_router`,已注册 main)落 5 端点:① `POST /world/generate`(worldbuilder writer 网关 `run_worldbuilder` → **预览**世界观,不入库,末尾 commit 落 ledger);② `POST /characters/generate`(character-gen writer 网关,注入已有角色防雷同 → **预览**角色卡);③ `POST /characters`(入库 **双 gate**:`precheck_generated_cards` continuity 预检 analyst 网关——有冲突且未 `acknowledge_conflicts` → **409 CONFLICT_UNRESOLVED**+冲突详情,作者裁决后放行;`partition_writes(character_gen_spec,...)` 白名单过滤越权写表;过 gate 写 `characters` 行,**schema list/str → DB JSONB dict 形变**);④ `GET /rules`(规则列表,复用 C5 读侧);⑤ `GET /skills`(技能库,经 registry)。新写侧 repo `ww_core.domain.{character_repo(traits/speech_tics→{"items"}/arc→{"text"}),world_entity_repo(rules→{"rules"})}`(只 flush,端点 commit)。**T5.4 follow-up 接线完成**:`build_gateway_for_tier` 据 DB `tier_routing.primary+fallback` 预备多 `OpenAICompatAdapter` + 注入 `chain_resolver=chain_from_routing(...)` 启用回退链;无路由行退回 `resolve_route`(单 provider,M1 兼容),无可用凭据→503。**仅 OpenAI 兼容 provider**(deepseek/kimi/qwen/glm/openai)——Anthropic/Gemini 真实回退需 @devops 加 SDK + provider_deps 据 provider 选适配器类(**余量 deferred 给 T5.7 切 provider**)。全仓门禁绿:ruff/format 净、mypy **142 文件**、**alembic 无漂移**(M5 复用 T0.2 既有表)、pytest **388 passed**(371→+17:13 生成/入库端点 + 4 网关回退接线 + 4 形变 helper)。新端点入 OpenAPI(验证 world/characters generate + POST characters + GET rules + GET skills 齐)。memory 登记 C3扩稳定 + 2 decisions(入库 gate / 形变)+ 2 gotchas。**@frontend(T5.3/T5.6) 行动**:先 `cd apps/web && pnpm gen:api` 纳入这 5 端点;入库流处理 409(展示冲突→裁决勾选→带 `acknowledge_conflicts` 重发)。下一步 **Wave C:T5.3+T5.6(@frontend)**(依赖 T5.2✅+T5.5✅,可起)。 - [2026-06-19] @orchestrator — **M5 Wave A 收口 ✅**(T5.1 + T5.4 + T5.5 三 agent 并行,disjoint 目录 + 隔离 mypy cache,零写冲突零返工)。worldbuilder/character-gen 双 spec + 节点 + 入库前 continuity 校验缝(C6扩)‖ 网关多 provider 韧性:Anthropic/Gemini 适配器 + 回退链 + 熔断 + 能力降级(C1扩,无 OpenAPI 形变)‖ Skill registry + 表权限白名单(filter_reads/partition_writes)+ `POST /rules`(C8 + C3扩)。全仓门禁绿:ruff/format 干净、mypy **135 文件**、**alembic 无漂移**(M5 复用 T0.2 已建的 world_entities/characters/rules/skills 表,无建表迁移)、pytest **371 passed**(296→+75)。**Wave B 衔接待办**(T5.2 @backend 须处理):① 角色 schema `traits`/`speech_tics`(list)/`arc`(str) ↔ DB JSONB **dict** 形变,入库须转换;② 入库 gate 调 `precheck_generated_cards`(continuity 冲突拦截) + `partition_writes`(越权写丢弃审计);③ 补 `GET /rules` + `GET /skills` 供 T5.6 前端;④ 接线 `build_gateway_for_tier`→`chain_from_routing` 启用回退链(T5.7 切 provider 依赖)。**@devops 待办**:加 `anthropic`/`google-genai` SDK 依赖(适配器懒 import,单测不需);评估把 `packages/skills` 纳入 uv workspace(现 impl 落 ww_core.domain + ww_skills façade)。下一步 **Wave B:T5.2(@backend 生成/入库 API)**。 - [2026-06-19] @llm — **M5 Wave A:T5.4✅ 网关多 provider 韧性**(契约 **C1 扩稳定**,仅扩 `packages/llm_gateway/`,与并行 T5.1 @llm 无文件冲突)。① **新适配器**`adapters/anthropic.py`(`AnthropicAdapter`:原生结构化经 instructor.from_anthropic 懒构建、`cache_control` 缓存断点、thinking 能力)+`adapters/gemini.py`(`GeminiAdapter`:`response_mime_type=json+response_schema` 结构化、thinking)——**均经注入的客户端 Protocol,不硬 import anthropic/google-genai SDK,单测零联网零依赖**。② **回退链 + 重试 + 熔断**`gateway.py`:`Gateway` 加 `chain_resolver`/`max_retries`/`breaker`(保留 `resolver=` M1 兼容→自动包成单元素链);沿链逐 provider→瞬时失败(tenacity 指数退避)重试 R 次→仍失败/熔断打开/无适配器→切下一个;首个成功者服务、非链首标 `served_by.fell_back`;链耗尽抛 `LLM_UNAVAILABLE`;记账记**实际服务方**(回退后不记主模型);流式仅首块前可切(产出后中途失败上抛不重连)。`CircuitBreaker(threshold/reset_seconds/clock)` 连续失败超阈值短时熔断、成功清零、冷却半开。③ **能力协商/降级**:结构化请求按 `capabilities().structured_output` 重排链优先选支持者;链上无支持者→降级首个可用(适配器自走 instructor JSON 提示)标 `served_by.degraded`。④ **瞬时错误契约**`errors.py`:`TransientProviderError`(三适配器按异常类名+status_code 包装)+网关视 `AppError(RATE_LIMITED)` 可重试。⑤ **routing.py**:`resolve_chain`/`chain_from_routing`(据 DB tier_routing 去重保序)/`ChainResolver` 别名导出。⑥ `ServedBy` 加 `degraded`(向后兼容,默认 False);`pyproject.toml` 加 `tenacity>=8.2`。**23 新测**(test_fallback_chain 10/test_capability_degradation 4/test_new_adapters 7/test_multi_provider_accounting 2),包内 **41 passed**;全仓门禁绿:ruff/format 净、mypy **135 文件**、core+api **226 passed**(无回归)。**无 OpenAPI 形变**(served_by 不出 API,前端无需 re-gen)。memory 登记 C1 扩 + 2 gotcha。**后续待办**:apps/api `build_gateway_for_tier` 接线回退链(预备多适配器+传 chain_resolver=chain_from_routing 包装)、@devops 加 anthropic/google-genai SDK 依赖供真实接线。下一步 Wave B(T5.2 @backend,依赖 T5.1)。 - [2026-06-19] @backend — **M5 Wave A:T5.5✅ Skill 运行时 + 规则**(契约 **C8 + C3 扩稳定**;与并行 T5.1/T5.4 @llm 无目录/写冲突,隔离 `MYPY_CACHE_DIR=.mypy_cache_t55`)。① **Skill registry loader**(`packages/core/ww_core/domain/skill_registry.py`):`SkillRecord`(frozen,skills 行声明快照) + `SkillRepo` Protocol + `SqlSkillRepo`(读 `skills` 表,裸 tier 非法→VALIDATION) + `SkillRegistry.load(repo)`(async,每条跑 `validate_declaration`,越权声明→AppError VALIDATION 加载中断;frozen `dict[name,AgentSpec]`)+ `get`/`names`/`list_scope`。复用 §5.1 网关机制(不重实现网关),不变量#2 只带 tier。② **表权限沙箱**(`skill_permissions.py`,纯函数/不可变,§5.6「声明式权限+apply 层白名单」非进程沙箱):`KNOWN_TABLES`(10 创作表白名单) + `filter_reads`(注入只喂声明 reads 表) + `partition_writes`(写库只应用声明 writes,越权→rejected 供审计 log+丢弃) + `validate_declaration`(reads/writes 越界→VALIDATION)。守不变量#3(allowed 仍过验收 gate,不开写后门)。③ **`POST /projects/:id/rules`**(`routers/rules.py`,已注册 main):`RuleCreateRequest{level:Literal[global/genre/style/project],content(min1)}`→**201** `RuleView{level,content}`,非法 level/空 content→FastAPI 422;写侧 `domain/rule_repo.py`(`RuleWriteRepo`/`SqlRuleWriteRepo`/`RuleWriteView`,只 flush,端点 commit);喂 assemble 四级 `merge_rules`。④ 注入缝 `get_rule_write_repo`/`get_skill_registry`(`project_deps`)。门禁绿(限本任务范围):ruff/format 净、`MYPY_CACHE_DIR=.mypy_cache_t55 mypy packages/skills packages/core apps/api` **0 error/94 文件**、pytest `packages/skills packages/core apps/api` **226 passed**(+18 新测:6 permissions + 4 registry + 2 rule_repo + 3 rules 端点 + 既有)。`POST /rules` 已入 OpenAPI(验证)。**实现位置决策**:registry/沙箱落 `ww_core.domain`(§5.6 强制点在编排/写库层 + `packages/skills` 当前非 uv workspace member,根 pyproject @devops 未接线);`ww_skills` 留再导出门面,待 @devops 纳入 workspace 可迁移(见 decisions/gotchas)。**@frontend(T5.6) 行动**:先 `cd apps/web && pnpm gen:api` 纳入 `POST /rules`(规则页);技能库经 `get_skill_registry`(若 T5.6 需 GET /skills 端点,请在此提请 @backend 补)。**@llm/T5.2 行动**:生成入库层用 `partition_writes` 守自定义 skill 产出白名单(仅声明 writes 表),仍过验收。 - [2026-06-19] @llm — **M5 Wave A 半程:T5.1✅**(C6 扩稳定,与并行 T5.4 @llm/T5.5 @backend 无目录/写冲突,隔离 MYPY_CACHE_DIR)。worldbuilder + character-gen 落地:① schema(`packages/agents/ww_agents/schemas.py`)`WorldEntityCard{type,name,rules:list[str]}`/`WorldGenResult{entities}` + `CharacterRelation{name,kind,note?}`/`CharacterCard{name,role,traits,backstory,arc,speech_tics,tags,relations}`/`CharacterGenResult{cards}`——**字段对齐 `world_entities`/`characters` DB 列**供 T5.2 ingest 直映。② spec(specs.py)`worldbuilder_spec`(writer,reads=projects,writes=world_entities,硬规则显式 prompt)、`character_gen_spec`(writer,reads=world_entities+characters,writes=characters,**system_prompt 注入「已生成卡+已有角色」差异化要求=M5-a 防雷同**)。③ 生成节点(`packages/core/ww_core/orchestrator/generation_node.py`,模块内自有 GatewayRun Protocol,不重复导出)`run_worldbuilder`/`run_character_gen`(`build_character_gen_context` 把已有+已生成卡序列化成 `name(role):traits` 简表注入,空时占位不崩,独立生成、网关失败上抛、parsed 违约 ValueError,只读不写库)。④ **入库前 continuity 校验缝**(ARCH §6.5)`precheck_generated_cards(spec=continuity_spec,*,cards,world_context,characters_context,...)->list[Conflict]`——编排器追加的检查、**非 character-gen 直接互调**(守 §5.4/不变量#1),T5.2 入库端点调它做 gate。门禁绿(限本任务文件):ruff/format 净、mypy 本任务 7 文件 0 error(`packages/agents packages/core` 余 3 error 全在并行 @backend T5.5 的 rule_repo/skill 测试)、pytest **246 passed**(+34 新测:22 spec 契约 + 12 节点/防雷同/校验缝)。memory 登记 **C6 扩稳定**。**T5.2(@backend) 行动**:`POST /world/generate`→run_worldbuilder、`/characters/generate`→run_character_gen(批量循环把已产卡传 generated_so_far)、`POST /characters` 入库前调 precheck_generated_cards gate;analyst/writer 网关复用 build_gateway_for_tier。下一步 **Wave B:T5.2**(依赖本任务✅,可起)。 - [2026-06-19] @docs — spec 一致性回写:把 T4.3 顺带新增的 `GET /projects/:id/style`(最新指纹 `{dimensions, evidence, version}`,无指纹 404)补进 ARCHITECTURE.md §7.2 端点表(紧邻 `POST /style` 行)+ PRODUCT_SPEC.md §6 端点清单(紧邻 `POST /style` 行),消除 code↔spec 漂移。`memory/decisions.md` 该决策的「待回写规格」flag 至此**已解决**。仅改两份 spec,未动代码/测试。 - [2026-06-19] @orchestrator — **M4 收官独立复核 ✅**。波次按计划走通 A(T4.2‖T4.1)→B(T4.3)→C(T4.4)→D(T4.5),每波契约先行 + 全仓门禁收口、零返工、无新 bug。最终独立复跑后端门禁确认绿:ruff/format 干净、mypy **115 文件**、**alembic 无漂移**(M4 全程零建表迁移,全靠 T0.2 预留的 `style_fingerprint`/`chapter_reviews.style`/`jobs` 列表)、pytest **296 passed**;前端 gen:api/lint/tsc/vitest **96**/build 绿。**M4(文风)出口 DoD 全达成**:学文风(16 维指纹带证据,版本化,异步 jobs+zombie reaper) + 漂移打分作为第四审真持久化(review 四并行分支+SSE `style`+`chapter_reviews.style`) + 回炉同步 diff(不写库守不变量#3)。Phase 4 完成;M1–M4 全部交付。下一阶段 M5(生成+多 provider+Skill) 见 DEV_PLAN,进入时归档 M3/M4 并拉入新表。 - [2026-06-19] @qa — **🎉 M4 完成**(Wave D:T4.5✅ M4 E2E)。`tests/test_m4_e2e.py` 真 pg + 多档位 mock 网关(零 token)真跑通过 3 用例:① **学文风闭环**:`POST /style`→**202** `{job_id}`→BackgroundTask `run_job` 自建独立 session 跑 `run_style_extraction`(analyst 假适配器产 StyleFingerprintResult)→`SqlStyleFingerprintWriteRepo.append` 真落 pg→`GET /jobs/{id}` status=done(result.version/dims_count)→`GET /style` 最新指纹→`mode="update"` 学一次 version+1;DB 真源 `style_fingerprint` 2 行(version 1/2,dimensions/evidence)+ `jobs` 2 行 done。② **写章第四审**:学文风后 assemble stable_core 含指纹→`POST .../review` SSE 四审齐(continuity/foreshadow/pace/style)无 error + `section{name:"style"}` + `style{score:72,segments:[{idx:2,score:40,label:"机翻腔"}]}` 帧真发 + `GET .../reviews` 留痕 style(dict);DB 真源 `chapter_reviews.style` 列真填。③ **回炉**:`POST .../refine`→200 `{original,refined}`,refined≠original(writer 据 output_schema is None 返改写串)+ 不写库(无 chapter/review 行,不变量#3)+ writer 记账落 usage_ledger。**无新 bug**。关键坑(记 memory/gotchas):学文风 BackgroundTask 在独立 session 上**自造网关**(`build_gateway_for_tier` 从凭据建真 OpenAI 适配器)→E2E 无真凭据,须 monkeypatch `style.build_gateway_for_tier`→真 Gateway 包假适配器(ledger 绑入参 session);写侧 repo + `run_job` 默认 `SqlJobRepo` 保持真→指纹/job 真落 pg;override `get_session_factory`→`e2e_sm`(同测试 engine/loop)。最终全收口门禁绿:ruff/format 干净、mypy **115 文件**、pytest **296 passed**(293→+3)、**alembic 无漂移**(M4 无建表迁移)。**M4 出口 DoD 全达成 → Phase 4 可归档。** - [2026-06-19] @frontend — **M4 Wave C:T4.4✅ 文风页 + 漂移 + 回炉 + job 轮询**(消费 C3 扩 + C4 扩,无契约变更)。先 `pnpm gen:api` 纳入 style/refine/GET-style + jobs 类型(`lib/api/types` 加 4 别名)。① **job 轮询**:新模式 `lib/jobs/job.ts`(纯状态机 `narrowJob`/`reducePoll`/`isTerminal`/`styleLearnResult`——`GET /jobs/{id}` 是松散 `{[k]:unknown}` 裸端点须安全收窄) + `useJobPoll.ts`(setTimeout 轮询,done/failed 终态停,卸载清理)。② **文风页** `app/projects/[id]/style/page.tsx`(RSC 取 project + `GET /style`→404 容忍返 null) + `components/style/`:`StyleUpload`(多段文本粘贴/选文件 `file.text()` 读成文本→空行切段→`POST /style`)、`useStyleLearn`(受理 202→`useJobPoll`→done 拉指纹)、`FingerprintView`(16 维行+可展开证据)。③ **审稿页第四审**:`lib/review/sse.ts` 加 `StyleEvent`/`StyleDriftReport`/`reduceReview` case "style"(替换式仿 pace)/`ReviewStreamState.style`/seed;`history`-style 收窄 `normalizeStyleDrift` 落 `lib/style/style.ts`;`StylePanel`(◔ 整体相似度% + 漂移段一键回炉)接进 ReviewReport(seed 扩四态)。④ **回炉** `RefineView` + `useRefine`(`POST /refine`→新旧 diff→采纳:把 refined 替换 finalText 中原段,经既有 draft 路径合入,不另写库守不变量#3;503 优雅提示)。LeftNav 开「文风」入口(`ActiveNav` 加 "style")。前端门禁全绿:gen:api/lint/tsc/**vitest 96**(+27:job 12 + style 10 + style-sse 5)/build(含 `/projects/[id]/style` 路由)。memory 记 1 gotcha。下一步 **Wave D:T4.5(@qa) M4 E2E**。 - [2026-06-19] @orchestrator — **M4 Wave B 完成 ✅**(T4.3,C3 扩稳定)。style/refine 三端点入 OpenAPI:`POST /style`(202+jobs BackgroundTask 跑 run_style_extraction→StyleFingerprintWriteRepo.append)、`GET /style`(最新指纹含证据/版本)、`POST /refine`(writer 网关同步返 {original,refined} 不写库)。全仓门禁绿:ruff/format 干净、mypy **115 文件**、**alembic 无漂移**、pytest **293 passed**(+13)。`GET /style` 顺带补入(回写 ARCH §7.2/PRODUCT_SPEC §6 留 @docs)。下一步 **Wave C:T4.4(@frontend)**——先 `pnpm gen:api` 纳入 style/refine/GET-style + jobs 类型,再做文风页(上传/指纹/证据) + `lib/jobs/useJobPoll` + 审稿页第四审 StylePanel + 回炉 diff。 - [2026-06-19] @backend — **M4 Wave B:T4.3✅ API style + refine**(契约 **C3 扩稳定**)。三端点入 OpenAPI(独立 `routers/style.py`,仿 outline.py,已注册):① `POST /projects/:id/style` → **202** `{job_id}`(学文风走 jobs,M4-c):`job_repo.create("style_learn")`+commit → `background_tasks.add_task(run_job, session_factory, job.id, work)`;`work` 在 `run_job` **自建独立 session** 上自造 analyst 网关+写侧 repo → `run_style_extraction` → 拆 `dimensions_json`/`evidence_json` → `append`(version+1) → `{version,dims_count}` 落 `jobs.result`;无凭据 → dep 解析阶段 **503**(调度 job 前拦下,不凭空写注定失败的 job)。② `POST /projects/:id/chapters/:no/refine` → 同步 200 `{original,refined}`(writer 网关 `run(refiner_spec)`,`output_schema=None`→`resp.text`;不写库守不变量#3;末尾 commit 让 usage_ledger 落库)。③ **顺带补 `GET /projects/:id/style`** → 最新指纹 `{dimensions,evidence,version}`(UX §6.9;ARCH §7.2 原缺读端点——决策见 decisions,**待 @docs 回写 ARCH §7.2/PRODUCT_SPEC §6**);不动 C5 读侧 `SqlStyleRepo`(assemble 行为不变)。新建写侧 `domain/style_repo.py`(`StyleFingerprintWriteRepo`/`Sql*`,`append` 只 flush + `latest` 含证据/版本);`project_deps` 加 `get_style_write_repo`/`get_style_extract_gateway`(analyst)/`get_refine_gateway`(writer)。全仓门禁绿:ruff/format 干净、mypy **115 文件**、**alembic 无漂移**(M4 无建表)、pytest **293 passed**(280→+13)。memory 登记 C3 扩 + 2 decisions + 1 gotcha。**@frontend 行动**:T4.4 前 `cd apps/web && pnpm gen:api` 纳入 style/refine/GET style + jobs 轮询类型。下一步 **Wave C:T4.4(@frontend)**。 - [2026-06-19] @orchestrator — **M4 Wave A 收口 ✅**(T4.2 + T4.1 全绿)。全仓门禁收口:ruff/format 干净、mypy **111 文件**、**alembic 无漂移**(M4 无建表迁移)、pytest **280 passed**(M3 基线 228 → +52:T4.2 37 + T4.1 15)。契约稳定:C6扩/C4扩(StyleFingerprintResult/StyleDriftReview/四审齐/SSE style 事件)、C3.5(JobRepo/run_job)。下一步 **Wave B:T4.3(@backend)**——单 @backend agent 顺序做 style/refine 两端点:新建 `StyleFingerprintWriteRepo` + `routers/style.py`(`POST /style` 202 调 `job_repo.create`+`run_job`(work=`run_style_extraction`→`StyleFingerprintWriteRepo.append`);`POST /refine` 同步 `refiner_spec` writer 网关返 {original,refined}),C3 扩;评估顺带 `GET /projects/:id/style`。 - [2026-06-19] @backend — **M4 Wave A:T4.1✅ jobs 长任务基建**(契约 **C3.5 稳定**,不碰 style 业务,与并行 T4.2 @llm 无目录/写冲突)。`packages/core/ww_core/domain/job_repo.py`:`JobRepo` Protocol + `SqlJobRepo`(create/set_running/set_progress 夹取/complete/fail/get/reap_zombies) + frozen `JobView`——状态写**只 flush**(提交归 `run_job`/端点),唯 `reap_zombies` 启动期独立调用故**自 commit**。`apps/api/ww_api/services/job_runner.py`:通用 `run_job(session_factory, job_id, work)` **自建独立 session**(仿 `run_overdue_scan`)→set_running→work→complete/异常→新 session fail,异常吞不冒泡;最小 `JobLifecycleRepo` Protocol + 可注入 `repo_factory` 缝。`get_job_repo` 加进 `project_deps`;`main.py` lifespan 在 `seed_stub_user` 后跑 reaper(M4-d,幂等自 commit)。门禁绿(限本任务文件):ruff/format 干净、mypy 本任务文件 0 error(`packages/core apps/api` 余 7 error 全在 @llm 在做的 `test_style_*`)、pytest **15 新测**(13 job_repo+2 job_runner),`packages/core` **130 passed** / `apps/api` **50 passed**。memory 登记 C3.5。**T4.3 行动**:`POST /style` 调 `job_repo.create` + `background_tasks.add_task(run_job, session_factory, job.id, work=…)`。 - [2026-06-19] @llm — **M4 Wave A 半程:T4.2✅**(C6 扩 + C4 扩稳定)。style-auditor 双轨落地:①提取轨 `style_extract_spec`(analyst,独立生成仿 outliner,**不进 review 图**)+`StyleFingerprintResult{dimensions:[StyleDimension{name,value,evidence}]}`(16 维带证据)+`orchestrator/style_extract_node.py`(`build_style_extract_request`/`run_style_extraction`,模块内自有 GatewayRun Protocol,网关失败上抛/parsed 违约抛 ValueError,只读不写库);②漂移轨第四审 `style_drift_spec`(name="style",light,只读)+`StyleDriftReview{score=100,segments:[StyleDriftSegment{idx,score,label?}]}`(默认值即无指纹降级态)→`REVIEW_SPECS` 四审齐;collect 加 `STYLE`/`extract_style`→record `style=` 列;sse 加 `EVENT_STYLE`/`style_event`+`_section_result_events` STYLE 分支(`style{score,segments}`,字典序 continuity能力不符>时间线倒错>地理矛盾>性格漂移,未知殿后)+ 组计数徽标 + 「跳到下一条未裁决」(按显示顺序环回)+ 主次裁决按钮(采纳改法=朱砂主、忽略/手改=次级)——新纯函数 `lib/review/grouping.ts`(`groupConflicts`/`displayOrder`/`nextUnresolvedInOrder`,**分组只改显示顺序、每条携原始 index,守 conflict_index 覆盖 range 不变量**),`ReviewReport` 改分组渲染。TDD:snippet 8 测 + grouping 10 测。前端门禁绿:lint/tsc/**vitest 201**(+18)/build。分支 `feat/ux-r1-r2-review-ergonomics`。下一步(按 plan):R3 验收前清单 → Tier4。 - [2026-06-20] @frontend — **UX R3 完成**(验收前清单 HITL gate,UX §7.4,纯前端无后端改,续 `feat/ux-r1-r2-review-ergonomics`)。新纯函数 `lib/review/accept-preview.ts`(`buildAcceptPreview`:据现有 client state 列「本次验收将更新」——晋升版次 + 提炼章节摘要 + 写回裁决(有冲突时) + 验收后伏笔到期扫描(可能置 OVERDUE) + 伏笔建议待登记提醒(有建议时))。**只列后端 accept 事务确实做的事**(核对 `projects.py` accept 端点:promote+digest+decisions+overdue 扫描;**不**声称改人物 latest_state/自动落库伏笔建议);带「以上为预期,最终以验收回执为准」免责。`AcceptPanel` 加 `foreshadowCount` prop + 未阻断时渲染清单,`ReviewReport` 传 `foreshadow.length`。TDD:4 测。前端门禁绿:lint/tsc/**vitest 205**(+4)/build。下一步(按 plan):Tier4(T4-a 顶部 AI 工具条 / T4-b 本章指令框+风格预设)。 - [2026-06-22] @backend — **✅ T6.2+T6.3 创作工具箱通用端点稳定**(通用生成器框架后端落地,分支 `feat/t6-creation-toolbox`)。新 `ww_skills.TOOLBOX`(11 条:legacy 3 + 新 8) + `get_tool`(`toolbox_registry.py`,descriptor 在代码无迁移)。3 新端点(`routers/toolbox.py`,已注册):`GET /skills/toolbox`(描述符列表,legacy 携 `legacy_route`:`codex?gen=world|character` / `/outline`)、`POST .../skills/{tool_key}/generate`(按 `spec.tier` 经新注入缝 `get_tier_gateway_builder` 建网关 → `run_generator` 结构化预览,**不写库**仅落 ledger;未知/legacy→404、项目不存在→404、无凭据→503)、`POST .../skills/{tool_key}/ingest`(仅 `ingest!=None` 工具;world_entities 走 **continuity 预检 409 gate**(`acknowledge_conflicts` 放行) + `partition_writes` 白名单 + schema→JSONB 写库,outline 细纲按 idx 拼 beats upsert;不可入库→422)。PURE context 派发 `services/toolbox_context.build_toolbox_context`(4 策略,缺章/大纲→空节拍不报错)。response/request schema `schemas/toolbox.py`(concrete 子模型,非裸 dict)。守不变量 #2/#3/#9。TDD:注册表 7 + context 8 + 端点 11 = **+26 测**。**全仓门禁绿**:ruff/format 干净、mypy **183 Success**(隔离 cache)、alembic **无漂移**(无新迁移)、pytest **578 passed**。OpenAPI 新增 3 端点 → **@frontend 须 `cd apps/web && pnpm gen:api`**(T6 前端前置)。记 contracts C3 扩(T6.2/T6.3)。 - [2026-06-20] @frontend — **Tier4 完成**(T4-a 全局 AI 工具条 + T4-b 本章指令直通生成,分支 `feat/ux-tier4-ai-toolbar`,未并 develop)。T4-a:新 `lib/nav/ai-tools.ts`(aiToolItems) + `components/AiToolbar.tsx` 服务端组件,AppShell 顶栏下仅项目页渲染(写本章/审稿/大纲/设定库/工具箱常驻一排,写本章=朱砂主动作),用 `--chrome` CSS 变量统一 chrome 高度(项目页 7rem=header4+toolbar3,余 4rem),6 个固定高度页改用 `calc(100vh-var(--chrome,4rem))` 适配。T4-b(全链):后端 `assemble/_build_volatile` 加 `directive` 形参(「本章指令」段领衔 volatile,**绝不入 stable_core**,守缓存前缀不变量 #9),`stream_draft` 加可选 body `DraftStreamRequest{directive}` 透传、`directive_len` 入 log 不记原文、不持久化无迁移,重生成 TS 客户端;前端新 `lib/workbench/directive.ts`(STYLE_PRESETS + composeDirective 纯函数)、`useDraftStream.start(p,no,directive?)` 非空时发 JSON body、Workbench 加可折叠「本章指令」textarea + 风格预设 chips。TDD:ai-tools 4 测 + directive 4 测 + assemble directive 测 + draft 端点 directive/向后兼容 2 测。门禁全绿:后端 ruff/format/mypy(163 文件)/**pytest 479 passed**;前端 lint/tsc/**vitest 213**/build。契约记 contracts C3 扩(T4-b)。 - [2026-06-28] @backend/@frontend — **UI Phase G 后端依赖补齐**:`ProjectResponse` 增加 `updated_at` / `pending_review_count`,`chapters` 加 `updated_at` 迁移 `8c1d2e3f4a5b`;`SqlProjectRepo` 聚合项目自身、章节、审稿时间作为最近编辑,并按草稿是否晚于最近 accepted/review 统计待审稿章数。前端 `pnpm gen:api` 后项目库默认按最近编辑排序,新增“待审稿”筛选,作品卡显示最近编辑与待审稿徽标。契约记 `memory/contracts.md` C3 扩,UI 计划记 `docs/design/ui-improvement-plan.md` 0.16。 - [2026-06-29] @frontend/@qa — **UI review follow-up 修复完成**:补 `lib/review/chapterNav.ts` 使新增章导航测试可运行;`ReviewSectionPanel` 改为本地 open 状态,避免用户折叠后重渲染强制弹开;`ReviewReport.hasReport` 纳入 clean review;新增 `tests/test_project_metadata_e2e.py` 用真实 DB 覆盖项目最近编辑/待审稿统计口径。验证:前端 lint/typecheck/vitest **490 passed**/build;后端 ruff/format/mypy、pytest **763 passed**;浏览器抽样 review/settings 无 console error/横向溢出。 - [2026-07-06] @frontend — **✅ 灵感⑥ 关系图谱(缩减版)+ D4 完成**(纯前端,无后端契约/迁移/gen:api)。新增 `lib/characters/graphLayout.ts`(纯逻辑:`roleStyle` role→分组配色关键词映射、`buildGraphNodes`/`buildGraphEdges`(kind→标签,`DEFAULT_RELATION_LABEL` 命名常量)、`countDanglingRelations`、`layoutGraph`(@dagrejs/dagre 一次性确定性初始坐标、中心→左上、不可变)、`buildRelationshipGraph` 端到端)——**仅用现有 `CharacterCardView.relations={name,kind,note}`**:边按 name-join、kind 作中文标签、节点按 role 上色,无 closeness/阵营(数据不存在,不硬造);悬空边(引用未入库角色)跳过并计数。新增 `components/characters/RelationshipGraph.tsx`(`'use client'`,React Flow @xyflow/react;CodexPage 经 `dynamic(ssr:false)` 挂载于人物 tab,显式容器高度 h-[28rem] + `import '@xyflow/react/dist/style.css'` + prefers-reduced-motion 关 fitView 动画 + 纸感 CSS 变量配色 + 悬空计数提示)。依赖:`@xyflow/react@^12.11.2`+`@dagrejs/dagre@^3.0.0`(见 contracts.md 2026-07-06)。TDD:graphLayout **+22 测**(role 映射/节点去空重名/kind 标签+默认/悬空跳过/自指跳过/无向去重/边 id 唯一/悬空计数/布局确定性+不可变/端到端);组件走人工/E2E。前端门禁全绿:lint 干净 · typecheck 干净 · **vitest 565 passed** · **coverage lib 95.4%(graphLayout.ts 97.6%)** · **build OK**(codex 路由 7.58 kB,React Flow 懒加载独立 chunk)。注:`pnpm build` 门禁与运行中的 `next dev`(3001) 共享 `.next` → 触发 dev 服务临时 500(stale chunk),非代码缺陷、dev watcher 下次重编自愈,未重启服务。