dsh-novel-write-engine 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.4
4
+
5
+ ### Changed
6
+
7
+ - **BREAKING:`novel_outline` 改为按标识 upsert**:`chapters` 按 `num`、`volumes` 按 `title` 合并;省略的卡保持不动,不再因“提交集合就是全集”而静默删除。删除必须显式 `remove` / `remove_volumes`,活动章、归档章、不存在的卡和已完成卷都会被拒绝。
8
+ - **可解释写入**:`novel_outline` 新增 `dry_run`(返回 `would_add`/`would_update`/`would_remove`,零写盘、零 journal)、`added`/`updated`/`removed` 变更回显、`outline/outline.deprecated.yaml` 一级回退副本;`novel_restore` 支持 `paths: ["outline/outline.yaml"]`、不要求 `chapter`。
9
+ - **工具契约**:持久集合的数组参数声明 upsert/replace/remove 语义;`novel_blueprint` 的卷列表仍是 replace,但删除卷必须显式 `confirm_remove:true`。DSH 的强制 JSON Schema 子集不接受未知 `x-semantics` 关键字,因此语义落在参数描述与 `src/tools/semantics.ts` 契约表中,并由 `tests/tool-contract.spec.ts` 强制。
10
+ - **文档**:`novel_outline` 描述明确“upsert by key”,P2 brief 增加“未提交卡保持”的提示,工具文档与升级说明同步。
11
+
12
+ ### Migration
13
+
14
+ - 老的**全量提交**调用在 upsert 下等价,无需改动。
15
+ - 老的**部分提交**以前会删掉未提交卡,现在只改提交的卡;这正是本次修正。
16
+ - 想清空计划卡:以前空数组可行,现在空数组是 no-op;必须显式 `remove: [3,4,...]`。数据格式不变,无需迁移文件。
17
+ - 同名卷会被新写入拒绝(upsert 以 `title` 为键,同名无法区分)。老项目只读加载仍兼容;若要提交 `volumes`/`remove_volumes`,先用 `novel_amend` 给其中一卷改名。
18
+
19
+ ### Tests
20
+
21
+ - 新增 `tests/024.spec.ts`(部分 upsert、显式删除、active/unknown/submitted/archived 拒绝、dry_run 零写入、deprecated、outline 恢复、volumes upsert/remove、blueprint confirm)与 `tests/tool-contract.spec.ts`。
22
+ - 质量:80 个测试文件 / 848 项测试全绿;覆盖率 93.97 / 89.14 / 98.00 / 93.97,逐文件门禁通过。
23
+
24
+ ## 0.2.3
25
+
26
+ ### Changed
27
+
28
+ - **报表账本**:`novel_report.effort` 对旧章按 committed journal 回填 `submitReview`/`submitRevision`/`runCheck`/`rollback` 与 `archived_at`,旧 `final_check` 从 check 文档补读;`quality` 改为每章一份最终评审,新增 `samples`(章数)、`attempts`(committed 评审轮次,含历史)与 `trend_basis`,样本不足时 `trend` 为 `null` 而不是伪造 0。
29
+ - **简报按章隔离**:`state/briefs.yaml` 仍是全书 append-only 审计,但 `pipeline.briefs` 与 `getBrief` 只取当前章收据;重复领取同一章同一阶段直接 replay,不写 journal、不升 revision;仍取项目写锁做 journal 一致性检查,避免误恢复其他写者的 pending 事务。
30
+ - **角色与伏笔**:旧 `extra` 角色可从 worldbook/P1 或叙述者修正;伏笔 `resolved` 同步写 `advance_log`,记录章节、note 与 new_info。
31
+ - **归档失败可直接修订**:`ready_to_archive` 增加 `submitRevision` 回 P4,不必先 `novel_rollback`;P5 任务书与所有 brief 暴露 `manuscript_hash`/patch 示例。
32
+ - **P8 契约**:brief 带最小 `state_delta` schema、每段示例和 `worldbook_facts_empty`;delta 错误回显 expected 形态;`ENGINE_ISSUE_IMMUTABLE` 区分“格式错”和“评审归属不可改”。
33
+ - **提示降噪**:advisory 按 code 每章一条并合并去重后的证据;`LOCKED_DETAIL_DRIFT` 带 expected/actual 片段,重复段带阈值;`RESOURCE_MISMATCH` 只在正文明确写反向符号时报,不再把中文省略符号当矛盾。strict 模式下 `facts` 为空时 doctor 提醒;同段“数量×单价/人数×单价”算式不一致会报 advisory;中文句密度上限校准为 10/100 字。
34
+ - **失败重试**:有 key 的写操作在 journal 记录 `retry_key`/`retry_context`;与上次被拒尝试内容、brief、stage、revision 完全相同时返回 `ENGINE_RETRY_UNCHANGED`,防止原样重试浪费事务;换 brief 或改内容后放行。
35
+ - **默认规则包 1.0.1**:`period_density.max` 8→10(中文短句校准),并加入同段乘式/人数×单价算式 advisory。
36
+ - **面板 v5**:紧凑顶栏、首屏“当前章/下一步”、双栏工作台、统计条;项目选择器按写作/待修复/失效分组,失效默认折叠、可搜索、同名用父目录区分;advisories 与 legacy 独立展示;payload v5,旧标签页刷新。
37
+ - **项目卫生**:`check:host` 同时隔离 `HOME`/`DSH_HOME`,用临时注册表正控证明 `novel_init` 确实写入了隔离行,并回查真实 `projects.yaml` 未新增 Host Smoke;新增 `/novel projects --prune [--yes]`,默认只列失效登记,`--yes` 才删登记行,不删项目目录。
38
+ - **升级审计**:doctor 新增 `legacy-data`,只读标出旧 check、缺 rounds/archived_at、extra 角色、pre-0.2.3 无 anchor 伏笔、stale `pipeline.briefs`;`state/briefs.yaml` 的跨章历史收据是正常 append-only 审计,不误报;不自动改历史。新伏笔用 `format_version:1` 区分合法 anchorless 条目。
39
+ - **发布前复核**:`ready_to_archive` 的可修订归档阻断改推荐 P5→revise;锁定细节、资源归属、千分位/枚举算式、重复段证据定位修正;quality `attempts` 补齐 journal 缺口;`check:host` 在创建临时 home 前验 DSH、保留隔离正控;`check:artifacts` 校验 `dsh.client` 清单。
40
+
41
+ ### Tests
42
+
43
+ - 新增 0.2.3 报表回填、quality 样本/attempts、briefs 按章、brief replay、ready_to_archive 直接修订、P5 hash、doctor legacy、面板分组/advisory/legacy、`projects --prune` 与 advisory 去重测试。
44
+ - 质量:78 个测试文件 / 836 项测试全绿;覆盖率 94.12 / 89.28 / 97.96 / 94.12,逐文件门禁通过;typecheck、lint、build、pack、artifacts、install、preset、host、crash、hygiene、isolated、rulepack 全过。
45
+ - 真实模型:`agnes-2.5-flash`(275 字 / 6 段)与 `agnes-3.0-flash`(373 字 / 8 段)在临时项目完成 P1→P8、归档、report/doctor/panel v5、六种导出、reopen 与重新归档;P1/P2/P3 真实,P4/P6 确定性夹具,详见 [0.2.3 真实模型证据](docs/0.2.3-real-model-evidence.md)。
46
+ - 版本升 0.2.3;`REMOTE_VERSION` 4 → 5;工具 28、服务操作 32、核心工具 10、门控 13、语义项 5、导出 6 均不变。
47
+
3
48
  ## 0.2.2
