Procházet zdrojové kódy

chore(task): archive 07-02-v7-boundary-code-fixes

lingfengQAQ před 10 hodinami
rodič
revize
568d3cb48c

+ 1 - 0
.trellis/tasks/archive/2026-07/07-02-v7-boundary-code-fixes/check.jsonl

@@ -0,0 +1 @@
+{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

+ 89 - 0
.trellis/tasks/archive/2026-07/07-02-v7-boundary-code-fixes/design.md

@@ -0,0 +1,89 @@
+# design.md — v7 边界收口:格式对齐与状态机判定修复
+
+> 依据:本任务 prd.md + spec 0.9(决策 27-32)。模块现状引用以 2026-07-02 代码为准(commit 6b312a4 起算,文档回填 871c448 不含代码)。
+
+## 1. 改动总览(按层)
+
+| 层 | 文件 | 改动 |
+|---|---|---|
+| 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 生成器。
+
+## 2. 关键设计
+
+### 2.1 收卷链路(B1)
+
+- **数据流**:细纲提案(AI,含收卷提议)→ 作者确认 → 草稿/定稿包 front matter `收卷: 是` → `ChapterWriter` 原样写出 → 定稿后缓存重建 `is_volume_end=1` → 下次 `next` 序 4 命中。
+- **解析宽容**:js-yaml 会把 `是` 解析为字符串 `"是"`;重建器接受 `收卷 === '是' || 收卷 === true`,其余值视为未收卷(不报错——非清单字段错误不属序 0)。
+- **序 4 判定**:`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)`,不得用章号除法。
+
+### 2.2 体检记录与最小体检(B2)
+
+- **`meta` 表**:`CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT)`;键 `last_health_check_chapter`。派生物语义:删缓存后键消失 → 视为 0 → 到期即重测,无害。
+- **序 5 判定**:`maxChapter - last_health_check >= 体检周期`(`体检周期` 来自 book.yaml,默认 50——字段已入 spec 0.9 §3)。
+- **最小体检 use case**(`runHealthCheck`):汇总既有查询(悬了太久清单、条目活跃率、连续弱钩计数)→ 写 `工作区/体检报告.md`(文体指纹/高频意象/句式小节输出"随 M5.5 落地"占位说明)→ `meta` 记录 `last_health_check_chapter = maxChapter`。零 token、needsAI=false,与序 5 的"脚本项"定位一致。CLI 动词 `health-check`(脚本命令不属 F1 的 AI 通道问题,与 prepare-chapter/mechanical-check 同类)。
+
+### 2.3 序 0 扫描清单(B3)
+
+`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 拦截后正常路径不会走到)。
+
+### 2.4 条目变动形式检查(C1)
+
+数据源:草稿 front matter 的 `伏笔/悬念/感情线` 块列表,行格式 `动词 ID`(spec 0.9 §4.1)。三条规则,全部查 `threads` 表、零语义:
+
+1. **类型一致**:ID 前缀须与所在数组类型一致(`悬念` 数组里出现 `伏笔-031` → issue)。
+2. **开启类动词**(埋下/设下/开启):ID 不得已存在(撞已有 ID → issue;新 ID 合法——条目文件由定稿创建)。
+3. **非开启动词**(推进/回收/揭晓/放弃/修成正果/无疾而终):ID 必须存在且状态=进行(不存在 → 疑似 AI 编造;状态≠进行 → 推进已收尾条目)。
+
+severity=high、blocking=true(确定性可数项,打回写稿不打扰作者——与禁词同级)。行格式不合 `动词 ID` 的行报 front matter 格式 issue(复用第 6 项检查的归属)。
+
+### 2.5 信息差边界注入(C2)
+
+- `rebuilder` 把 `secrets.short_title` 从文件名第三段起解析(`信息差-021-灭门真凶` → `灭门真凶`;无短题回落 id)。
+- `SecretReader.listUnrevealed()` 返回 `{id, 短题, 知情人[], 关键词[]}`(缓存直出);新增 `readContentFirstLine(id)`(精准片段:`## 内容` 首行)。
+- 备料输出行模板:`- 信息差-021(灭门真凶):知情人=大长老、神秘老者;关键词=大长老/灭门/血书;内容:灭门真凶是大长老。——读者未知,除知情人的对话与视角外不得出现`。
+
+### 2.6 ReviewInput 补料(C3)
+
+- `拟条目变动`:解析草稿 front matter 三数组 → `[{type, verb, id}]`(与 C1 同一解析函数,放 `util/`,机检与 review 共用、不双写)。
+- `相关条目`:现有元数据基础上,对草稿声明涉及的条目附 `履历尾部`(`ThreadLedgerReader` 读 `## 履历` 末 3 行);未被声明但 status=进行 的条目维持纯元数据(控制 token)。
+- `roles/事实审查.md` 输入清单行改为:`章号、草稿全文、本章要写到的事、全书近况、相关角色(境界/状态/位置/持有)、相关条目(含拟变动与近期履历)、时间线片段、信息差候选。`(evidence 维度的核对对象在 M5 F1 接线后扩展为拟履历行——本任务不改 evidence 维度定义文字。)
+
+## 3. 兼容与风险
+
+| 风险 | 处置 |
+|---|---|
+| 缓存 schema 变更 | schema 版本号 +1,`ensureReady` 检测不匹配即全量重建(确认既有机制支持;不支持则加版本检查——实现第一步核实) |
+| `收卷: 是` 被 YAML 解析为布尔的方言差 | 重建器同时接受 `'是'`/`true`;序列化侧防呆方言不输出裸 `是` 以外形态 |
+| fixtures 路径变更牵连测试面 | 先改 fixtures + 全量跑测,用失败清单定位牵连点(测试是探针不是约束) |
+| `book-status` 若按章号除法推当前卷 | 改用 `MAX(volume_num)`;有测试锁行为 |
+| roles 变更影响壳生成 | 跑 `build-host-shells --check` 与 validator;dist 不提交 |
+
+## 4. 回滚
+
+四个主题 commit(见 implement.md),任一主题可独立 revert;缓存 schema 回滚 = revert 后删 `.cache` 重建,无持久迁移。

+ 1 - 0
.trellis/tasks/archive/2026-07/07-02-v7-boundary-code-fixes/implement.jsonl

@@ -0,0 +1 @@
+{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

+ 55 - 0
.trellis/tasks/archive/2026-07/07-02-v7-boundary-code-fixes/implement.md

@@ -0,0 +1,55 @@
+# implement.md — v7 边界收口:格式对齐与状态机判定修复
+
+> 执行序:A 格式对齐 → B 状态机判定 → C 缺料补齐 → D 收口审计。每阶段末跑全量测试并 commit(回滚点)。验证一律 `cd v7 && node --test`(Windows 本机即双平台之一,CI 补另一平台)。
+
+## 阶段 A:格式对齐(fix(v7): 格式对齐 spec 0.9——卷纲子目录/卷摘要文件名/信息差字段)
+
+- [x] A0 前置核实:`grep -rn "大纲.*第.*卷\|卷摘要\|谁知道\|读者知道" v7/src v7/test` 列全牵连点(含 `book-status.js` 是否有卷纲/卷规模耦合)
+- [x] A1 卷纲路径 → `大纲/卷纲/第NN卷.md`:
+  - `OutlineReader.readVolumeOutline` / `listVolumes`(目录与正则)
+  - `persist.js` `persistCreateBook`(`大纲/卷纲/第01卷.md`)与 `persistVolumeReview`
+  - fixtures:`test/fixtures/sample-book/大纲/第01卷.md` 移入 `卷纲/`
+- [x] A2 卷摘要 → `第NN卷.md`:`persistVolumeReview`(及 SummaryWriter 若涉卷摘要)、persist 测试断言
+- [x] A3 信息差字段 → `知情人`/`读者已知`:`rebuilder.scanSecrets`、`SecretWriter`(含 JSDoc)、fixtures `信息差-001`、`SecretReader/SecretWriter/read-secret/mechanical-check` 测试
+- [x] A4 全量测试绿 + 重建用例绿;commit
+
+## 阶段 B:状态机判定(fix(v7): 状态机判定——收卷声明制/体检距上次/序0清单/序3细纲)
+
+- [x] B0 前置核实:`schema.js` 的版本/重建触发机制(`ensureReady` 对 schema 不匹配的行为);不支持版本检测则先补
+- [x] B1 收卷声明制:
+  - `schema.js`:`chapters` 加 `is_volume_end INTEGER DEFAULT 0`;新增 `meta(key TEXT PRIMARY KEY, value TEXT)`;版本 +1
+  - `rebuilder.scanChapters`:`收卷 === '是' || === true` → 1
+  - `state-machine/index.js` 序 4:改读最新章 `is_volume_end`;DTO `卷` 取该章 `volume_num`
+  - `dto.js` 序 6 期望产物:补"提案可含收卷提议(依据卷纲进度与卷规模参考值)"
+  - `book-status.js`:当前卷改 `MAX(volume_num)`(若现为章号除法)
+  - 测试:AC2 正反用例(带收卷 → 序 4;第 40 章无收卷 → 不触发)
+- [x] B2 体检:
+  - 序 5 判定 `maxChapter - last_health_check >= 体检周期`(meta 表读)
+  - 新增 `src/health-check/index.js`(汇总 悬了太久/条目活跃率/连续弱钩 → `工作区/体检报告.md`;文体小节占位"随 M5.5";写 meta)+ `src/commands/health-check.js` + bin --help 补行
+  - 测试:AC3(触发窗口、执行后不再触发、删缓存后重测不报错)
+- [x] B3 序 0 清单:`detectParseFailures` 补 book.yaml / 文风铁律 / 名册 / 时间线(缺失跳过规则见 design §2.3);测试注入四类损坏样本(AC4),并断言 book.yaml 损坏时不再走默认值路径
+- [x] B4 序 3:`hasUnfinishedWork` 计入 `细纲.md`、`本章写作材料.md`;测试 AC5(仅细纲 → 序 3 且文件未被改写)
+- [x] B5 全量测试绿;commit
+
+## 阶段 C:缺料补齐(feat(v7): 机检条目形式检查/备料信息差边界/ReviewInput 补料)
+
+- [x] C0 共用解析:`util/` 新增 front matter 条目变动解析(`[{type, verb, id}]`),机检与 review 共用
+- [x] C1 机检:`checkThreadDeclarations`(design §2.4 三规则,severity=high/blocking=true);测试 AC6 三类用例
+- [x] C2 备料:`rebuilder` 补 `secrets.short_title` 短题解析;`SecretReader.listUnrevealed` 扩返回 + `readContentFirstLine`;`prep` 输出行按 design §2.5 模板;测试 AC7 前半
+- [x] C3 ReviewInput:拟条目变动 + 声明条目附履历尾 3 行(`ThreadLedgerReader`);`roles/事实审查.md` 输入清单行更新;跑 `build-host-shells --check` + validator;测试 AC7 后半
+- [x] C4 全量测试绿;commit
+
+## 阶段 D:收口审计(chore/fix 视内容)
+
+- [x] D1 AC1 全库审计:`grep -rn "谁知道\|读者知道" v7/` 零命中(references 题材模板里的"知情人"文案不属此列);卷纲扁平路径零命中
+- [x] D2 AC8:全量 `node --test`;删 `.cache` 全量重建用例;drift check;package validator
+- [x] D3 `git push` 前看 CI 双平台矩阵结果;PRD 验收项逐条勾选
+- [x] D4 若实现中发现与 spec 0.9 冲突处:停下修 spec(决策记录 33+)再继续——不得代码侧自行发挥
+
+## 验证命令速查
+
+```bash
+cd v7 && node --test                                   # 全量
+node --test test/state-machine/ test/cache/           # 判定与缓存
+node scripts/build-host-shells.mjs --check            # drift check
+```

+ 54 - 0
.trellis/tasks/archive/2026-07/07-02-v7-boundary-code-fixes/prd.md

@@ -0,0 +1,54 @@
+# v7 边界收口:格式对齐与状态机判定修复
+
+> 来源:2026-07-02 设计边界回顾(作者逐项确认)。文档侧裁决已由 `07-02-v7-boundary-doc-backfill` 落进 spec 0.9 / PRD 1.1 / multi-agent v3.5 / 实施计划 0.3(commit 871c448)——**本任务以 spec 0.9 为依据改代码**。
+> 定位:M5 安装器的前置(实施计划 0.3 §0 已登记)。deep review 的判断同样适用:"否则 M5 安装器接一个跑不通的循环"。
+
+## Goal
+
+消除"格式法律文本 ↔ 参考实现"漂移中该改代码的一侧(3 项),修复状态机三个判定边界 bug(含收卷声明制落地),补齐机检/备料/ReviewInput 三处缺料。九项全部对齐 spec 0.9。
+
+## Requirements
+
+### A. 格式对齐(spec 0.9 §2.1/§4.3/§4.6,改代码从 spec)
+
+- A1 **卷纲路径**:`大纲/第NN卷.md` → `大纲/卷纲/第NN卷.md`。涉及 `OutlineReader`(读取与 `listVolumes` 正则)、`persist.js`(`persistCreateBook`/`persistVolumeReview`)、测试 fixtures、相关测试。
+- A2 **卷摘要文件名**:`定稿/摘要/卷摘要/NN.md` → `第NN卷.md`(与时间线、卷纲同形态)。
+- A3 **信息差字段名**:`谁知道`/`读者知道` → `知情人`/`读者已知`(spec §4.3)。涉及 `rebuilder.js`、`SecretWriter`/`SecretReader` 及注释、fixtures、机检测试。无存量用户数据,直接切换、不做旧字段兼容读取。
+
+### B. 状态机判定(spec 0.9 §10)
+
+- B1 **收卷声明制**(决策 27):序 4 判定由 `maxChapter % 卷规模 === 0` 改为"最新定稿章 front matter `收卷: 是`"。链路:`chapters` 缓存表加收卷列(重建器填充;缓存可删重建,无迁移问题)→ `detectors`/状态机改判定 → 序 6 细纲 DTO 的期望产物文案补"可含收卷提议(依据卷纲进度与卷规模参考值)"。
+- B2 **序 5 体检判定**(决策 29):`% 体检周期 === 0` 改"距上次体检 ≥ 体检周期";上次体检章号存 `.cache`(丢失重测无害,符合派生物语义)。**必须配最小体检执行点**,否则新语义下序 5 自第 50 章起每次启动命中、主循环卡死:新增 `health-check` 脚本命令(零 token),汇总既有报表(悬了太久、条目活跃率、连续弱钩)落 `工作区/体检报告.md` 并记录上次体检章号;文体统计项标注"随 M5.5"。
+- B3 **序 0 源文件清单**(决策 28):`detectParseFailures` 按 spec 清单补扫 `book.yaml`、`文风/文风铁律.md` front matter、`定稿/设定/名册.md` 表、`定稿/设定/时间线/` 表;解析失败进序 0 修复确认,不再静默降级为默认值。
+- B4 **序 3 未完成流程**(决策 29):`hasUnfinishedWork` 把 `细纲.md`、`本章写作材料.md` 计入(对齐 spec 续跑映射表),已确认细纲不再被序 6 覆盖。
+
+### C. 缺料补齐(spec 0.9 §8)
+
+- C1 **机检补"条目变动形式检查"**(§8 第 5 步;M2 D2 七项之一,漏做):草稿 front matter 声明的条目变动做两条纯形式校验——①声明的条目 ID 在 `threads` 表存在("埋下"相反:要求 ID 不存在);②生命周期动词与条目当前状态兼容(推进/回收/揭晓等要求状态=进行)。查表即可,不判语义。
+- C2 **备料信息差边界补内容**(§8 第 3 步;PRD §4 #3"注入出场人物知识边界"):每条未揭晓信息差由只给 ID 改为注入 短题+知情人+关键词+内容首句,并明示"除知情人的对话与视角外不得出现"。
+- C3 **ReviewInput 补 evidence 维度数据**(§8 第 6 步③):相关条目由纯元数据扩为附 履历尾部 N 行;新增"拟条目变动"(来自草稿 front matter 的 伏笔/悬念/感情线 声明)。`roles/事实审查.md` 输入清单同步补"相关条目(含拟变动与近期履历)"。边界说明:含证据引用的"拟履历行"起草与核对随 M5 F1 接线(`save-review`/`finalize` 通道),不在本任务。
+
+## Constraints
+
+- 依据只认 spec 0.9 文本;实现与 spec 再冲突时停下来改 spec(走决策记录),不得代码侧自行发挥。
+- 零新增运行时依赖;错误全中文、永不带堆栈(既有规范)。
+- 测试是探针不是约束:过时的路径/字段断言随格式对齐一并改;新判定逻辑各配正反用例。
+- roles 单源改动后跑 drift check / package validator(`v7/scripts/`),dist 生成物不提交。
+- Windows 中文路径 CI(双平台矩阵)保持绿。
+
+## Acceptance Criteria
+
+- [x] AC1 格式三项与 spec 0.9 一致:全库 grep 无扁平卷纲路径、无 `谁知道`/`读者知道`;卷摘要写出 `第NN卷.md`。fixtures 同步。
+- [x] AC2 收卷链路端到端:定稿一章带 `收卷: 是` → 重建缓存 → `next` 判序 4 卷复盘;不带收卷的第 40/80 章 → 不触发卷复盘(旧整除行为消失)。
+- [x] AC3 体检按"距上次体检 ≥ 周期"触发;体检记录随 `.cache` 删除后重测不报错。
+- [x] AC4 序 0 用例:损坏的 book.yaml / 文风铁律 / 名册 / 时间线 各注入一例 → `next` 返回序 0 修复确认;不再出现"静默用默认值"路径。
+- [x] AC5 序 3 用例:工作区仅存 细纲.md → `next` 返回序 3(续跑),细纲文件未被改写。
+- [x] AC6 机检条目形式检查:声明不存在 ID、动词与状态不兼容、"埋下"撞已有 ID 三类各有用例并报 issue。
+- [x] AC7 备料产物含信息差 短题/知情人/关键词/内容首句;ReviewInput 含拟条目变动与履历尾部(测试断言)。
+- [x] AC8 全量 `node --test` 绿;「删光 `.cache` 全量重建」用例绿;drift check + validator 绿。
+
+## Out of Scope
+
+- F1 宿主 CLI 缝(`next --json`/`review-input`/`save-review`/`persist-*`/`finalize` 命令)→ M5(实施计划 0.3 已列清单)。
+- 体检统计项实现(高频意象/句式体检/指纹提取)→ M5.5。
+- 拟履历行的起草流程 → M5 F1 接线。

+ 26 - 0
.trellis/tasks/archive/2026-07/07-02-v7-boundary-code-fixes/task.json

@@ -0,0 +1,26 @@
+{
+  "id": "v7-boundary-code-fixes",
+  "name": "v7-boundary-code-fixes",
+  "title": "v7 边界收口:格式对齐与状态机判定修复",
+  "description": "",
+  "status": "completed",
+  "dev_type": null,
+  "scope": null,
+  "package": null,
+  "priority": "P2",
+  "creator": "codex",
+  "assignee": "codex",
+  "createdAt": "2026-07-02",
+  "completedAt": "2026-07-03",
+  "branch": null,
+  "base_branch": "v7",
+  "worktree_path": null,
+  "commit": null,
+  "pr_url": null,
+  "subtasks": [],
+  "children": [],
+  "parent": null,
+  "relatedFiles": [],
+  "notes": "",
+  "meta": {}
+}