每条格式:[severity] file:line 一句话问题 | 怀疑理由 | 建议探针 | 置信
src/migrate/transform.js:117 + src/cache/rebuilder.js:257
e.type(中文「角色」)原样写进名册「类型」列;rebuilder const type = row.类型 || 'character' 原样入 entities.type,而 review 名册 / list-characters / report-book-stats / book-status 全部按 WHERE type = 'character'(英文)过滤 → 迁移书的角色以 type='角色' 入库,对上述四处全部不可见。read-v6.js:85 type: r.type || '角色'、transform.js:123 if (e.type !== '角色') continue、测试 _v6.js:51 INSERT entities VALUES('jiangyao','角色',...) 三处确证 migrate 子系统用「角色」;消费端 review/index.js:56、storage/adapters/EntityReader.js:107、commands/report-book-stats.js:10、prep/book-status.js:25 全按 'character';rebuilder.js:38 scanEntities(plain INSERT,type=角色)先于 :47 scanCharacters,后者 :291 ON CONFLICT(id) DO UPDATE 未含 type → 角色卡的 'character' 不会覆盖已入库的「角色」。sample-book fixture 用英文 character 恰好掩盖此漂移;test/migrate/e2e.test.js 只查 COUNT(*) FROM chapters 与 next 序号,从不查 entities.type / list-characters。tempV6Sqlite 后 SELECT type FROM entities WHERE id='江遥'(预期 '角色');再进程内跑 list-characters(预期空数组)、report-book-stats(预期角色数 0)。test/commands/_helper.js:77-86
git init + git add -A + commit。真实建书 persistCreateBook(state-machine/persist.js:60,66)写 .gitignore(含 .cache/、工作区/)、git.setQuotepathFalse()、只 git.add(written)(仅 book.yaml/大纲/AGENTS/.gitignore)。→ 测试仓库把 工作区/细纲.md 纳入 git 跟踪、无 core.quotepath;真实仓库 工作区/ 未跟踪。goto-chapter 的 reset --hard(state-machine/flows/goto-chapter.js:57)回退全部已跟踪文件——测试仓库(工作区被跟踪)会删/回退 工作区草稿,真实仓库(工作区未跟踪)保留,行为相反;M6「批次进行中 × goto-chapter」交叉的关键不变量(待定稿批次在 工作区/ 应免于 reset)无法用此脚手架如实验证。finalize 工作区清理、relink 的 status 前缀过滤在两种仓库形态语义不同。git ls-files | grep 工作区 对比;在两种仓库跑 goto-chapter --confirm 后查 工作区/细纲.md 是否仍在。test/integration/cli-main-loop.test.js:19 + scripts/pack-install-e2e.mjs
cache.close() finally、exitCode 约定对这 8 命令未端到端验证。spawn|execFile.*webnovel-writer|bin/webnovel-writer 仅命中 finalize(git)/installer/cli-main-loop;test/migrate/e2e.test.js:8-9 走 migrateV6/migrateCmd 进程内;cli-main-loop 只覆盖 M2-M5 主循环(next/persist-book/list-books/prepare/mechanical-check/review-input/save-review/finalize/switch-book);pack-install-e2e 只覆盖 init/persist-book/next/update。execFile(BIN,[...]) 冒烟,验 scope='book'(export/batch)与 scope='workdir'(migrate)在真实 cwd 下解析 + exitCode。..,与 stage-chapter 守卫不一致src/finalize/index.js:142-144 + src/commands/finalize.js:20-24
工作区/ 前缀(finalize.js:21 replace(/^工作区[\\/]/,'')),不挡 ..;finalizeChapter 直接 fs.rm(path.join(repoPath,'工作区',wf)),wf='../定稿/正文/0001-x.md' 会逃逸删到工作区外。stage-chapter(staging/index.js:331 if (!name.includes('..')))对同类清理有守卫,两路不一致。workspaceFiles:['../定稿/正文/0001-...md'],观察该定稿文件是否被删(git status 出现 deleted)。test/fixtures/sample-book/定稿/设定/名册.md:3-4 + SKILL.md:39
character(作者面向中文文件里的机器味,历史模式10),系统内部 entities.type 词表确为英文 'character';但 grep roles/ 无名册类型指引、SKILL.md rosterUpserts 未定义「类型」取值 → 手写书由 AI 自由填,若循全中文基调填「角色」则同样触发 G-2 的不可见问题。EntityWriter.upsertRosterRow 原样写入不校验。EntityWriter.js:8 ROSTER_HEADERS 含「类型」但无值域约束;消费端硬编码 'character'。v7/docs/migration-guide.md + package.json:12-19 + src/installer/vendor.js:11
files 白名单(['bin/','src/','roles/','skills/','adapters/','templates/'],无 docs/),也不在 vendored RUNTIME_ENTRIES(['bin','src','roles','package.json'])→ npx 装完、工作目录都拿不到;且 grep migration-guide 仅命中文件自身,无 SKILL/roles/migrate 输出引用(非断链)。migrate 输出指向的是 工作区/迁移报告.md(另一份,正常)。npm pack 解包看 docs/ 是否在 tar;确认该指引是否本就仅供仓库 README 引流。../ 可派发到 commands 目录外、报错人话降级bin/webnovel-writer.js:106-108
commandPath = path.join(__dirname,'../src/commands',${command}.js) 后 import,command 含 ../ 经 path.join 归一后会 import 到 commands 外模块(如 ../runtime/locate → src/runtime/locate.js);因目标无 .run 导出,落到 catch 的「执行命令时出错:mod.run is not a function」而非「未知命令」,人话降级。webnovel-writer ../runtime/locate 看报错文案与 exitCode(预期非「未知命令」)。src/commands/next.js:16
--json 路径直接 r.gitHealth.fixed.length;若 determineNextState 在序1(空工作目录/无 book,allowNoBook 下 ctx.repoPath=null)返回不含 gitHealth 的对象,next(无 --json)会抛 "Cannot read length of undefined"。cli-main-loop 只测了空工作目录的 --json 路径(不碰 gitHealth)。.webnovel/ 工作目录里跑 next(不带 --json),看是否抛未定义。bin/webnovel-writer.js:31
精准读取接口(41 个,分布于 21 个命令) 是硬编码文案,与实际读接口数无测试绑定(历史模式7 双源)。命令清单本身三方无漂移(48 个命令文件全部在 --help;SKILL 引用为合理子集);「21 个命令」已核对准确(read/list/report 类命令恰 21)。| 项 | 位置 | 结论 |
|---|---|---|
| bin cache.close() finally 覆盖 | bin:150-152 |
cache 仅在 book/workdir-book 分支赋值,ensureReady 抛错前已赋值,finally 恒关;workdir/anywhere/no-book 分支不建 cache。在位 |
| installer 新清单以旧为底防丢 | installer/index.js:71 newFiles={...manifest.files} |
在位 |
| persistCreateBook git init+quotepath+ensureIdentity+随手 commit | state-machine/persist.js:68-77 |
在位(P0-2) |
| 薄壳契约「不碰 console/process」 | grep commands/ `console. | process.(exit|argv|cwd|stdout|stderr)` |
| init/update 将 report 映射为 output | init.js:13/update.js:13 |
正确映射,报告可打印。无 G-hypothesis 的静默 bug |
| vendored 运行时含 migrate/export 新模块 | vendor.js:11 RUNTIME_ENTRIES 含 'src' 整目录拷 |
天然含,migrate/export/staging 均在 .webnovel/src/ |
| host-shells drift/条件块渲染 | host-shells/generate.js:26-34 |
renderTemplate 处理 {{#if}}/{{#unless}}/{{var}};SKILL 新增自动模式/例外段无条件块(纯 {{cmd}} 插值),driftCheck 双跑字节比对确定性守住。无漂移 |
| scope 分级逐命令 | 见下 | 无「该 workdir 却缺省成 book」误判 |
anywhere: init(装 .webnovel/ 前要能跑)✓workdir: list-books / update / switch-book / session-context / migrate(建新书,用 ctx.workdir)✓workdir-or-book: persist-book(建书时书目录还不存在)✓allowNoBook=true: next(空工作目录报序1)✓rebuilder.js:289-298 逐行确认。--version/--help/未知命令三路 exitCode 与错误人话(不带栈)均合规。