[severity] file:line 问题 | 怀疑理由 | 建议探针 | 置信[P1] v7/src/state-machine/persist.js:123 | 置信=CONFIRMED(静态链路 + 测试空白已复核,未跑 CLI 探针)
persistRepair 对每一条修复内容都用 parseFrontMatter(r.content) 校验(persist.js:118-127)。但序0 源文件清单里有 3 类不是 front-matter markdown:
book.yaml:纯 YAML,serializeYAML 输出 key: value 无 --- 围栏(yaml-dialect.js:39;detectors.js:39-45 经 BookConfigReader 检测)定稿/设定/名册.md:纯表格,serializeMarkdownTable 输出(EntityWriter.js:63;detectors.js:57-63 用 parseMarkdownTable 检测)定稿/设定/时间线/第NN卷.md:纯表格(TimelineWriter.js:42;detectors.js:66-75)---,parseFrontMatter 直接返回 ok:false,缺少 front matter 分隔符(front-matter.js:30-39)。于是 persistRepair 在 :124 判定「修复内容仍解析失败」拒写。序0 在状态机最前(index.js:23-26),命中即停——一旦 book.yaml/名册/时间线 表损坏,序0 每次都触发,而唯一出口 persist-repair 又拒收合法修复 → 书被锁死在序0,作者无法在工具内自愈(违背序0「永不带堆栈崩溃/AI 提议作者确认」承诺)。检测侧用对了解析器(parseFrontMatter vs parseMarkdownTable),只有修复回写侧校验一刀切,是读写不对称。定稿/正文/0001-起.md(front-matter 文件),从未覆盖 book.yaml/名册/时间线,正是「流程自身绿、边角没人测」。persistRepair(ctx, {repairs:[{file:'定稿/设定/名册.md', content:'| 正名 | 别名 |\n|---|---|\n| 林晚 | |\n'}]}, {allowedFiles:['定稿/设定/名册.md']}) → 预期错返 修复内容仍解析失败(定稿/设定/名册.md):缺少 front matter 分隔符。book.yaml 同理。[P1] v7/src/state-machine/flows/goto-chapter.js:38-57 | 置信=CONFIRMED(静态推演;建议 CLI 探针坐实)
工作区/待定稿/ 批次被 gitignore(persist.js:60 建书 / migrate:50 迁移都 ignore 工作区/),是未跟踪文件。goto-chapter 只 createBackupRef + resetHard(:54-57),不带 git clean,故 reset --hard 不删批次;脏树检查 dirtyScoped 只看 定稿/大纲(:41-44),gitignore 文件不进 porcelain,批次在场也不拦回退。goto-chapter 1 --confirm → next --json,看是否仍出序3 批次续跑且批次起章 > maxChapter+1(断档),且无任何冲突提示。[P2] v7/src/state-machine/persist.js:134 与 detectors.js:99 | 置信=PLAUSIBLE
listManualEdits 只收 定稿/大纲 前缀路径(detectors.js:99),relink 也据此圈范围(relink.js:16)。文风/文风铁律.md(front-matter 文件,能被 persistRepair 写成功)落在 定稿/大纲 之外 → 序2 永远看不见 → relink 不会提交它。reset --hard(dirtyScoped 同样只看 定稿/大纲,:41-44)会静默抹掉这个已跟踪文件的改动。book.yaml 同类,但它更早卡在 P1-D1 写不进来。文风/文风铁律.md 走 persistRepair → next --json 看是否出序2;git status 看 文风/ 是否长期 dirty;relink 后确认文风铁律仍未被提交。[P2] v7/src/state-machine/flows/retcon.js:48-49 | 置信=PLAUSIBLE
git.restore(['定稿/','大纲/']) + git.clean(['定稿/','大纲/'])——整棵子树回滚。finalize 早已刻意改为逐文件回滚,并在注释里点名「非整棵 定稿/大纲 子树,避免误伤同子树其他章手改」(finalize/index.js:148-149)。retcon 仍是宽版。[P3] v7/src/state-machine/dto.js:75 → staging/index.js:74-78 | 置信=PLAUSIBLE
next 路由组 DTO 时 dto.js:75 调 readBatch;readBatch 在「批次.json 缺失/损坏但有章目录」时会 writeAtomicBatch 重建元数据(staging:74-78)。即状态机的读路径隐含一次落盘。工作区/待定稿/批次.json 保留章目录 → next --json,看是否静默重写出 批次.json。[P3] v7/src/state-machine/detectors.js:129 vs v7/src/staging/index.js:80 | 置信=PLAUSIBLE
待定稿/ 只按 readdir(...).length>0 计入(detectors.js:129);readBatch 则要求有 ^\d{4}- 章目录或有效 meta 才 exists:true(staging:42-82)。当 待定稿/ 只含杂项文件时,现存含「待定稿/」但 batchDetail 因 exists=false 返回 {}(dto.js:76)。从哪继续='待定稿批次续跑' 却无 批次 明细字段,AI 拿到「续跑」却无可跑内容。非崩溃,属边角不一致。工作区/待定稿/ 放一个非章目录杂文件 → next --json 看 从哪继续 与是否缺 批次 字段。[P3] v7/src/state-machine/index.js:46-48 | 置信=Low
cache.query('SELECT ... FROM chapters ...')(:46-48)无 try/catch,而同文件 readLastHealthCheck(:78-83)有。缓存查询若在路由中途抛错,determineNextState 直接抛。| 项 | 结论 | 证据 |
|---|---|---|
P1-1 卷复盘 commit vol(NN) |
✅ 在位 | persist.js:104 commit(vol(${nn}): 复盘与下卷规划),有 ensureIdentity + hasStagedChanges 守卫(:102-105) |
| P1-2 五处改源自刷缓存 | ✅ 全在位 | finalize/index.js:139、goto-chapter.js:59、retcon.js:44、persist.js:107(卷复盘)、persist.js:135(修复回写),均调 refreshCacheAfterSourceChange |
| P1-3 relink 执行通道 | ✅ 在位 | v7/src/commands/relink.js(add+fix(手改) commit+刷缓存,与 listManualEdits 同源圈范围);dto.js:33 序2 期望产物指向 relink;detectors.js:36 message 带补登命令。但见 P2-D3:文风/book.yaml 不在 relink 覆盖面 |
!repoPath 兜空目录。范围一致。收卷:是→is_volume_end,index.js:55 读 is_volume_end + volumeReviewDone 防重复触发(detectors.js:153-161 以卷摘要存在为准),与 §10 序4/§4.1/§7 一致,未用章号整除。maxChapter-lastCheck >= 体检周期,默认 50(index.js:52 / book.yaml),与 §10「距上次体检已满周期」一致;记录存 meta,丢失重测无害(readLastHealthCheck 容错)。