Commit Graph

20 Commits

Author SHA1 Message Date
Yaojia Wang
90a66437d7 feat: improve app ui and project metadata 2026-06-28 07:31:20 +02:00
Yaojia Wang
bf39f50b2f fix(qa): 实施 4 组设计型 QA 项——规则删除/codex 角色/大纲卷过滤/文风回炉锚点
#5 规则 DELETE + id:暴露 RuleView.id(PK);新增 DELETE /projects/{id}/rules/{rule_id}
  (项目/规则不存在→404,成功→204,按 (id,project_id) 限定);rule_repo 加
  list_for_project/delete;RulesPage 每条加删除(乐观删+回滚+toast)。assemble 侧
  RuleView(缓存前缀)不动,列表另立 RuleListItemView。
#7 codex 角色 relations:写侧本已持久化、读端点 _existing_characters 硬编码 []。
  加 _relations_from_jsonb 解析 {name,kind,note},CodexPage 渲染关系 chip。
#8 角色入库幂等:SqlCharacterWriteRepo.create 改 (project_id,name) app 层 upsert——
  重复入库改更新而非插入;不加 UNIQUE/迁移(线上已有重复行会让约束迁移失败)。
#1 大纲卷过滤:GET /outline 支持可选 ?volume(无参=全部,向后兼容);OutlineEditor
  加「查看:全部/卷N」筛选,与生成目标卷解耦。
H3/#9 文风回炉锚点:StyleDriftSegment 加 text(逐字命中段),style.md 指示审稿输出;
  前端按内容锚点定位回炉目标(idx 仅排序),命中失败 → 提示「无法定位该段」而非
  静默 no-op。style golden fixture 已重生成。

契约变更已 pnpm gen:api(RuleView.id / DELETE rules / outline ?volume)。无迁移
(alembic 无漂移)。门禁绿:ruff/mypy(210)/alembic/pytest 760 · 前端 tsc/lint/vitest 329。
2026-06-25 12:53:03 +02:00
Yaojia Wang
0021426f09 docs(prompt-mgmt): 方案A 协同回写 + 补声明 structlog 依赖
- memory/contracts.md:立 C6-ext(load_prompt/SPECS/SCHEMA_CATALOG/
  REVIEW_RESERVED_NAMES/SpecResolver + 打包契约)+ 变更日志一笔
- memory/decisions.md:方案A 决策(运行时值金标准/尾换行方案B/守卫前移/
  schema 留 Python/同一实例)
- memory/gotchas.md:spec.name 连字符命名 / 改稿须重生成金标准 /
  .gitattributes 完整嵌套路径 / wheel artifacts 带 .md / structlog 补声明 /
  SpecResolver 零 DB + 精确匹配
- PROGRESS.md:已封板「Prompt 管理重构 + 全量重写」波次
- ARCHITECTURE.md §5.1:补 prompt 外置 + SPECS/SCHEMA_CATALOG/SpecResolver,
  内置 agent 计数 8→21
- packages/{llm_gateway,core}/pyproject.toml:补声明 structlog>=24.1
  (原直接 import 未声明,靠 apps/api 传递;裸装 import ww_agents 会缺)