4
49
 
5
50
  ### Changed
package/README.md CHANGED
@@ -8,7 +8,11 @@
8
8
 
9
9
  0.2.2 把多项目选择从“进程最近一次工具调用”改为**会话级持久绑定**:显式 `project_root` 成功后会记住当前 DSH 会话;同一工作区的 A/B 会话不再串书。面板 v4 增加项目选择器与 choose/missing 状态,`/novel scan` 用于发现项目,`/novel resume` 与面板复制按钮共用同一份续写指令;项目被删除/移动/替换时 fail-closed,不静默换书。
10
10
 
11
- > **当前版本:0.2.2** · 中文界面 · MIT
11
+ 0.2.3 把“账本说真话”补到旧项目:`report` 从 journal 回填旧章 effort,quality 每章只算最终评审并区分 `samples`/`attempts`;briefs 按当前章隔离;归档失败可直接修订;advisory 按章去重并带证据;面板 v5 改为书桌式工作台,项目选择器分离写作/待修复/失效;`check:host` 不再污染真实注册表。
12
+
13
+ 0.2.4 根治 `novel_outline` 的“省略即删除”:`chapters` 按 `num`、`volumes` 按 `title` 做 upsert,省略的卡保持不动;删除必须显式 `remove` / `remove_volumes`;新增 `dry_run`、`added`/`updated`/`removed` 回显、`outline.deprecated.yaml` 与 `novel_restore` 的 outline 恢复路径。这是有意的 BREAKING 语义修正:从“提交集合就是全集”变成“提交的是要改的对象”。
14
+
15
+ > **当前版本:0.2.4** · 中文界面 · MIT
12
16
  >
13
17
  > **宿主范围**:DSH `>=0.2.1-rc.2 <0.3.0-0`;Node `>=24.9.0`。
14
18
 
@@ -86,7 +90,7 @@
86
90
  | ------------------------------- | -------------------------------------------------------------------------------------------- |
87
91
  | 28 个 `novel_*` 工具 | 书级蓝图/大纲/世界观 + 单章流水线 + 报告/体检/搜索/引导/重开/备份恢复 |
88
92
  | 5 条 `/novel*` 命令 | 状态、下一步、统计、导出、隐藏操作;`/novel` 内含 outline/guide/report/doctor/search 子命令 |
89
- | 1 个侧边栏面板「网文工坊」 | 只读;Hero、统计、全书规划、世界观、质量趋势、健康、最近活动、九阶段轨、章节卡、门控区 |
93
+ | 1 个侧边栏面板「网文工坊」 | 只读;当前章/下一步、统计、全书规划、世界观、质量趋势、健康、最近活动、九阶段轨、章节卡、门控区 |
90
94
  | 1 个内置 Agent 预设「网文工坊」 | 28 个工具全开,**不挂载 bash / 文件写入工具**;会话开始后不能切换 |
91
95
  | 1 个入口技能 | 告诉模型什么时候该用引擎、哪些写入口是唯一的 |
92
96
  | 13 条门控 | 任务书、阶段、字数、格式、摘要、场景类型、大纲对齐、评分、问题清零、阅读证明、一致性、轮次、归档原子性 |
@@ -95,7 +99,7 @@
95
99
  | 规则包外置 | 词表、格式、标点、语义、跨章阈值都在 `rulepacks/default.yaml`,代码只执行 |
