依据:本任务 prd.md + spec 0.9(决策 27-32)。模块现状引用以 2026-07-02 代码为准(commit
6b312a4起算,文档回填871c448不含代码)。
| 层 | 文件 | 改动 |
|---|---|---|
| storage/adapters | OutlineReader.js |
卷纲路径进 卷纲/ 子目录(A1) |
| storage/adapters | SummaryWriter.js |
卷摘要写 第NN卷.md(A2,若卷摘要写出在 persist 则改 persist) |
| storage/adapters | SecretWriter.js / SecretReader.js |
字段名 知情人/读者已知;listUnrevealed 扩返回(A3/C2) |
| cache | schema.js |
chapters 表加 is_volume_end;新增 meta 表(B1/B2);schema 版本号 +1 触发自动重建 |
| cache | rebuilder.js |
字段名切换(A3);收卷 填充 is_volume_end;secrets.short_title 从文件名短题解析(B1/C2) |
| state-machine | index.js |
序 4 改读收卷标记;序 5 改"距上次体检 ≥ 周期";book.yaml 解析失败不再默认值兜底(B1/B2/B3) |
| state-machine | detectors.js |
序 0 补扫四类;序 3 计入 细纲/材料(B3/B4) |
| state-machine | dto.js |
序 6 期望产物文案补收卷提议提示(B1) |
| state-machine | persist.js |
卷纲路径(A1)、卷摘要文件名(A2) |
| mechanical-check | index.js |
新增条目变动形式检查(C1) |
| prep | index.js |
信息差边界注入 短题/知情人/关键词/内容首句(C2) |
| review | index.js |
ReviewInput 增 拟条目变动 + 相关条目履历尾部(C3) |
| 新增 use case | src/health-check/index.js + src/commands/health-check.js |
最小体检:汇总既有报表 + 记录上次体检章号(B2) |
| roles | 事实审查.md |
输入清单补"相关条目(含拟变动与近期履历)"(C3) |
| test | fixtures + 相关测试 | 路径/字段名同步;新判定正反用例 |
不动:finalize/index.js(收卷经 payload.frontMatter 透传,无需改)、ChapterWriter(未知字段原样写出已覆盖 收卷)、SKILL.md、host-shells 生成器。
收卷: 是 → ChapterWriter 原样写出 → 定稿后缓存重建 is_volume_end=1 → 下次 next 序 4 命中。是 解析为字符串 "是";重建器接受 收卷 === '是' || 收卷 === true,其余值视为未收卷(不报错——非清单字段错误不属序 0)。SELECT chapter_num, volume_num, is_volume_end FROM chapters ORDER BY chapter_num DESC LIMIT 1,is_volume_end=1 即卷复盘;DTO 的 卷 取该章 volume_num(替换原 Math.floor(maxChapter/卷规模))。卷规模;它只由 book-status(全书近况"本卷 24/40 章(参考)")与细纲提议消费——实现时核对 book-status.js 的 当前卷 计算改用 MAX(volume_num),不得用章号除法。meta 表:CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT);键 last_health_check_chapter。派生物语义:删缓存后键消失 → 视为 0 → 到期即重测,无害。maxChapter - last_health_check >= 体检周期(体检周期 来自 book.yaml,默认 50——字段已入 spec 0.9 §3)。runHealthCheck):汇总既有查询(悬了太久清单、条目活跃率、连续弱钩计数)→ 写 工作区/体检报告.md(文体指纹/高频意象/句式小节输出"随 M5.5 落地"占位说明)→ meta 记录 last_health_check_chapter = maxChapter。零 token、needsAI=false,与序 5 的"脚本项"定位一致。CLI 动词 health-check(脚本命令不属 F1 的 AI 通道问题,与 prepare-chapter/mechanical-check 同类)。detectParseFailures 在既有六目录 front matter 扫描外补四类(spec 0.9 §10 清单,实现不得自行增减):
| 目标 | 判定 | 缺失时 |
|---|---|---|
book.yaml |
BookConfigReader.read() 不 ok → failure |
缺失不算失败(序 1 建书态负责) |
文风/文风铁律.md |
有文件且 front matter 解析失败 → failure | 缺失跳过(文风可选) |
定稿/设定/名册.md |
parseMarkdownTable 不 ok → failure |
缺失跳过(名册非必需,与重建器口径一致) |
定稿/设定/时间线/*.md |
逐文件 parseMarkdownTable 不 ok → failure |
目录缺失跳过 |
与重建器的分工:重建器对名册/时间线解析失败是软跳过 + warning(不阻断重建),序 0 是作者面对的修复确认——两者互补,口径不冲突。index.js 序 4-6 段的 config.ok || 默认值 兜底保留为纯防御(序 0 拦截后正常路径不会走到)。
数据源:草稿 front matter 的 伏笔/悬念/感情线 块列表,行格式 动词 ID(spec 0.9 §4.1)。三条规则,全部查 threads 表、零语义:
悬念 数组里出现 伏笔-031 → issue)。severity=high、blocking=true(确定性可数项,打回写稿不打扰作者——与禁词同级)。行格式不合 动词 ID 的行报 front matter 格式 issue(复用第 6 项检查的归属)。
rebuilder 把 secrets.short_title 从文件名第三段起解析(信息差-021-灭门真凶 → 灭门真凶;无短题回落 id)。SecretReader.listUnrevealed() 返回 {id, 短题, 知情人[], 关键词[]}(缓存直出);新增 readContentFirstLine(id)(精准片段:## 内容 首行)。- 信息差-021(灭门真凶):知情人=大长老、神秘老者;关键词=大长老/灭门/血书;内容:灭门真凶是大长老。——读者未知,除知情人的对话与视角外不得出现。拟条目变动:解析草稿 front matter 三数组 → [{type, verb, id}](与 C1 同一解析函数,放 util/,机检与 review 共用、不双写)。相关条目:现有元数据基础上,对草稿声明涉及的条目附 履历尾部(ThreadLedgerReader 读 ## 履历 末 3 行);未被声明但 status=进行 的条目维持纯元数据(控制 token)。roles/事实审查.md 输入清单行改为:章号、草稿全文、本章要写到的事、全书近况、相关角色(境界/状态/位置/持有)、相关条目(含拟变动与近期履历)、时间线片段、信息差候选。(evidence 维度的核对对象在 M5 F1 接线后扩展为拟履历行——本任务不改 evidence 维度定义文字。)| 风险 | 处置 |
|---|---|
| 缓存 schema 变更 | schema 版本号 +1,ensureReady 检测不匹配即全量重建(确认既有机制支持;不支持则加版本检查——实现第一步核实) |
收卷: 是 被 YAML 解析为布尔的方言差 |
重建器同时接受 '是'/true;序列化侧防呆方言不输出裸 是 以外形态 |
| fixtures 路径变更牵连测试面 | 先改 fixtures + 全量跑测,用失败清单定位牵连点(测试是探针不是约束) |
book-status 若按章号除法推当前卷 |
改用 MAX(volume_num);有测试锁行为 |
| roles 变更影响壳生成 | 跑 build-host-shells --check 与 validator;dist 不提交 |
四个主题 commit(见 implement.md),任一主题可独立 revert;缓存 schema 回滚 = revert 后删 .cache 重建,无持久迁移。