2026-06-24 08:25:26 +02:00
Yaojia Wang
cfe6a535b0 feat(templates): F3b 模板库 repo + 端点(GET/POST/DELETE /templates)
- TemplateRepo/SqlTemplateRepo(list/create/delete,owner stub 过滤,只 flush 端点 commit)
- schemas/templates.py(title/body min_length=1→422)+ routers/templates.py(删不存在→404)+ main 注册
- 单测:repo CRUD/owner 隔离/frozen + 端点 201/列出/204/422×2/404
2026-06-23 20:17:32 +02:00
Yaojia Wang
18aa87d751 feat(chain): F2 续写式链 continue_volume——上一章 accepted 正文作前文引子
- core write_chapter 支持续写模式:chain_key==continue_volume 时经注入的
  chapter_repo.latest_accepted 读上一章已验收终稿,build_continuation_context
  改写 volatile(仅断点后块,守不变量 #9;core 不 import apps/api)。
- ChapterDraftRepo 协议加 latest_accepted;draft_volume 行为不变(无回归)。
- apps/api SUPPORTED_CHAINS 加 continue_volume;run 端点透传 chain_key 选模式
  (chain_runner 经 initial_chain_state 把 chain_key 落入 ChainState)。
- 单测(mock 网关+MemorySaver+fake session):续写第二章请求含第一章正文;
  draft_volume 不注入前文;端点 202 透传 chain_key。守不变量 #1/#5。
2026-06-23 20:09:08 +02:00
Yaojia Wang
87dd09a797 feat(agents): Scope B A1 — 4 竞品生成器 schema/spec + 2 上下文 builder
续写/扩写/降AI率/拆书 4 个生成器的声明(preview-only writes=[],只声明 tier #2):
- schemas.py: ContinuationResult/PolishResult/DeAiResult/BookTeardownResult
- specs.py: continue(writer)/expand(writer)/de-ai(analyst)/teardown(analyst),各配 system_prompt
- generation_node.py: build_continuation_context(续写读前文)/build_text_input_context(原文输入)
- 单测: 4 spec/schema 契约 + 2 builder 纯函数(确定性/空值降级)

守不变量 #2(只声明 tier)/#3(纯预览不写库)/#9(system_prompt 进缓存前缀)。
2026-06-23 19:01:11 +02:00
Yaojia Wang
d1ea83b191 fix(chain): 修评审 CRITICAL+HIGH — 链网关按 session 重建/日志脱敏/resume 原子化+所有权/死导入
- CRITICAL #1:链 write/review 节点经 gateway_builder 按节点自建 session 现建网关,
  usage_ledger sink 绑活 session,随节点 commit 持久化;run_chain_job 不再转发请求网关
  (其 session 在 BackgroundTask 跑时已关闭,记账行被静默丢弃)。新增 get_chain_gateway_builder
  缝(仿 digest builder),get_chain_gateway 退化为纯 503 凭据预检。守不变量 #1。
- HIGH #2:chain_runner 失败日志不再记 str(exc)(可能含 key/连接串/LLM 输出),改记
  _classify_job_error 脱敏文案 + exc_type(设计 §5)。
- HIGH #3:resume 端点原子抢占 awaiting→running(JobRepo.claim_awaiting_to_running 条件
  UPDATE),抢不到 → 409,防并发 resume 双 Command(resume) 损坏图。
- HIGH #4:resume 校验 job.project_id == project_id(JobView 新增 project_id),不匹配 → 404。
- HIGH #5:resume 返回新 ChainResumeAccepted{job_id,chain_key},去掉无意义哨兵 start/count=0。
- HIGH #6:删 nodes.py 死导入 extract_conflicts(import + __all__)。
- 测试 #7:e2e 断言链跑后 usage_ledger 有行(#1 回归守卫)+ chapter_reviews 每章一行;
  新增 claim 原子抢占单测 + resume 跨项目 404 / 并发 409 端点测。
2026-06-23 18:04:10 +02:00
Yaojia Wang
29349dc7ee feat(api): C2 多章链 服务+端点+schema+checkpointer 接线
承 C1 链图(build_chain_graph),落地多章工作流链的 apps/api 壳:
- 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、resume 非 awaiting→409、无凭据→503。
- schemas/chain.py:ChainRunRequest/ChainRunAccepted/ChainResumeRequest
  (ConflictDecision 复用 schemas/projects)。
- 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)+ get_digest_gateway_builder。

单测 apps/api/tests/test_chain.py 12 用例(mock 网关 + MemorySaver + fake session/
accept_op,无 DB/无网络/无真 LLM):run/resume→202、未知 key 404、count 越界 422、
resume 非 awaiting 409、run_chain_job 无冲突→done、冲突→awaiting→resume→done、
错误脱敏、accept_op 冲突缺判→CONFLICT_UNRESOLVED。