96
100
  | 六种导出 | Markdown、纯文本、EPUB 3、大纲、世界观、书级提案;正文导出附 `cover.svg` 与 `blurb.txt` |
97
101
  | 整书规划层 | 书级蓝图、卷结构、章卡(严格模式)、世界观设定集;支持 Markdown/YAML/JSON 导入 |
98
- | 可观测性 | `novel_status` 书级摘要、`novel_report`、`novel_doctor`、`novel_search`、panel v4、journal 活动 |
102
+ | 可观测性 | `novel_status` 书级摘要、`novel_report`、`novel_doctor`、`novel_search`、panel v5、journal 活动 |
99
103
  | 多项目注册表 | `stateDir/projects.yaml` 记录已知项目与面板默认 active;不影响项目目录 |
100
104
  | 运行期依赖极少 | 只有 `yaml` 一个运行时依赖;Host 侧不访问外部网络、不调用外部 API、不读环境变量里的密钥 |
101
105
 
@@ -278,7 +282,7 @@ dsh plugin --profile web add dsh-novel-write-engine
278
282
  固定版本:
279
283
 
280
284
  ```bash
281
- dsh plugin --profile web add dsh-novel-write-engine@0.2.2
285
+ dsh plugin --profile web add dsh-novel-write-engine@0.2.4
282
286
  ```
283
287
 
284
288
  `dsh` 不在 PATH 时,用 `npx @deepseek-ai/dsh` 代替命令开头的 `dsh`。
@@ -333,13 +337,13 @@ corepack pnpm pack
333
337
  会得到类似:
334
338
 
335
339
  ```text
336
- dsh-novel-write-engine-0.2.2.tgz
340
+ dsh-novel-write-engine-0.2.4.tgz
337
341
  ```
338
342
 
339
343
  把这个 tarball 复制到目标机器,放在一个**稳定、不会随手删除**的路径,然后:
340
344
 
341
345
  ```bash
342
- dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.2.tgz
346
+ dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.4.tgz
343
347
  ```
344
348
 
345
349
  tarball 安装不会运行源码构建,也不会要求 `allowBuilds`;包内已经包含 `lib/`、`rulepacks/`、`briefs/`、`skills/`、`presets/` 和两层 patch。它是最接近「发布产物」的安装方式。
@@ -354,9 +358,9 @@ tarball 安装不会运行源码构建,也不会要求 `allowBuilds`;包内
354
358
  2. 点 **添加插件**(或旁边的箭头 → **安装第三方插件**)。
355
359
  3. 在包名字段填入以下任一形式:
356
360
  - npm 包名:`dsh-novel-write-engine`
357
- - 精确版本:`dsh-novel-write-engine@0.2.2`
361
+ - 精确版本:`dsh-novel-write-engine@0.2.4`
358
362
  - 源码目录:`/absolute/path/to/dsh-novel-write-engine`
359
- - tarball:`/absolute/path/to/dsh-novel-write-engine-0.2.2.tgz`
363
+ - tarball:`/absolute/path/to/dsh-novel-write-engine-0.2.4.tgz`
360
364
  4. npm 包名从 npm 官方源安装;本地目录和 tarball 直接使用宿主路径。
361
365
  5. 点 **安装**,等待完成。
362
366
  6. 如果出现 **立即启用**,点它;否则安装后到「已安装插件」里把 **dsh-novel-write-engine** 打开。
@@ -428,7 +432,7 @@ dsh --profile web --dump-config | grep -A8 '# == dsh-novel-write-engine'
428
432
  先做全书蓝图、第一卷大纲和世界观设定,再写第一章的世界观约束和章节计划。
429
433
  ```
430
434
 
431
- 模型会调用 `novel_init` 建项目,然后按 `novel_guide` 的提示做书级规划;也可以跳过蓝图直接写第一章,严格大纲是可选开关。0.2.2 推荐顺序:
435
+ 模型会调用 `novel_init` 建项目,然后按 `novel_guide` 的提示做书级规划;也可以跳过蓝图直接写第一章,严格大纲是可选开关。0.2.4 推荐顺序:
432
436
 
433
437
  ```text
434
438
  novel_init
@@ -497,12 +501,11 @@ novel_init
497
501
 
498
502
  归档后打开左侧 **网文工坊**:
499
503
 
500
- - Hero:书名、类型、平台、当前章、阶段;
501
- - 统计:已归档章节、总字数、平均分、待处理门控;
502
- - 全书规划:蓝图/严格模式、章卡进度、**全部卷**(滚动列表)、世界观计数、质量趋势、健康度;
503
- - 九阶段轨;
504
- - 下一步卡与“复制续写指令”;
505
- - 章节卡、门控区与最近活动。
504
+ - 紧凑顶栏:书名、类型、平台、当前章、阶段;
505
+ - 首屏“当前章 / 下一步”卡与“复制续写指令”;
506
+ - 统计条:已归档章节、总字数、平均分、待处理门控;
507
+ - 双栏工作台:左侧阶段轨、门控、提示、旧数据;右侧全书规划(蓝图/严格模式、章卡进度、**全部卷**、世界观计数、质量趋势、健康度)与最近活动;
508
+ - 底部章节卡。
506
509
 
507
510
  面板每 15 秒静默同步一次,也可以点刷新;它**只读**,所有写操作仍在对话/工具里。
508
511
 
@@ -527,13 +530,13 @@ novel_init
527
530
  | 工具 | 阶段 | 做什么 | 写盘 |
