方法:逐文件精读 73 源文件(非上一份的 grep + 架构断言),交叉 spec 后端规范,跑全量 246 测试复现(绿)。 上一份 review(06-27)的结论(v6 清洁 / 架构自洽 / 246 绿)成立,但漏掉了核心写章循环里的两个 P0——它们正好落在 F1(后半段无 CLI 缝)与测试盲区里。
代码风格、v6 清洁、DTO 隔离、确定性生成(drift)这些都没问题。但「备料→机检→两审→定稿→next」这条主循环在真实环境下跑不起来:定稿后缓存不重建且无 rebuild CLI,状态机会重抄最新章;建书流程不 git-init 书仓库。两者都被测试脚手架(预 init git、预落章、手动 rebuild)掩盖。spec 层有 1 处硬矛盾、多处「待增量补充」未闭环。
next 无限重抄最新章finalizeChapter(src/finalize/index.js:21)写章、commit、清工作区,全程不碰 cache(ctx 收了 cache? 却从不引用)。定稿后磁盘多了 定稿/正文/0003-...,但 .cache/index.db 里 MAX(chapter_num) 还是上一章。bin/webnovel-writer.js:85 每条命令只调 cache.ensureReady,而 ensureReady(src/cache/index.js:21)仅在 db 缺失/损坏/空表时重建——db 存在且有非空表就直接复用,无任何陈旧检测。rebuildFromSource(只有测试调)。src/commands/ 下没有 rebuild/refresh-cache 命令——宿主连「刷新缓存」的 CLI 都没有,唯一刷新手段是作者手动删 .cache/。webnovel-writer next → 缓存仍说 maxChapter=2 → 序6「起草第 3 章细纲」。第 3 章已在定稿里。主循环卡死,每章都要作者手动删缓存。test/finalize/finalize.test.js:96 在定稿后手动 ctx.cache.rebuildFromSource(...)——测试作者知道要重建,所以断言绿;test/state-machine/router.test.js 在 ensureReady 之前就把章文件落盘,从不测「定稿后 next」。F1(无定稿 CLI)使整条 finalize→next 从没被端到端测过。persistCreateBook(src/state-machine/persist.js:29)只写 book.yaml + 总纲.md + 第01卷.md,不 git init、不 git config core.quotepath false、不写 .gitignore。finalizeChapter 的 git.add/commit 直接失败 → 每次定稿都回滚报「定稿中断」。.cache/index.db 被当普通文件跟踪/提交,违反 spec database §2.1/2.2(缓存必须 gitignored)。工作区/ 不在 v7/.gitignore(只 ignore .cache/ node_modules/ dist/)→ 草稿/审稿被跟踪,定稿清工作区后留下「已删除未暂存」脏状态。quality-guidelines §3.3 明确「书仓库初始化必须设 git config core.quotepath false」是 CI 硬约束——目前无任何代码落地,归属也不清(建书流程?M5 安装器?)。router.test.js:17-29 的 makeGitBook 手动 git init + git config + 写 .gitignore('.cache/\n工作区/\n'),把这三件事全替生产代码做了。测试绿,生产裸。P0-1/P0-2 都不是「与 smoke 一起推迟」能解释的——它们是脚本面(M1-M4 范围内)的真实缺口,只是因为 smoke 推迟、没有端到端测试而没暴露。建议进 M5 前先补上,否则 M5 安装器接一个跑不通的循环。
rebuildCache(src/cache/rebuilder.js:12)DELETE 6 表后逐表 INSERT,无 BEGIN/COMMIT。scanEntities 在别名冲突时 return {ok:false}(:241),此时 chapters/threads/secrets 已写入、entities/aliases/characters 空——返回失败但库已半填,CacheManager 不在失败时重建。与上一份「数据完整性 SOUND」断言冲突。注:注释说「DELETE 五表」实际删 6 表。
goto-chapter --confirm 不 stash 未提交改动 → reset --hard 丢手改src/state-machine/flows/goto-chapter.js:33 先 createBackupRef(只存 HEAD 指针)再 resetHard(hash)。git reset --hard 丢弃所有已跟踪文件的未提交改动,rescue ref 不含工作树。若作者有序 2 式未登记手改(定稿/大纲已跟踪且改了),改动被静默抹掉、无法从 rescue ref 找回。该 flow 也不跑 checkGitHealth。应:reset 前先 git stash 或拒绝脏树。
clean 未包 trysrc/finalize/index.js:112-113 回滚用 git.restore(['定稿/','大纲/']) + git.clean(['定稿/','大纲/']):
written 集合。若本次只写第 5 章、但第 4 章有未提交手改,回滚会把第 4 章手改一起抹掉。注释自称「仅 定稿/大纲」是树级不是文件级。git.clean(src/finalize/git.js:33)没有 try/catch(restore 有)。Windows 文件锁使 clean 抛错时,错误逃出 finalizeChapter 的 catch,破坏 {ok,error} 契约,调用方拿到 throw 而非结构化失败。ChapterWriter.writeChapter(src/storage/adapters/ChapterWriter.js:24)直接 ${NNNN}-${标题}.md。标题含 ? * " < > | 或半角冒号时 Windows 写盘失败 → finalize 回滚。标题是 AI DTO,无 sanitize。writeChapter 用 writeFile 覆盖同文件名,标题变了文件名就变,旧文件残留 → scanChapters 两条同 chapter_num → INSERT ... PRIMARY KEY 冲突 → 重建失败。updateFrontMatter throw / installer 占位:已知 F3,无人依赖,诚实占位,留未来。yaml-dialect.needsQuoting 不全(src/storage/serializers/yaml-dialect.js:79):只查数字/布尔/null/冒号/#/-/换行。漏 [ { & * ! | > % @ 等首字符 → 值如 [弱钩]、{x} 会被 YAML 误判为数组/映射。当前数据形状碰不到,潜伏坑。ThreadLedgerWriter.updateThread(src/storage/adapters/ThreadLedgerWriter.js:29)调 serializeFrontMatter(merged, parsed.body) 不传 originalYAML。merged 含全部原字段,平铺字段能保留;但若作者加了嵌套映射自定义字段,serializeYAML 抛「防呆方言禁止嵌套」→ updateThread 返回 ok:false → finalize 回滚。与 spec §4.5「未知字段必须保留原样写回」直接冲突(见 S2)。checkRepetition(src/mechanical-check/index.js:100)break 在首条命中,漏报其余;L=6/阈值 3 对常见六字短语易误报。checkNewProperNouns 正则 ([一-龥]{2,3})(…|道|说|…) 对「他便笑道」类产生大量非阻断候选噪声(不拦截,仅噪)。assembleReviewInput(src/review/index.js:43)用 草稿全文.includes(name)(name=文件名=正名)。草稿只用别名「大师兄」、文件名「林晚」时漏纳入,审查上下文缺失。session.assembleSessionContext 自愈不回写(src/session/index.js:57):books.jsonl 损坏时只扫描重建到内存返回,不写回 books.jsonl,下个会话再扫一遍。函数名/注释「自愈」名不副实(spec 说本层只读、M5 写侧——但「自愈」措辞误导,建议改名或 spec 注明)。findChapterCommit 依赖 git 默认 BRE:git.js:93 --grep=ch(${n}): 能匹配字面 ch(3): 仅因 git 默认 BRE 里 () 是字面。一旦 grep.extendedRegex 配置开启即失效。脆弱但当前正确。roleToToml/roleToMarkdown(src/host-shells/generate.js:42-47)直接拼 description = "${role.description}"。description 含 " 或换行即破 TOML/YAML frontmatter。validator 只查源 roles 的 ABS_PATH,不校验生成物格式,drift check 也只比字节相等不比合法。validator.ABS_PATH 漏路径(src/host-shells/validator.js:9):只匹配 [A-Za-z]:\\ 和 /(Users|home)/。/root/、/mnt/、裸 /etc/x 都漏。便携性校验有洞。database-guidelines §2.4 列五表(chapters/threads/secrets/entities/fingerprints),实现六表(多了 entity_aliases)。spec 漏列 entity_aliases,文档与代码不一致。quality §3.3 要求书仓库初始化设 core.quotepath false,但未指定由哪一步负责(建书流程?安装器?),导致无人实现(P0-2)。spec 应钉死责任方。quality §2.2「AI 调用每章预算上限(实现时定数)」M4 已过仍未定数,runReviews 固定 2 次调用无上限约束。应在 M4 收口或显式推迟到 beta。error-handling §3.1「所有多文件写入操作必须原子」,但 persistReviewReport(src/review/index.js:104)写 3 个文件非原子无回滚。spec 未给「工作区多文件写入」豁免边界,要么补豁免、要么补实现。directory-structure §3.3「书仓库内零工程文件(唯一例外 AGENTS.md)」未说明 .gitignore 归位,而 .cache/ 必须被 ignore——存在规范真空,导致 P0-2 里 .cache 被跟踪无人挡。v7/package.json 是 0.0.0。发布前须设版本;README 版本表是 CI 硬约束,M5 发版前要对齐。router.test 在 ensureReady 前已落章 → 不测「定稿后 next 陈旧」。finalize.test 手动 rebuildFromSource → 掩盖「无自动重建」。makeGitBook 手动 git init+config+.gitignore → 掩盖「建书不 init git」。goto-chapter.test 未构造「脏工作树 + confirm」用例 → P1-2 不触发。建议补一条端到端集成:init 书 → 备料 → 机检 → runReviews(桩) → finalize → next,期望 next 报序6 且 nextChapter = 已定稿章 +1。这条一加,P0-1 立刻红。
next/finalize 入口刷新)+ P0-2(建书流程 git init + .gitignore + core.quotepath)——这是让主循环跑通的前提。