门禁绿:ruff/format 干净 · mypy 193 Success · alembic 无漂移 · pytest 600 passed。
守不变量 #1/#3/#4/#5/#9。唯一新增 DDL(langgraph 检查点表)= C3 迁移。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 17:12:53 +02:00
Yaojia Wang
7f3eaaba3d feat(orchestrator): C1 多章工作流链图 — write→review→decide→accept 有环图
实现 chain-workflow 设计 §3 的 LangGraph 链图,激活休眠资产
(checkpointer + interrupt-on-conflict)。

- packages/core/ww_core/orchestrator/chain/{state,nodes,graph}.py:
  - ChainState(§3.1):只放控制流(章号/计数/written/has_conflicts),
    正文/审稿/冲突明文落领域表,resume 重读(不变量 #1/#3/#5)。
  - 四节点 write_chapter/review_chapter/decide/accept_chapter(§3.2):
    各自短事务、独立 session;decide 纯逻辑读最近审稿留痕定 has_conflicts;
    accept 节点起始处遇冲突 interrupt() 暂停交人,恢复值=裁决清单。
  - build_chain_graph(§3.3 签名):节点 DB 依赖经 session_factory +
    repo 工厂 + assemble/accept_op 闭包默认参绑定(langgraph 不收 partial);
    条件边实现循环(current<=last 回 write)+ interrupt。
  - 验收事务(apps/api)经 accept_op 闭包注入,守目录所有权 + 不变量 #3/#4。
  - 收集版写章:复用 build_write_request 同一组装路径(stream=False → run)。
- 删除无人调用的 build_write_graph 工厂(write_node 本体保留,被链复用);
  清理其 __init__ 导出 + 旧单节点图单测(YAGNI,解决审计死代码)。
- 单测(mock gateway + fake session_factory + fake repos + MemorySaver):
  无冲突全跑到 END 且 written 满 / 有冲突→__interrupt__ / resume(Command)→
  accept→END / decide 纯逻辑 / accept 推进章号;零 token 零联网。

门禁:packages ruff/format/mypy 干净;pytest 314 passed。
2026-06-23 16:54:47 +02:00
Yaojia Wang
f43ccd293f feat(toolbox): T6 创作工具箱通用生成器框架 — 8 新生成器 + 声明驱动落地页 + P2 收尾
通用执行路径驱动全部生成器("加生成器=加一份声明"):
- @llm: ww_agents +7 输出 schema + 7 spec(book-title/blurb/name/golden-finger/
  glossary/opening/fine-outline,只声明 tier)+ build_outline_chapter_context
- @backend: ww_skills GeneratorTool 描述符 + TOOLBOX(11) + get_tool;3 通用端点
  GET /skills/toolbox · POST .../skills/{tool_key}/generate(预览不写库,仅记账) ·
  POST .../ingest(复用 continuity 409 + partition_writes 白名单);纯 context 派发
- @frontend: 工具箱落地页 RSC + 声明驱动 GeneratorRunner + lib/toolbox 纯函数
  + LeftNav「工具箱」+ ⌘K nav-toolbox/action-gen-*;legacy 3 跳现页
- @qa: tests/test_t6_toolbox_e2e.py 5 用例真 pg + mock 网关零 token,无端点 bug
- P2 收尾: 限流→decisions.md 记延后(单用户原型);noopener/Committable 早已修

守不变量 #2(只声明 tier)/#3(预览不写库,入库经验收 gate)/#9(缓存前缀)。无 DB 迁移。
门禁绿: 后端 ruff/format/mypy 195/alembic 无漂移/pytest 583;前端 lint/tsc/vitest 279/build。
spec 回写 PRODUCT_SPEC §7 + ARCHITECTURE §7.2 端点表。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 20:37:55 +02:00
Yaojia Wang
d028c790c9 feat(agents,api): 一致性冲突一键采纳补丁对 original/replacement + SSE/契约透传
- Conflict schema 增 original/replacement(可缺):可局部修复时审稿提议精确补丁
- 续审 prompt 指示填逐字 original + 改后 replacement,不可定位则留空
- orchestrator SSE conflict 事件 + apps/api ReviewConflictView 透传两字段
- 重生成 TS 客户端 schema.d.ts;memory/contracts.md 记录契约变更(向后兼容)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 18:28:04 +02:00
Yaojia Wang
016509c5c6 fix(gateway): 熔断计入持续 4xx + 去 assert + Protocol/transient 去重
P1-2 非可重试错误(持续 401/403)也 record_failure,坏 key 可触发熔断。
P1-9 _complete_structured 的 assert 改显式 raise ValueError(-O 安全)。
P2 GatewayRun 抽到 orchestrator/_protocols.py 单点(去 4 处重复);
  _is_transient 抽到 adapters/base.py is_transient_by_name(去 3 处重复);
  Gemini Protocol 改 async def;gateway._retrying 去无用 async。
2026-06-21 19:32:49 +02:00
Yaojia Wang
345cc73965 fix(txn+security): 仓储改 flush + 启动校验/兜底 + job.error 脱敏 + SSE 异常硬化
P0-1 SqlCredentialStore/save_draft 由自提交改 flush,端点/服务统一 commit
  (新增 CredentialStore.commit() 统一提交点;token 刷新落库显式提交);
  补多凭据一请求中途失败整体回滚集成测试。
P0-2 启动校验 _fernet(enc_key) 快速失败 + catch-all Exception → ErrorEnvelope;
  credential_enc_key 改 SecretStr。
P0-3 run_job 异常分类:AppError 存 code+message,其余存通用文案不泄 str(exc)。
P0-4 评审/正文 SSE 失败先发 error 事件,尾部 commit 包 try/except。
P1-4 max_version 加 FOR UPDATE 行锁消除 TOCTOU。
P1-5 scan_overdue 谓词下推 + 批量 UPDATE RETURNING。
P1-10 移除 OAuth user_code 日志。
P2 provider_deps 改调网关 build_adapter;accept_service Committable Protocol;
  CORS 白名单收窄;request_id 安全字符集白名单;stdlib 日志接管;读端点 404 校验;
  httpx timeout;测试用合法 Fernet key;类型化响应模型(JobResponse/DimensionEntry/
  ReviewConflictView/selling_points)+路由 ErrorEnvelope responses(供 codegen)。
2026-06-21 19:32:24 +02:00
Yaojia Wang
2155193cdc feat(ux): T4-b 后端 — draft 端点接收本章指令 directive 直通 assemble(volatile)
assemble/_build_volatile 加 directive 参数(仅入 volatile,守缓存前缀不变量 #9);stream_draft 加可选 body {directive} 透传;不持久化、无迁移;directive 长度入 log 不记原文。新 DraftStreamRequest schema + 重生成 TS 客户端。后端门禁绿: ruff/mypy/pytest。契约记 memory/contracts.md。
2026-06-20 18:25:00 +02:00
Yaojia Wang
0d473e726e feat(b0): 注入透明可控版后端 — PUT override(pin/排除/recent_n) + 选择函数加参 + draft 同读
- selection.select_relevant_entities 加 pinned/excluded frozenset 入参;新理由 author_pin(末位)
- assemble 加 override 关键字参(recent_n 覆盖回看章数 + pin/排除);GET/PUT/draft 同读同一覆盖(不变量 #6 作者兜底)
- 新 domain/injection_repo.py(InjectionOverride/EntityRef/SqlInjectionOverrideRepo,upsert 只 flush)
- 新表 chapter_injection(迁移 ad2c4c663daf,唯一 project_id+chapter_no;复用 outline 行已否决)
- PUT/GET injection 端点 + InjectionOverrideRequest/回显 pinned/excluded;recent_n 1..20 校验 422
- 单测:selection pin/排除/exclude>pin + assemble override + 端点 PUT/404/422;regen TS 客户端
- 门禁绿:ruff/format · mypy 163 · alembic 无漂移 · pytest 476

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 11:56:54 +02:00
Yaojia Wang
e8cccf7389 feat: T6.1 创作工具箱通用生成器框架地基 — 脑洞生成器 + run_generator
- schemas: Idea/IdeaListResult(脑洞生成器结构化产出,纯预览不入库)
- specs: brainstorm_spec(light 档,reads=[projects] writes=[])
- orchestrator: build_brief_context(brief_only 策略)+ 通用 run_generator
  (据 spec.output_schema 校验 parsed,不绑定具体类型,任一声明式生成器共用)
- 新增 13 测;两包 ruff/format/mypy 干净、252 passed 无回归

P1 第一波(@llm):证实「加生成器=加一份声明」。@backend T6.2/T6.3 待续。
2026-06-20 11:31:59 +02:00
Yaojia Wang
765dbdfbd4 feat: M4 文风 + M5 生成/多provider/Skill + Kimi Code 订阅接入 + 本地联调修复
M4(文风): style-auditor 双轨(提取指纹/漂移第四审)+ jobs 长任务框架(zombie reaper) + 回炉 refine + GET /style read-back。
M5(生成+扩展): worldbuilder/character-gen(入库 continuity 409 gate + partition_writes 白名单 + schema→JSONB 形变);
  网关多 provider 回退链/熔断/能力降级(Anthropic/Gemini 适配器);Skill registry + 表权限沙箱 + 规则;
  前端 角色生成器/世界观/Codex/规则页/技能库/⌘K 命令面板。
K1(Kimi Code 订阅接入): OAuth device-flow(kimi-code)+ 静态 Console key(kimi-code-key)两路径;
  coding 端点 KimiCLI 伪造头(实测 UA allow-list 门禁,缺则 403)+ JSON 模式结构化(thinking ⊥ tool_choice)。
本地联调修复: CORS 中间件;assemble 注入 premise+「写第N章」指令(修空 prompt 400);
  GET /outline·/draft read-back + 大纲/工作台/审稿页重载;写页 client/server 常量边界 + notFound 健壮化;
  字数 toLocaleString locale 水合;审稿页终稿从已存草稿 seed(修 accept 422)。
门禁: backend ruff/mypy(157)/alembic 无漂移/pytest 451 · frontend lint/tsc/vitest/build。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 10:39:58 +02:00
Yaojia Wang
5fb7bfb1de 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
2026-06-18 14:21:17 +02:00
Yaojia Wang
68f194a043 feat: M2 — 写→审(一致性)→裁决→验收(事务);未决冲突禁验收
- 续审 Agent 声明(AgentSpec) + 结构化输出契约(ContinuityReview/Conflict 五类)
- LangGraph 并行审子图(可扩四审) + collect 落 chapter_reviews 留痕 + review SSE(section/conflict)
- 验收-side Repository:章节 accepted 版本晋升 + digest append-only + 审稿留痕/裁决
- API:review(SSE) + reviews 历史 + accept(单原子事务:晋升 version + 终稿 digest + 裁决留痕)
- 冲突 gate:未决裁决拦截(CONFLICT_UNRESOLVED);digest 从终稿提炼(不变量#4)
- 前端:审稿报告页 + 冲突就地标注 + 裁决(采纳/忽略/手改) + 未决禁验收 + 「本次将更新」清单
- M2 E2E:真实 DB + 多档位 mock 网关零 token 走通 写→审→裁决→验收→摘要入库
- 多 agent 协同台账(PROGRESS.md) + 共享记忆(memory/contracts·decisions·gotchas)
2026-06-18 11:38:28 +02:00
Yaojia Wang
b523b4fd21 feat: M1 — 立项→写章草稿(SSE)→自动保存;连一家 provider
- 薄自建 LLM 网关:OpenAI 兼容适配器(DeepSeek) + instructor 结构化输出 + usage_ledger 记账 + 档位路由
- 记忆服务 assemble:确定性选择(显式+主角+近况) + 渲染卡 + 缓存断点(中性文本)
- LangGraph 写章节点 + Postgres checkpointer + SSE 归一(token/done/error)
- API:立项 + 写章 draft(SSE) + PUT 自动保存 + 提供商凭据(Fernet 加密/测试连接)
- 前端:AppShell + 作品库 + 5 步立项向导 + 写作工作台(流式打字机+自动保存) + 设置页
- M1 E2E:真实 DB + mock 网关零 token 走通闭环
2026-06-18 11:38:28 +02:00