528
531
  | -------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
529
532
  | `novel_init` | — | 建项目:`novel.yaml` + `state/` + `outline/` + 目录 | 是 |
530
- | `novel_status` | 任意 | 当前活跃章、阶段、轮次、阻塞项、`state_errors`、下一步;`chapter` 只用于字数查询 | 项目正文否;显式 root 写绑定 |
533
+ | `novel_status` | 任意 | 当前活跃章、阶段、轮次、阻塞项、`state_errors`、`manuscript_hash`、下一步;`chapter` 只用于字数查询 | 项目正文否;显式 root 写绑定 |
531
534
  | `novel_brief` | P1–P6 / P8 | 领取当前活跃章的任务书,返回 `fields`、`context`;`context.world_plan` 与 `context.context_meta` 在 `context` 内,并记录 digest | 仅任务书签收 |
532
535
  | `novel_plan_world` | P1 | 提交世界观约束;数组/对象形状在服务边界校验 | 是 |
533
536
  | `novel_plan_chapter` | P2 | 提交章节计划;标题长度读 rulepack、5–8 个节拍、opening/ending 非空;可选 `cast` 1–8 人 | 是 |
534
537
  | `novel_write_draft` | P3 | 提交初稿;格式/字数/摘要不过整体拒绝 | 是 |
535
538
  | `novel_review` | P4 | 十五维评分、问题清单、三段阅读证明;issue 维度/段落和 blocker waived 会被拒绝 | 是 |
536
- | `novel_revise` | P5/P6 | 提交修订;支持整章 `content` 或 `patch:{expected_hash,edits}`;blocker 必须有正文证据,不能降级或 waived | 是 |
539
+ | `novel_revise` | P5/P6/待归档 | 提交修订;支持整章 `content` 或 `patch:{expected_hash,edits}`;`ready_to_archive` 可直接修订并回 P4;blocker 必须有正文证据,不能降级或 waived | 是 |
537
540
  | `novel_check` | P6 | 机检 + 五个语义一致性项;`advisories` 与 blocker 分开,可用 `acknowledge` 记录知悉;单 scope 不推进归档 | 是 |
538
541
  | `novel_archive` | P7–P9 | 事务归档;必须带 `state_delta`;旧 review 无 digest 时拒绝 | 事务 |
539
542
  | `novel_word_count` | 任意 | 按引擎唯一口径数字数 | 否 |
@@ -545,11 +548,11 @@ novel_init
545
548
  | `novel_audit` | 任意 | 读追加式 journal 审计记录,含被拒 attempt;支持 chapter/op/status/since/until/cursor | 否 |
546
549
  | `novel_pipeline` | 任意 | 「下一步该做什么」的建议 | 否 |
547
550
  | `novel_blueprint` | 任意 | 写书级蓝图:梗概、主题、基调、视角、读者、卖点、结局、卷结构 | 是 |
548
- | `novel_outline` | 任意 | 写卷/章卡;严格模式、归档卡保护、归档章自动回填 | 是 |
551
+ | `novel_outline` | 任意 | 按 `num` upsert 章卡、按 `title` upsert 卷;省略不删,`remove`/`remove_volumes` 显式删除,`dry_run` 预览 | 是 |
549
552
  | `novel_outline_import` | 任意 | 从 Markdown / YAML / JSON 导入大纲;`dry_run` 先预览 | 可选 |
550
553
  | `novel_worldbook` | 任意 | 增量更新世界观设定集(设定/规则/势力/地点/角色/术语/前史) | 是 |
551
554
  | `novel_search` | 任意 | 跨正文、摘要、大纲、世界观、台账搜索,带章号/段落号/片段 | 否 |
552
- | `novel_doctor` | 任意 | 项目体检:项目/流水线/状态、journal、正文 hash、孤立正文、大纲覆盖与漂移、当前阶段产物、锁、备份、目标进度、下一步 | 否 |
555
+ | `novel_doctor` | 任意 | 项目体检:项目/流水线/状态、journal、正文 hash、孤立正文、大纲覆盖与漂移、旧数据审计(legacy-data)、数值事实、当前阶段产物、锁、备份、目标进度、下一步 | 否 |
553
556
  | `novel_report` | 任意 | 全书报告:进度、大纲、世界观、质量、节奏、角色、伏笔、时间线、关系、活动 | 否 |
554
557
  | `novel_guide` | 任意 | 上手引导:当前状态、已完成步骤、下一步工具、命令与提示;无项目也能用 | 否 |
555
558
  | `novel_reopen` | 已归档章 | 从归档备份把紧邻的上一章重开到 P3;必须带非空 `reason`,活动章未开始、备份存在时才允许 | 是 |
@@ -563,7 +566,7 @@ novel_init
563
566
 
564
567
  三类错误:
565
568
 
566
- - **改参数**:`ENGINE_SCHEMA_INVALID`、`ENGINE_STAGE_ORDER`、`ENGINE_PROJECT_NOT_FOUND`、`ENGINE_PROJECT_EXISTS`;
569
+ - **改参数**:`ENGINE_SCHEMA_INVALID`、`ENGINE_STAGE_ORDER`、`ENGINE_PROJECT_NOT_FOUND`、`ENGINE_PROJECT_EXISTS`;修订改了评审归属字段时是 `ENGINE_ISSUE_IMMUTABLE`;同内容同上下文重试是 `ENGINE_RETRY_UNCHANGED`;
567
570
  - **改正文**:`ENGINE_GATE_BLOCKED`、`ENGINE_SCORE_MISMATCH`、`ENGINE_READING_PROOF_INVALID`、`ENGINE_DELTA_UNVERIFIED`;
