实现 v7 的格式层基础设施,使系统能够可靠地读写 story repo 源文件,并通过缓存支撑精准读取。交付四层能力(依赖链):① 容错读写库 → ② Storage Adapter 小端口 → ③ .cache/index.db 五表+重建器 → ④ 41 精准读取接口 CLI。出口:删光 .cache 全量重建测试绿 + 精准读取 41 接口逐条测试绿。
上游就绪:
v7/src/storage/index.js、v7/src/cache/index.js 占位已存在范围决策(2026-06-27):
R1.1 Front matter 读写
--- 包裹),提取结构化字段R1.2 平铺 YAML 与 book.yaml
book.yaml 的平铺字段(spec §3)- 项,禁 [a,b])true/null 等易误判的字符串)R1.3 时间线 Markdown 表格
定稿/设定/时间线/第NN卷.md 的 Markdown 表(| 章 | 书内时间 | 一句话事件 | 在场 |)R1.4 名册解析
定稿/设定/名册.md 表(| 正名 | 别名 | 类型 | 首现章 |)禁止上帝对象:不做 20 方法的 StoryRepo 大类,拆成职责最小的独立端口:
ChapterReader:读章节 front matter、正文、指定范围ChapterWriter:写新章到定稿(M2 调用,本任务只定接口)ThreadLedgerReader:读三类条目(伏笔/悬念/感情线)基本信息、履历、收尾计划ThreadLedgerWriter:更新条目状态、追加履历(M2 调用,本任务只定接口)EntityReader:读角色卡、解析别名、列出实体TimelineReader:读指定卷时间线、按在场过滤SecretReader:读信息差基本信息、内容OutlineReader:读总纲/卷纲指定小节BookConfigReader:读 book.yaml每个端口只暴露 2-5 个方法,调用方按需依赖(权限最小化)。
.cache/index.db 五表与重建器R3.1 五表 DDL(O4 §1,表名英文、内容中文)
chapters:章 front matter 展开(章号/标题/卷/视角/章定位/钩子/情绪定位/字数/file_path)threads:三类条目统一存放(id/type/short_title/strength/status/opened_chapter/planned_end/last_advanced_chapter/file_path)secrets:信息差(id/short_title/known_to JSON/reader_knows/registered_chapter/keywords JSON/file_path)entities:名册(id 即正名/type/status/location/realm/possessions JSON/last_changed_chapter/file_path)entity_aliases:别名表(alias PK/entity_id FK)fingerprints:文体指纹(chapter_range_start+end 复合 PK/is_baseline/常用特征列/fingerprint_data JSON)索引:按 O4 §1 各表建必要索引(volume_num、type、status、reader_knows、is_baseline 等)
R3.2 重建器(node:sqlite,O4 §4)
定稿/、大纲/、文风/(不读工作区)、book.yaml.cache/index.db 不存在或损坏时R3.3 派生值策略(O4 §0 原则 5)
当前最大章号 − last_advanced_chapter)、信息差蓄积章数、是否超期 → 查询时算命令式 API:每个接口是一个明确的脚本命令,可在 CLI 调用、AI 友好、默认精准、统一 JSON/文本输出。
P0(8 个,写章流程依赖):
P1(7 个,机检与全书近况):
P2(26 个,AI 角色与自动模式优化):
完整清单见 O4 §2.2-2.9 各表(本 PRD 不重复列全部参数)。
C1 零第三方依赖(质量规范 §1.1)
node:sqlite(内置)node:test + node:assert(内置)js-yaml(MIT、零传递依赖、YAML 规范复杂不适合手写);序列化手写(简单、可控)C2 容错与防呆(不变量 1/9,数据规范 §4)
C3 编码与平台(质量规范 §3)
0152-、伏笔-031),不依赖中文字典序C4 职责分界(质量规范 §2.1)
C5 错误处理(错误规范 §1)
AC1 删光 .cache 全量重建(不变量 2)
.cache/index.db 不存在时,首次查询触发全量重建.cache/ 后再次查询,行为不变(可重建)AC2 41 精准读取接口逐条测试
AC2 权威 41 接口清单(对齐 O4 §2.2-2.9,命令层测试逐条核对此表):
| # | 分类 | 接口 | 命令 |
|---|---|---|---|
| 1 | 条目 | 读条目基本信息 | read-thread <id> --fields=基本信息 |
| 2 | 条目 | 读条目履历 | read-thread <id> --履历 |
| 3 | 条目 | 读条目收尾计划 | read-thread <id> --收尾计划 |
| 4 | 条目 | 读条目描述 | read-thread <id> --描述 |
| 5 | 条目 | 列出悬了太久 | list-threads --悬了太久 |
| 6 | 条目 | 列出某类型 | list-threads --type=<t> [--status=<s>] |
| 7 | 条目 | 按强度筛选 | list-threads --strength=高 |
| 8 | 大纲 | 读总纲指定小节 | read-outline --总纲 --section=<标题> |
| 9 | 大纲 | 读总纲结局段 | read-outline --总纲 --结局 |
| 10 | 大纲 | 读卷纲全文 | read-outline --卷=<N> |
| 11 | 大纲 | 读卷纲指定小节 | read-outline --卷=<N> --section=<标题> |
| 12 | 大纲 | 列出所有卷纲 | list-volumes |
| 13 | 正文 | 读章节 front matter | read-chapter <num> --front-matter |
| 14 | 正文 | 读章节正文 | read-chapter <num> |
| 15 | 正文 | 读章节结尾 N 字 | read-chapter <num> --tail=<N> |
| 16 | 正文 | 读章节开头 N 字 | read-chapter <num> --head=<N> |
| 17 | 正文 | 读章摘要 | read-chapter <num> --摘要 |
| 18 | 正文 | 读章节范围摘要 | read-chapters --range=<a>-<b> --摘要 |
| 19 | 正文 | 读近 N 章结尾 | read-chapters --recent=<N> --tail=<M> |
| 20 | 正文 | 按章定位筛选 | list-chapters --章定位=推进 [--卷=<N>] |
| 21 | 时间线 | 读当前卷 | read-timeline --current-volume |
| 22 | 时间线 | 读当前卷+上一卷 | read-timeline --current-and-prev |
| 23 | 时间线 | 读指定卷 | read-timeline --卷=<N> |
| 24 | 时间线 | 按在场筛选 | read-timeline --在场=<角色名> |
| 25 | 设定 | 读角色卡 front matter | read-character <name> --front-matter |
| 26 | 设定 | 读角色卡完整 | read-character <name> |
| 27 | 设定 | 读角色卡指定小节 | read-character <name> --section=<标题> |
| 28 | 设定 | 解析别名 | resolve-alias <别名> |
| 29 | 设定 | 列出所有角色 | list-characters [--status=<s>] |
| 30 | 设定 | 读世界观指定小节 | read-worldview --section=<标题> |
| 31 | 信息差 | 读信息差基本信息 | read-secret <id> --基本信息 |
| 32 | 信息差 | 读信息差内容 | read-secret <id> --内容 |
| 33 | 信息差 | 列出未揭晓 | list-secrets --reader-knows=false |
| 34 | 检索 | 关键词全文检索 | grep-story <关键词> |
| 35 | 检索 | 正则检索 | grep-story --regex=<pattern> |
| 36 | 报表 | 悬了太久清单 | report-overdue-threads |
| 37 | 报表 | 信息差蓄积 | report-secret-accumulation |
| 38 | 报表 | 文体漂移(M1 边界:读表对比基线,不做特征提取) | report-style-drift |
| 39 | 报表 | 条目活跃率 | report-thread-activity --卷=<N> |
| 40 | 报表 | 连续弱钩计数 | report-weak-hook-streak |
| 41 | 报表 | 全书统计摘要 | report-book-stats |
合计 7+5+8+4+6+3+2+6 = 41,分布于 21 个命令文件(多接口共享文件,靠 --选项 分流)。
AC3 容错读取(不变量 9)
自定义字段: 值 的章节文件,读→改标题→写,自定义字段不丢失AC4 防呆写出(数据规范 §4)
- 项)"123" 不误判数字、"true" 不误判布尔)AC5 Windows 中文路径全链路(质量规范 §3.2)
AC6 章节读取
read-chapter <num> --front-matter:返回 JSON 含章号/标题/卷/视角等字段read-chapter <num> --tail=500:返回正文末尾 500 字(不含 front matter)AC7 条目读取
read-thread <id> --fields=基本信息:返回强度/状态/开启章/预计收尾read-thread <id> --履历:返回履历列表(按章号排序)list-threads --悬了太久:计算 当前最大章号 − last_advanced_chapter,返回超过阈值的条目(阈值从 book.yaml 读取)AC8 别名解析
resolve-alias <别名>:返回正名AC9 报表生成
report-overdue-threads:按类型分组返回悬了太久的条目清单report-book-stats:返回总章数/总字数/条目数/角色数report-weak-hook-streak:返回末尾连续弱钩章数AC10 重建器校验
AC11 小端口分离(架构原则 §1.5)
storage/ 导出至少 8 个独立 reader 端口(ChapterReader / ThreadLedgerReader / EntityReader / TimelineReader / SecretReader / OutlineReader / BookConfigReader + 未来 Writer 占位)AC12 测试镜像 src
v7/test/storage/ 与 v7/src/storage/ 镜像v7/test/cache/ 与 v7/src/cache/ 镜像*.test.jsfingerprints 表建好但留空,特征提取随 M3+ 体检/卷复盘补);report-style-drift 接口存在、能读表并对比基线,但 M1 不实现特征提取函数无阻断问题。以下为实施细节,进入 design 阶段决策:
js-yaml(零依赖、MIT)vs 手写轻量解析器?v7/test/fixtures/ 放示例书仓库?bin/commands/*.js 还是按类型分组子目录?fingerprints 表在 M1 只建表结构 + 占位查询接口,特征提取函数留桩——这个边界在 design 明确