568
571
  - **查环境**:`ENGINE_SCHEMA_TOO_NEW`、`ENGINE_PATH_ESCAPE`、`ENGINE_LOCK_TIMEOUT`、`ENGINE_TXN_ABORTED`、`ENGINE_RULEPACK_INVALID`。
569
572
 
@@ -591,6 +594,7 @@ novel_init
591
594
  /novel search <关键词>
592
595
  /novel export md|txt|epub|outline|worldbook|proposal
593
596
  /novel projects
597
+ /novel projects --prune [--yes]
594
598
  /novel current
595
599
  /novel use <path|title|.>
596
600
  /novel unbind
@@ -734,7 +738,8 @@ novel_blueprint {premise, logline, themes, tone, pov, audience,
734
738
  selling_points, ending, strict, volumes}
735
739
 
736
740
  # 2. 章卡;或从 Markdown/YAML/JSON 导入
737
- novel_outline {chapters}
741
+ novel_outline {chapters: [{num: 1, ...}], volumes: [...]}
742
+ # 删除:novel_outline {remove: [2]};预览:加 dry_run:true
738
743
  novel_outline_import {format:"markdown", content:"...", dry_run:true}
739
744
 
740
745
  # 3. 世界观设定集(按段替换)
@@ -925,7 +930,29 @@ corepack pnpm test
925
930
 
926
931
  ## 十一、升级
927
932
 
928
- ### 11.0 从 0.2.1 升级到 0.2.2
933
+ ### 11.0 从 0.2.3 升级到 0.2.4
934
+
935
+ 0.2.4 不迁移 `outline.yaml` 数据,只反转 `novel_outline` 的写入口语义:
936
+
937
+ 1. `chapters` 按 `num`、`volumes` 按 `title` upsert;省略的卡保持不动,不再被删除。
938
+ 2. 删除必须显式 `remove` / `remove_volumes`;活动章、归档章、不存在的卡和已完成卷会被拒绝。
939
+ 3. `dry_run:true` 可预演 `would_add/would_update/would_remove`;成功返回 `added/updated/removed`。
940
+ 4. `novel_outline` 正常写入会保留 `outline/outline.deprecated.yaml`;`novel_restore {txid, paths:["outline/outline.yaml"]}` 可恢复整份大纲(要求与当前章索引一致)。
941
+ 5. `novel_blueprint` 的卷列表仍是 replace;删卷必须 `confirm_remove:true`。
942
+ 6. 同名卷会被新写入拒绝;老项目若已有同名卷,先用 `novel_amend` 改名再提交 volumes(只读加载仍兼容)。
943
+
944
+ ### 11.0 从 0.2.2 升级到 0.2.3
945
+
946
+ 0.2.3 不迁移项目数据,只改变读模型、面板 wire 版本与项目卫生:
947
+
948
+ 1. **面板 payload v5**:旧标签页显示刷新;刷新后是书桌式布局、分组项目选择器和独立的提示/旧数据区。
949
+ 2. **report 口径**:旧章 effort 从 journal 回填,quality 每章只算最终评审;`samples` 是章数,`attempts` 是 committed 评审轮次(含历史),样本不足时 `trend` 为 `null`。
950
+ 3. **briefs 按章**:`pipeline.briefs` 只显示当前章;`novel_state {kind:"briefs"}` 仍保留全书收据。
951
+ 4. **归档失败**:`ready_to_archive` 可以直接 `novel_revise` 回 P4,不需要先 rollback。
952
+ 5. **advisory**:同章同 code 只显示一条并合并证据;`RESOURCE_MISMATCH` 不再因中文省略符号误报。
953
+ 6. **项目卫生**:`/novel projects --prune` 默认只列失效登记,`--yes` 才删登记行;`check:host` 不再写真实注册表。
954
+
955
+ ### 11.0.1 从 0.2.1 升级到 0.2.2
929
956
 
930
957
  0.2.2 不迁移项目数据,但改变多项目选择与面板契约:
931
958
 
@@ -995,7 +1022,7 @@ dsh plugin --profile web add dsh-novel-write-engine@latest
995
1022
  也可以固定版本:
996
1023
 
997
1024
  ```bash
998
- dsh plugin --profile web add dsh-novel-write-engine@0.2.2
1025
+ dsh plugin --profile web add dsh-novel-write-engine@0.2.4
999
1026
  ```
1000
1027
 
1001
1028
  升级后核对:
@@ -1037,7 +1064,7 @@ corepack pnpm pack
1037
1064
 
1038
1065
  ```bash
1039
1066
  dsh plugin --profile web remove dsh-novel-write-engine
1040
- dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.2.tgz
1067
+ dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.4.tgz
1041
1068
  ```
1042
1069
 
1043
1070
  ### 11.5 在 Web / Desktop 里升级
@@ -1281,7 +1308,7 @@ DSH_BUNDLE_ROOT=/path/to/deepseek-harness/packages/bundle/web-app corepack pnpm
1281
1308
  6. `dsh plugin remove`,确认层与面板消失;
1282
1309
  7. 确认项目数据未被卸载动作删除。
1283
1310
 
1284
- `pnpm check:host` 覆盖第 1–4 与第 6 步的自动断言(含 28 个工具的宿主声明、面板 v4 字段与空态结构)。`/novel` 命令的 handler 与路由由 `tests/adapters`、`tests/smoke` 的假宿主覆盖,真实宿主命令路由仍属于第 4 步的人工确认;第 5 步也是人工全流程(需要可用模型)。
1311
+ `pnpm check:host` 覆盖第 1–4 与第 6 步的自动断言(含 28 个工具的宿主声明、面板 v5 字段与空态结构)。`/novel` 命令的 handler 与路由由 `tests/adapters`、`tests/smoke` 的假宿主覆盖,真实宿主命令路由仍属于第 4 步的人工确认;第 5 步也是人工全流程(需要可用模型)。
1285
1312
 
1286
1313
  ### 16.4 项目结构
1287
1314
 
@@ -1305,17 +1332,25 @@ tests/ # 单测、集成、falsify、e2e
1305
1332
 
1306
1333
  ### 16.5 当前验证基线
1307
1334
 
1308
- 0.2.2 在 Node 24.19 / DSH 0.2.1-rc.3 上完成过:
1335
+ 0.2.4 在 Node 24.19 / DSH 0.2.1-rc.3 上完成:
1336
+
1337
+ - `npm test` 全绿:**80 个测试文件 / 848 项**;
1338
+ - 覆盖率:**语句 93.97% / 分支 89.14% / 函数 98.00% / 行 93.97%**;`test:coverage` 与逐文件 domain/book/insight/viewmodel 门禁全过;
1339
+ - `typecheck`、`lint`、`build` 通过;`check:hygiene`(143 files,0 v8 ignore)、`test:isolated`(60 源文件)、`validate:rulepack`、`check:pack`(25 files)、`check:artifacts`、`check:install`(26 packages)、`check:preset`(13 packages)、`check:host`、`check:crash` 全绿;
1340
+ - 0.2.4 outline 契约、dry_run、deprecated、outline restore、volumes upsert/remove 与 blueprint confirm 见 [0.2.4 验收](docs/0.2.4-acceptance.md);
1341
+ - 真实模型链路:`agnes-2.5-flash` 与 `agnes-3.0-flash` 在 0.2.4 临时项目完成 P1→P8、归档、report/doctor/panel v5、六种导出、`novel_reopen` 与重新归档;详见 [0.2.4 真实模型证据](docs/0.2.4-real-model-evidence.md)。
1342
+
1343
+ 0.2.3 在 Node 24.19 / DSH 0.2.1-rc.3 上完成:
1309
1344
 
1310
- - `npm test` 全绿:**77 个测试文件 / 772 项**;
1311
- - 覆盖率:**语句 93.87% / 分支 89.15% / 函数 97.89% / 行 93.87%**,高于 0.2.1 基线(93.86 / 89.04 / 97.81 / 93.86);`test:coverage` 与逐文件 domain/book/insight/viewmodel 门禁全过;
1312
- - `typecheck`、`lint`、`check:hygiene`(138 files,0 v8 ignore)、`test:isolated`(58 源文件)、`build`、`validate:rulepack`、`check:pack`(25 files)、`check:artifacts`、`check:install`(26 packages)全绿;
1313
- - `check:preset` 从真实 bundle 解析 13 个 packages;
1314
- - `check:host` 在临时 DSH profile 中实测:面板未认证 401、认证后 200(payload v4)、预设可见、headless 真实触发 `novel_init` 并落盘、`remove` 后层消失;
1315
- - `check:crash` 在真实子进程 SIGKILL 后恢复 pending journal:目标项目仍可读,无遗留 pending 事务;
1316
- - `agnes-2.5-flash` 与 `agnes-3.0-flash` 在临时项目完成 P1→P8、归档、report/doctor/panel v4、history/backups、六种导出、`novel_reopen` 与重新归档;P1/P2/P3 为真实模型,P4/P6 为确定性夹具,详见 [0.2.2-real-model-evidence.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.2-real-model-evidence.md)。
1345
+ - `npm test` 全绿:**78 个测试文件 / 836 项**;
1346
+ - 覆盖率:**语句 94.12% / 分支 89.28% / 函数 97.96% / 行 94.12%**;`test:coverage` 与逐文件 domain/book/insight/viewmodel 门禁全过;
1347
+ - `typecheck`、`lint`、`check:hygiene`(139 files,0 v8 ignore)、`test:isolated`(58 源文件)、`build`、`validate:rulepack`、`check:pack`(25 files)、`check:artifacts`、`check:install`(26 packages)全绿;`check:preset` 在 `DSH_BUNDLE_ROOT=/home/tianhao/deepseek-harness/packages/bundle/web-app` 下解析 13 个包;`check:crash` SIGKILL 恢复通过;
1348
+ - 0.2.2 的历史基线保留在 `docs/0.2.2-evidence.md`:77 files / 772 tests,覆盖率 93.87 / 89.15 / 97.89 / 93.87。
1349
+ - `check:host` 在临时 DSH profile 中实测:面板未认证 401、认证后 200(payload v5)、预设可见、headless 真实触发 `novel_init` 并落盘、`remove` 后层消失;临时 `HOME`/`DSH_HOME` 隔离后真实注册表没有新增 Host Smoke。
1350
+ - 真实 `1971` 项目只读回归:effort 8/6/8/4;quality samples=2、attempts=8、trend=null;第 3 章 briefs 为空;cast 陈嘉树=protagonist;panel legacy 4 类可见(doctor 另含 LEGACY_CHECK_FORMAT,共 5 类)。详见 [0.2.3 验收](docs/0.2.3-acceptance.md)。
1351
+ - 0.2.3 真实模型链路:`agnes-2.5-flash` 与 `agnes-3.0-flash` 在临时项目完成 P1→P8、归档、report/doctor/panel v5、六种导出、`novel_reopen` 与重新归档;P1/P2/P3 为真实模型输出,P4/P6 为确定性夹具。详见 [0.2.3-real-model-evidence.md](docs/0.2.3-real-model-evidence.md)。
1317
1352
 
1318
- 具体数字会随代码变化;发布前请以重新跑出的结果为准,不要把上面的历史数字当保证。完整验收矩阵见 `docs/0.2.2-evidence.md`。
1353
+ 具体数字会随代码变化;发布前请以重新跑出的结果为准,不要把上面的历史数字当保证。完整验收矩阵见 [docs/0.2.3-acceptance.md](docs/0.2.3-acceptance.md)。
1319
1354
 
1320
1355
  ---
1321
1356
 
@@ -1326,6 +1361,11 @@ tests/ # 单测、集成、falsify、e2e
1326
1361
  | [DESIGN.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/DESIGN.md) | 完整设计书:架构、数据模型、规则、门控、事务、面板、预设 |
1327
1362
  | [PLAN.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/PLAN.md) | 0.1.0 实施计划(历史) |
1328
1363
  | [docs/0.2.0-plan.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.0-plan.md) | 0.2.0 整书生命周期:蓝图/大纲/世界观/章卡/观测 |
1364
+ | [docs/0.2.4-plan.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.4-plan.md) | 0.2.4 outline upsert、显式删除、dry_run 与恢复路径的根治方案 |
1365
+ | [docs/0.2.4-acceptance.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.4-acceptance.md) | 0.2.4 验收矩阵、质量门与边界 |
1366
+ | [docs/0.2.4-real-model-evidence.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.4-real-model-evidence.md) | 0.2.4 两条真实模型 P1→P8 链路回执 |
1367
+ | [docs/0.2.3-plan.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.3-plan.md) | 0.2.3 可信账本、工作流、advisory、面板 v5 与项目卫生 |
1368
+ | [docs/0.2.3-acceptance.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.3-acceptance.md) | 0.2.3 验收矩阵、真实项目回归与质量门证据 |
1329
1369
  | [docs/0.2.2-plan.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.2-plan.md) | 0.2.2 会话级项目选择、scan/resume、面板 v4 与完整卷 |
1330
1370
  | [docs/0.2.2-evidence.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.2-evidence.md) | 0.2.2 验收矩阵与质量门证据 |
1331
1371
  | [docs/0.2.2-real-model-evidence.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.2-real-model-evidence.md) | 0.2.2 两条真实模型 P1→P8 链路回执 |
package/briefs/P2.md CHANGED
@@ -8,6 +8,7 @@
8
8
  - 上一章结尾(`state/handoff.yaml` 的 `last_summary`)。
9
9
  - 未回收伏笔:`novel_state foreshadows`。
10
10
  - 当前章卡:`novel_state {"kind":"outline"}`(`context.outline_card` 已带入 brief)。只要存在章卡,标题、场景类型、目标字数区间与 cast 就必须一致,否则 PG-13 会拒绝;严格模式还要求章卡必须存在。
11
+ - 更新章卡时用 `novel_outline` 的 upsert:只提交要改的 `num`,省略的章卡不会被删除;删除必须显式 `remove`。不要靠重交全量卡来“确保存在”。
11
12
  - 世界观相关条目:`context.worldbook`。
12
13
 
13
14
  ## 产出
package/briefs/P5.md CHANGED
@@ -12,12 +12,33 @@
12
12
 
13
13
  ## 提交
14
14
 
15
- 调用 `novel_revise`:
15
+ 调用 `novel_revise`,二选一:
16
+
17
+ **推荐:patch 模式**(只传改动,省 token):
18
+
19
+ ```json
20
+ {
21
+ "chapter": 3,
22
+ "patch": {
23
+ "expected_hash": "任务书 fields.manuscript_hash 的值",
24
+ "edits": [{ "old_text": "原文里的唯一片段", "new_text": "替换后的片段" }]
25
+ },
26
+ "proof": { "opening": {}, "middle": {}, "ending": {} }
27
+ }
28
+ ```
29
+
30
+ - `expected_hash` 必须是当前正文的 SHA-256;`fields.manuscript_hash` 就是它。
31
+ - `old_text` 默认必须在当前正文中唯一;重复出现时用 `occurrence` 指定第几次。
32
+ - 多处编辑不能重叠;hash 不匹配、锚点不唯一都会零写入拒绝。
33
+
34
+ **整章模式**:
16
35
 
17
36
  - `content`:修订后的完整正文(不是 diff)。
18
37
  - `summary`:如果事件、伏笔、资源有变化,同步更新;没变化可省略。**如果改动前两段/opening,必须同时提交新的 summary**,否则引擎会拒绝。
19
38
  - `proof`:修订后正文的三个逐字引用(opening/middle/ending),每段至少 12 字;PG-09 会逐条回查,开头/结尾按实际段落判定,不能改写、编造或靠提交段落号挪动边界。
20
39
 
40
+ `ready_to_archive` 阶段也允许直接 `novel_revise`:正文会退回 P4 重新评审,不需要先 `novel_rollback`,也不会多写一份废稿快照。
41
+
21
42
  ## 常见返工原因
22
43
 
23
44
  - 只改了措辞,没解决 issue 指出的信息缺口。
package/briefs/P8.md CHANGED
@@ -28,11 +28,77 @@
28
28
  - `timeline` 的键是 `summary.events` 的下标(字符串)。
29
29
  - `relationships` 的键是 `"甲|乙"`;每项至少 `{ "action": "created", "type": "关系类型", "description": "..." }`,缺 `type` 会被拒绝。
30
30
  - `characters` 的键是人物名;`world` 的键是新地点/能力/物品名;`foreshadows` 的键是 summary 里的 planted key 或已分配的 `FS-xxx`。
31
+ - `foreshadows` 的 `summary.foreshadows.resolved` 里列出的 key,delta 必须写 `action: "updated"`;引擎会把 `resolved_at` 写成当前章,并把 `note`/`new_info` 追加到 `advance_log`。示例:`"FS-002": {"action":"updated","note":"欠条被收回,债清了","new_info":true}`。
32
+
33
+ ## 最小 schema
34
+
35
+ ```json
36
+ {
37
+ "characters": {
38
+ "<人物名>": {
39
+ "action": "created|updated|no_change",
40
+ "reason": "no_change 必填;其余建议写",
41
+ "role": "protagonist|supporting|antagonist|extra(可选)",
42
+ "profile": {},
43
+ "current_state": { "resources": { "<资源名>": 0 } },
44
+ "growth": { "change": "string", "status_change": "string" }
45
+ }
46
+ },
47
+ "foreshadows": {
48
+ "<planted key 或 FS-xxx>": {
49
+ "action": "created|updated|no_change",
50
+ "title": "可读标签",
51
+ "anchor": "正文逐字原句,至少 8 字",
52
+ "description": "string",
53
+ "note": "resolve/advance 时写清如何兑现",
54
+ "new_info": true
55
+ }
56
+ },
57
+ "timeline": {
58
+ "<summary.events 下标>": {
59
+ "action": "created",
60
+ "story_time": "Day 3 清晨",
61
+ "description": "string",
62
+ "location": "string",
63
+ "characters": ["人物名"]
64
+ }
65
+ },
66
+ "world": {
67
+ "<新地点/能力/物品名>": {
68
+ "action": "created|updated|no_change",
69
+ "kind": "location|ability|artifact|faction|rule",
70
+ "description": "string"
71
+ }
72
+ },
73
+ "relationships": {
74
+ "<甲|乙>": {
75
+ "action": "created|updated|no_change",
76
+ "type": "关系类型(created 必填)",
77
+ "description": "string",
78
+ "note": "updated 时写变化"
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ 错误会带上 `expected` 形态和收到的字段值,例如 `characters.陈嘉树.current_state must be an object; expected {"resources":{"港币":-3},...}`。不要猜字段名,按报错补齐即可。
85
+
86
+ ## summary 与 delta 的联动(归档前必核)
87
+
88
+ `novel_archive` **不接受 summary**;它校验的是 P3/P5 已经固定的 draft summary。delta 必须与这份 summary 对齐:
89
+
90
+ - `characters` 里每个 `action:"created"` 的人物名,必须出现在 draft `summary.characters_appeared`。
91
+ - `world` 里每个 `action:"created"` 的 location / ability / artifact,必须出现在 draft `summary.new_elements` 的对应数组,且 `kind` 一致;`faction` / `rule` 只需正文出现并按 kind 写入,`summary.new_elements` 没有对应数组。
92
+ - `timeline` 的键必须是 draft `summary.events` 的下标(字符串)。
93
+ - `relationships` 的两端必须已在 `characters` 里声明(本章 created 或台账已有),否则先声明人物。
94
+
95
+ 如果 summary 与本章事实不符,**不要直接改 delta 硬凑**:先 `novel_brief P5` → `novel_revise` 修正正文和 summary,再重走 P4/P6/P8。归档阶段没有第二次 summary 入口。
96
+
31
97
  - **伏笔的 title 是可读标签,anchor 才是校验证据**:`title` 可以写「船费债」这样的短语;要归档,请把正文原句放进 `anchor`(summary 的 planted 项或 delta 项都行,至少 8 个字符)。没有 `anchor` 时,`title` 必须逐字出现在正文里,否则 `ENGINE_DELTA_UNVERIFIED`。示例:`{"action":"created","title":"船费债","anchor":"三百蚊的船费先欠着","description":"欠船费三百蚊"}`。
32
98
  - 归档失败的报错会指出字段、值和缺失的锚点;不要把 title 改成碎片来“凑命中”,补 `anchor` 即可。
33
99
  - 第一人称的叙述者若在 `narrator` 里声明为 `陈默(第一人称「我」)`,正文用「我」也视为出现,但角色键仍写 `陈默`。
34
100
  - 每个新实体至少写 `action: "created"`;已存在且没变化写 `action: "no_change", reason: "..."`。
35
- - `world` 新实体还要写 `kind: "location"|"ability"|"artifact"` 和 `description`。
101
+ - `world` 新实体还要写 `kind: "location"|"ability"|"artifact"|"faction"|"rule"` 和 `description`。
36
102
  - `state_delta` 可以是对象,也可以是 JSON 字符串;引擎两者都接受。
37
103
  - 未知伏笔 key、未声明角色、`no_change` 缺原因都会被 `ENGINE_DELTA_UNVERIFIED` 拒绝,引擎不会猜。
38
104
  - 不要传正文、不要传评分、不要传轮次。`handoff` 可选,用于覆盖卷/幕/进度。