dsh-novel-write-engine 0.2.1 → 0.2.2

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,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.2
4
+
5
+ ### Changed
6
+
7
+ - **会话级项目选择**:删除 `EngineService.lastRoot`;显式 `project_root` 成功加载后写入 `stateDir/session-bindings.yaml`,按 DSH `SessionId` 持久化,DSH 重启后仍在。A/B 会话不再因为“最近一次工具调用”互相串书。
8
+ - **四层解析**:显式参数 → 会话绑定 → 会话 cwd 的有限工作区发现(`cwd`、`cwd/novel`、唯一直接子目录)→ 唯一注册项目/配置 `projectRoot`。有 Agent 且多项目无上下文时返回 `ENGINE_PROJECT_AMBIGUOUS`,不再用 `active` 静默兜底。
9
+ - **删除/移动/替换 fail-closed**:绑定目录消失返回 `ENGINE_PROJECT_MISSING`;同路径换成另一本 `created_at` 不同(或新项目没有 `created_at`)的书返回 `ENGINE_PROJECT_IDENTITY_MISMATCH`;不自动回退、不自动重建、不自动清登记/绑定。移动后若注册表有同 `created_at` 的其他 root,错误提示候选但不会自动跳转。
10
+ - **注册表增强**:`projects.yaml` 保持 v1,新增可选 `created_at`;注册按规范化 root upsert,同一路径始终一行;重复注册幂等,显式 `register` 在路径被替换时返回 `replaced:true`;`active` 降级为面板默认与无 Agent 默认。
11
+ - **`novel_init` 的 root 规则**:省略 `project_root` 时只使用配置 `projectRoot`,不读会话绑定/工作区/active;创建成功后才绑定当前会话并登记。
12
+ - **工具 schema**:`novel_init` 使用独立的 `project_root` 说明;其他工具说明四层顺序;`novel_status` 新增 root/source 文本与 JSON `selection`。
13
+ - **面板 v4**:新增项目选择器、choose/missing 状态、`summary.root`、`projects[]`、`selection`、`resume`、`missing`;选择只改 GET `?root=` 与浏览器 `localStorage`,不写会话绑定;加入请求竞态/Abort、未登记偏好回退、损坏项目提示、移动端选择器布局。面板读取使用 `loadAt`,不会因查看项目而登记或绑定。
14
+
15
+ ### Added
16
+
17
+ - **新增命令**:`/novel projects|current|use <path|title|.>|unbind|scan [--register]|resume`。`scan` 只扫有限工作区、最多 50 个候选、不递归/不跟随符号链接;普通 scan 不写,`--register` 只登记健康项目且不改 active/绑定,单条登记失败不中断整批。`resume` 与面板复制共用 `formatResumePrompt`,只读,不写绑定/注册表/项目;scan/resume 不是新工具(工具仍 28、操作仍 32)。
18
+ - **复制续写指令**:下一步卡新增只写浏览器剪贴板的按钮,文本与 `/novel resume` 同源;剪贴板失败时展示可手动选中的只读文本。
19
+
20
+ ### Fixed
21
+
22
+ - **完整卷视图**:修复 0.2.1 只显示前 8 卷的 UI 截断,全部卷在滚动容器中展示并高亮当前卷;不新增 DTO 字段或接口。
23
+ - **删除项目的写入与事务**:写操作在取项目锁前确认 novel/state 证据,`FileLock` 不再把已删除目录重新 mkdir;事务在每次 rename 前与提交后检查 root,root 在 rename 之间消失时 abort、跳过补偿且不补写 journal,避免被后续 mkdir 从空目录重建。
24
+ - **扫描批次与只读**:`scanWorkspace` 在第一个超过 50 的候选处停止检查;`scan --register` 的登记写入失败按候选记录为 skipped,不中断后续候选;`resume` 和面板查看不再通过 `load()` 自动登记/绑定。
25
+ - **面板/初始化旧口径**:修正面板教程中“跟随最近工具调用/activate 切换”的旧说法,README 的初始化 root、旧面板版本号、注册表提示和工具写入说明。
26
+ - **第二轮全量 review 补强**:`novel_amend` 保留已解析 Selection,绑定路径被替换时不再绕过 `created_at` 身份检查,workspace amend 不再产生持久绑定;绑定有身份而磁盘项目失去 `created_at` 时 fail-closed;写工具/amend 在项目锁前返回 `ENGINE_PROJECT_MISSING`,doctor/guide 不再把丢失绑定伪装成“还没有项目”。
27
+ - **元数据与扫描**:重复显式读取不再抢 metadata 写锁;`SessionBindings.bind` 不因 source 变化重写文件;scan 不为已登记候选构建完整 Snapshot,新增候选快照 damaged 时不写占位登记;相对 `project_root` 按会话 cwd 解析,与 `/novel use` 一致。
28
+ - **读取容错与纯文本 diff**:显式 `project_root` 的读取不再因绑定/登记元数据损坏或不可写而失败,无 root 解析仍 fail-closed;`novel_diff` 的 `before`+`after` 纯文本模式不再读取项目,`chapter` 缺省返回 0。
29
+ - **事务根身份**:staging 期间根目录被删除或替换时 fail-closed,不重建、不写入替换目录;`FsStore.writeText/writeBytes/appendText` 不再用 recursive mkdir 重建缺失根。
30
+ - **面板与契约**:切换项目时显示骨架并隐藏旧书数据/复制按钮;`AbortSignal.any` 缺失时保留 10s 超时;missing 提示中文化并区分 `unreadable`,`/novel current` 显示失效绑定状态;`novel_word_count` 纯文本与 `novel_diff` 纯文本模式明确不读项目、不绑定会话;`check:host` 从构建产物派生 28 个工具名并校验 v4 字段。
31
+
32
+ ### Tests
33
+
34
+ - 新增会话绑定、选择矩阵、并发绑定、扫描封顶、扫描单条写失败、命令、面板 v4、只读 resume/panel、damaged 绑定、符号链接根、工作区优先级、可见加载竞态、事务 root 删除、完整卷与剪贴板测试;README/DESIGN/docs/tools/面板教程与错误码同步。
35
+ - 版本升 0.2.2;`REMOTE_VERSION` 3 → 4;工具 28、服务操作 32、核心工具 10、门控 13、语义项 5、导出 6 均不变。
36
+ - 质量:77 个测试文件 / 772 项测试全绿;覆盖率 93.87 / 89.15 / 97.89 / 93.87,高于 0.2.1 基线;typecheck、lint、isolated、build、pack、artifacts、install、preset、host、crash、hygiene 全过。真实模型两条 P1→P8 链路通过,见 [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)。
37
+
3
38
  ## 0.2.1
4
39
 
5
40
  - **面板可滚动**:`nwe-root` 改为 `height:100%; overflow-y:auto; overscroll-behavior:contain`,宿主 `overflow:hidden` 不再裁掉下方内容;教程弹框保持 fixed,Esc/遮罩/焦点返回不回归。教程弹框新增「续写下一章」可直接复制给 Agent 的话术:先 `novel_pipeline` 确认状态,缺章卡用全量 `novel_outline`,目标章数满了用 `novel_amend`,再按 P1→P8 推进并在归档前确认 `state_delta`。
@@ -119,6 +154,6 @@
119
154
  - 面板 exact route 复用 connection 的 Host/Origin/认证闸门;没有阻塞的阶段显示下一步工具与原因。
120
155
  - `FsStore` 对读、写、事务目标拒绝项目内 symlink 重定向。
121
156
  - `/novel admin` 增加 `novel_registry_list/register/activate/forget` 与 `novel_import`,让多项目注册表和设定包导入有显式命令入口。
122
- - 第四轮加固:跨进程归档幂等 replay、原子锁回收、持久化 staging pre-image、活跃项目解析、面板跟随最近工具调用项目、修订不再重写评审 `content_hash`、低分评审可进入 P5、短对话不触发重复段阻塞、嵌套 schema 校验与稳定快照读取。
157
+ - 第四轮加固:跨进程归档幂等 replay、原子锁回收、持久化 staging pre-image、活跃项目解析(0.2.2 已由会话绑定取代)、修订不再重写评审 `content_hash`、低分评审可进入 P5、短对话不触发重复段阻塞、嵌套 schema 校验与稳定快照读取。
123
158
  - 面板 UI/UX 改造:Hero + 四项统计、九阶段完成态、下一步中文动作、章节卡与评分进度、门控可视化、loading/empty/error/outdated 状态、响应式布局、键盘焦点与每 15 秒自动同步。
124
159
  - 第五轮发布前加固:归档前逐文档 `narrow` 校验(非法 delta/handoff 不再写坏项目)、锁回收行不再遮蔽 pending 事务、修订 issue 状态校验、初始化不覆盖失落的 `novel.yaml` 状态、失败 root 不污染面板、长事务锁只按存活进程回收、draft/revision 备份使 `novel_diff` 有基线、journal 对全部历史 state 哈希对账、check 文档嵌套校验、面板单章完成态与全书完成态区分、工具警告回传、preset 补回 subagent 控制工具。
package/README.md CHANGED
@@ -6,7 +6,9 @@
6
6
 
7
7
  0.2.0 起,它同时管理**整本书**:书级蓝图 → 卷/章大纲 → 世界观设定集 → 单章 P1–P9 流水线 → 归档回写大纲 → 报告/体检/搜索/面板。大纲可以从 Markdown/YAML/JSON 导入,也可以由模型按章卡结构生成;严格模式下 P2/P3 会按章卡校验标题、场景、字数与 cast。
8
8
 
9
- > **当前版本:0.2.1** · 中文界面 · MIT
9
+ 0.2.2 把多项目选择从“进程最近一次工具调用”改为**会话级持久绑定**:显式 `project_root` 成功后会记住当前 DSH 会话;同一工作区的 A/B 会话不再串书。面板 v4 增加项目选择器与 choose/missing 状态,`/novel scan` 用于发现项目,`/novel resume` 与面板复制按钮共用同一份续写指令;项目被删除/移动/替换时 fail-closed,不静默换书。
10
+
11
+ > **当前版本:0.2.2** · 中文界面 · MIT
10
12
  >
11
13
  > **宿主范围**:DSH `>=0.2.1-rc.2 <0.3.0-0`;Node `>=24.9.0`。
12
14
 
@@ -93,9 +95,9 @@
93
95
  | 规则包外置 | 词表、格式、标点、语义、跨章阈值都在 `rulepacks/default.yaml`,代码只执行 |
94
96
  | 六种导出 | Markdown、纯文本、EPUB 3、大纲、世界观、书级提案;正文导出附 `cover.svg` 与 `blurb.txt` |
95
97
  | 整书规划层 | 书级蓝图、卷结构、章卡(严格模式)、世界观设定集;支持 Markdown/YAML/JSON 导入 |
96
- | 可观测性 | `novel_status` 书级摘要、`novel_report`、`novel_doctor`、`novel_search`、panel v3、journal 活动 |
97
- | 多项目注册表 | `stateDir/projects.yaml` 记录已知项目与当前项目;不影响项目目录 |
98
- | 运行期依赖极少 | 只有 `yaml` 一个运行时依赖;不联网、不调用外部 API、不读环境变量里的密钥 |
98
+ | 可观测性 | `novel_status` 书级摘要、`novel_report`、`novel_doctor`、`novel_search`、panel v4、journal 活动 |
99
+ | 多项目注册表 | `stateDir/projects.yaml` 记录已知项目与面板默认 active;不影响项目目录 |
100
+ | 运行期依赖极少 | 只有 `yaml` 一个运行时依赖;Host 侧不访问外部网络、不调用外部 API、不读环境变量里的密钥 |
99
101
 
100
102
  ### 关键设计
101
103
 
@@ -171,7 +173,7 @@ P7/P8/P9 在 `novel_archive` 一次事务里完成。单章归档后状态是 `d
171
173
  | pnpm | CLI 安装/卸载插件需要;建议 11.x(本仓库开发基线为 11.28.9) |
172
174
  | 宿主形态 | Web(面板 + 预设)、Desktop(面板 + 预设,插件由桌面壳管理)、headless(只有工具/命令,无面板/预设) |
173
175
  | 磁盘 | 项目根目录可写;默认 `stateDir` 可写 |
174
- | 网络 | 安装时需要访问 npm / Git 托管;运行插件本身不联网 |
176
+ | 网络 | 安装时需要访问 npm / Git 托管;运行期 Host 侧不访问外部网络,Web 客户端只向宿主面板发同源 GET |
175
177
  | 语言 | 0.2.x 仅中文 |
176
178
 
177
179
  检查命令:
@@ -276,7 +278,7 @@ dsh plugin --profile web add dsh-novel-write-engine
276
278
  固定版本:
277
279
 
278
280
  ```bash
279
- dsh plugin --profile web add dsh-novel-write-engine@0.2.1
281
+ dsh plugin --profile web add dsh-novel-write-engine@0.2.2
280
282
  ```
281
283
 
282
284
  `dsh` 不在 PATH 时,用 `npx @deepseek-ai/dsh` 代替命令开头的 `dsh`。
@@ -331,13 +333,13 @@ corepack pnpm pack
331
333
  会得到类似:
332
334
 
333
335
  ```text
334
- dsh-novel-write-engine-0.2.1.tgz
336
+ dsh-novel-write-engine-0.2.2.tgz
335
337
  ```
336
338
 
337
339
  把这个 tarball 复制到目标机器,放在一个**稳定、不会随手删除**的路径,然后:
338
340
 
339
341
  ```bash
340
- dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.1.tgz
342
+ dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.2.tgz
341
343
  ```
342
344
 
343
345
  tarball 安装不会运行源码构建,也不会要求 `allowBuilds`;包内已经包含 `lib/`、`rulepacks/`、`briefs/`、`skills/`、`presets/` 和两层 patch。它是最接近「发布产物」的安装方式。
@@ -352,9 +354,9 @@ tarball 安装不会运行源码构建,也不会要求 `allowBuilds`;包内
352
354
  2. 点 **添加插件**(或旁边的箭头 → **安装第三方插件**)。
353
355
  3. 在包名字段填入以下任一形式:
354
356
  - npm 包名:`dsh-novel-write-engine`
355
- - 精确版本:`dsh-novel-write-engine@0.2.1`
357
+ - 精确版本:`dsh-novel-write-engine@0.2.2`
356
358
  - 源码目录:`/absolute/path/to/dsh-novel-write-engine`
357
- - tarball:`/absolute/path/to/dsh-novel-write-engine-0.2.1.tgz`
359
+ - tarball:`/absolute/path/to/dsh-novel-write-engine-0.2.2.tgz`
358
360
  4. npm 包名从 npm 官方源安装;本地目录和 tarball 直接使用宿主路径。
359
361
  5. 点 **安装**,等待完成。
360
362
  6. 如果出现 **立即启用**,点它;否则安装后到「已安装插件」里把 **dsh-novel-write-engine** 打开。
@@ -426,7 +428,7 @@ dsh --profile web --dump-config | grep -A8 '# == dsh-novel-write-engine'
426
428
  先做全书蓝图、第一卷大纲和世界观设定,再写第一章的世界观约束和章节计划。
427
429
  ```
428
430
 
429
- 模型会调用 `novel_init` 建项目,然后按 `novel_guide` 的提示做书级规划;也可以跳过蓝图直接写第一章,严格大纲是可选开关。0.2.1 推荐顺序:
431
+ 模型会调用 `novel_init` 建项目,然后按 `novel_guide` 的提示做书级规划;也可以跳过蓝图直接写第一章,严格大纲是可选开关。0.2.2 推荐顺序:
430
432
 
431
433
  ```text
432
434
  novel_init
@@ -457,7 +459,7 @@ novel_init
457
459
 
458
460
  `premise`、`logline`、`themes`、`tone`、`pov`、`audience`、`selling_points`、`ending` 与 `strict_outline` 都是可选的书级种子;不填也能开书,之后用 `novel_blueprint` 补。
459
461
 
460
- `genre` 必须是 12 类之一,`platform` 必须是 `fanqie` / `qidian` / `qimao` / `jinjiang` 之一。`project_root` 省略时按「最近活跃项目 → 注册表 active → 配置里的 `projectRoot`(默认 `./novel`,相对于 DSH 进程工作目录)」解析;有多个项目时请显式传 `project_root`,避免写错书。
462
+ `genre` 必须是 12 类之一,`platform` 必须是 `fanqie` / `qidian` / `qimao` / `jinjiang` 之一。对 `novel_init`,省略 `project_root` 时只使用配置 `projectRoot`,**不读会话绑定、工作区或 active**;创建成功后才绑定当前会话。其他工具省略 `project_root` 时按 0.2.2 的四层顺序解析:**当前 DSH 会话绑定 → 会话 cwd 中唯一项目 → 唯一注册项目/配置 `projectRoot`**;有多个项目且无法唯一确定时返回 `ENGINE_PROJECT_AMBIGUOUS`,需要 `/novel use` 或显式 `project_root`。`active` 只影响面板默认,不再静默决定 Agent 的工具项目。
461
463
 
462
464
  ### 4. 一章的标准调用顺序
463
465
 
@@ -497,9 +499,9 @@ novel_init
497
499
 
498
500
  - Hero:书名、类型、平台、当前章、阶段;
499
501
  - 统计:已归档章节、总字数、平均分、待处理门控;
500
- - 全书规划:蓝图/严格模式、章卡进度、卷列表、世界观计数、质量趋势、健康度;
502
+ - 全书规划:蓝图/严格模式、章卡进度、**全部卷**(滚动列表)、世界观计数、质量趋势、健康度;
501
503
  - 九阶段轨;
502
- - 下一步卡;
504
+ - 下一步卡与“复制续写指令”;
503
505
  - 章节卡、门控区与最近活动。
504
506
 
505
507
  面板每 15 秒静默同步一次,也可以点刷新;它**只读**,所有写操作仍在对话/工具里。
@@ -525,7 +527,7 @@ novel_init
525
527
  | 工具 | 阶段 | 做什么 | 写盘 |
526
528
  | -------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
527
529
  | `novel_init` | — | 建项目:`novel.yaml` + `state/` + `outline/` + 目录 | 是 |
528
- | `novel_status` | 任意 | 当前活跃章、阶段、轮次、阻塞项、`state_errors`、下一步;`chapter` 只用于字数查询 | 否 |
530
+ | `novel_status` | 任意 | 当前活跃章、阶段、轮次、阻塞项、`state_errors`、下一步;`chapter` 只用于字数查询 | 项目正文否;显式 root 写绑定 |
529
531
  | `novel_brief` | P1–P6 / P8 | 领取当前活跃章的任务书,返回 `fields`、`context`;`context.world_plan` 与 `context.context_meta` 在 `context` 内,并记录 digest | 仅任务书签收 |
530
532
  | `novel_plan_world` | P1 | 提交世界观约束;数组/对象形状在服务边界校验 | 是 |
531
533
  | `novel_plan_chapter` | P2 | 提交章节计划;标题长度读 rulepack、5–8 个节拍、opening/ending 非空;可选 `cast` 1–8 人 | 是 |
@@ -588,6 +590,12 @@ novel_init
588
590
  /novel doctor
589
591
  /novel search <关键词>
590
592
  /novel export md|txt|epub|outline|worldbook|proposal
593
+ /novel projects
594
+ /novel current
595
+ /novel use <path|title|.>
596
+ /novel unbind
597
+ /novel scan [--register]
598
+ /novel resume
591
599
  /novel admin
592
600
  /novel admin novel_state {"kind":"pipeline"}
593
601
  ```
@@ -622,7 +630,7 @@ novel_write_draft, novel_review, novel_revise, novel_check, novel_archive
622
630
  /novel admin novel_restore {"txid":"..."}
623
631
  ```
624
632
 
625
- 多项目注册表与设定包导入**只有命令入口**:
633
+ 多项目注册表与设定包导入**不走工具 schema**;日常发现/切换用 `/novel scan`、`/novel use`,底层登记仍可用:
626
634
 
627
635
  ```text
628
636
  /novel admin novel_registry_list
@@ -637,13 +645,15 @@ novel_write_draft, novel_review, novel_revise, novel_check, novel_archive
637
645
 
638
646
  ### 8.4 侧边栏「网文工坊」
639
647
 
640
- - 只读 HTTP 路由:`GET/HEAD /api/novel-write-engine/panel`;
648
+ - 只读 HTTP 路由:`GET/HEAD /api/novel-write-engine/panel?root=<可选>`;
641
649
  - 精确路由,复用 connection 的认证/Origin 闸门;未认证返回 401/403,连接层缺席返回 503;
642
650
  - 返回 `schemaVersion`;版本不一致时面板提示刷新;
643
- - 状态:loading(骨架)、empty(显示 `novel_init`)、error(重试)、outdated(刷新)、ready;
644
- - 章节超过 30 章时只渲染最近 30 张,并显示总记录数;
645
- - 窄屏纵向堆叠,支持减少动态效果;
646
- - 面板跟随**最近一次工具调用**的项目;冷启动时读注册表的 active,最后回退到配置的 `projectRoot`。
651
+ - 状态:loading(骨架)、choose(多项目先选)、missing(项目不在)、empty(显示 `novel_init`)、error(重试)、outdated(刷新)、ready;
652
+ - 顶部项目选择器(`aria-label="切换项目"`);选择只影响面板 GET 的 `?root=`,写入浏览器 `localStorage`,**不调用 activate、不写会话绑定**;
653
+ - 只接受注册表、active 或配置 `projectRoot` 的路径;未登记路径返回 `missing.reason="not_registered"`,不读取任意目录;
654
+ - 章节超过 30 章时只渲染最近 30 张,并显示总记录数;卷列表展示全部卷,长列表在滚动容器内;
655
+ - 下一步卡提供“复制续写指令”,文本与 `/novel resume` 同源,只写浏览器剪贴板;
656
+ - 窄屏纵向堆叠,支持减少动态效果。
647
657
 
648
658
  面板不是第二个写入口。它没有改名、归档、回退按钮。
649
659
 
@@ -688,13 +698,33 @@ exports/
688
698
  - EPUB:EPUB 3,自写 ZIP 容器,`mimetype` 首条且 store 方式;
689
699
  - 封面是纯 SVG 字符串,不依赖 canvas;平台简介由模板生成,不调用模型。
690
700
 
691
- ### 8.6 多项目
701
+ ### 8.6 多项目与会话绑定
702
+
703
+ 四层选择模型:
704
+
705
+ 1. 显式 `project_root`(成功后写入当前会话绑定);
706
+ 2. `stateDir/session-bindings.yaml` 中当前 DSH `SessionId` 的绑定;
707
+ 3. 会话 cwd 的有限工作区发现(`cwd`、`cwd/novel`、唯一直接子目录;多候选报歧义);
708
+ 4. 唯一注册项目或配置 `projectRoot`;有多个项目时不猜 `active`。
709
+
710
+ ```text
711
+ /novel projects 列出项目;* 当前会话,P 面板默认,! 目录缺失/待修复
712
+ /novel current 当前绑定或推导来源
713
+ /novel use /abs/book 切换当前会话;active 不变
714
+ /novel use . 按当前 cwd 的工作区规则选唯一项目
715
+ /novel use 书名 按注册表精确标题切换;重名要求用路径
716
+ /novel unbind 只清当前会话绑定,不删项目
717
+ /novel scan 只读扫描当前工作区候选
718
+ /novel scan --register 只登记健康项目;不改 active、不绑会话
719
+ /novel resume 输出可粘贴的续写指令;只读
720
+ ```
692
721
 
693
- - `stateDir/projects.yaml` 记录已知项目与 active;
694
- - 工具可用 `project_root` 覆盖当前项目;
695
- - 面板跟随最近一次工具调用的项目;
696
- - 切换 active:`/novel admin novel_registry_activate {"root":"/abs/book"}`;
697
- - 删除注册行:`/novel admin novel_registry_forget {"root":"/abs/book"}`;项目文件不动。
722
+ - `projects.yaml` 仍是 v1,新增可选 `created_at`;`session-bindings.yaml` 只保存会话选择,删掉不会删书;
723
+ - `/novel use` 成功绑定后会 best-effort 把项目加入注册表,便于随后在 `/novel projects` 与面板看到;不改变 active。`novel.yaml` 损坏时不绑定、不登记,先用显式 `project_root` 修复;
724
+ - 无显式 `project_root` 时,注册/绑定文件损坏 fail-closed,不静默重建;显式 `project_root` 的读取仍可用(损坏的绑定不会被更新),写事务提交后元数据失败只回 warning;
725
+ - 项目目录被删除时保留登记与绑定,返回 `ENGINE_PROJECT_MISSING`,不自动回退、不自动重建;
726
+ - 原路径被另一本书占用时返回 `ENGINE_PROJECT_IDENTITY_MISMATCH`,必须显式重新 `/novel use`;
727
+ - `/novel admin novel_registry_activate` 只改面板默认,不切换当前会话;`forget` 不删绑定与项目。
698
728
 
699
729
  ### 8.7 整书规划、观测与修复
700
730
 
@@ -733,12 +763,12 @@ novel_reopen {chapter:3, reason:"结尾需要重写"}
733
763
 
734
764
  | 键 | 默认 | 说明 |
735
765
  | ----------------- | -------------------------------- | ------------------------------------------------------------------------- |
736
- | `projectRoot` | `./novel` | 默认项目根;相对路径相对于 DSH 进程工作目录。工具可用 `project_root` 覆盖 |
766
+ | `projectRoot` | `./novel` | 无会话绑定/工作区/唯一项目时的兜底根;相对路径相对于 DSH 进程工作目录 |
737
767
  | `minScore` | `120` | PG-07 过关线,整数 0..150;唯一 owner |
738
768
  | `toolsMode` | `all` | `all` 注册 28 个工具;`core` 只注册 10 个,其余走 `/novel admin` |
739
769
  | `briefsOverrides` | 空 | 自定义任务书目录;某阶段文件缺失时回退包内 `briefs/<阶段>.md` |
740
770
  | `backupRetention` | `5` | 保留的归档备份目录数;只清理引擎自己创建的 `backups/<txid>` |
741
- | `stateDir` | 空 → `~/.dsh/novel-write-engine` | 多项目注册表位置;**不要放进项目目录** |
771
+ | `stateDir` | 空 → `~/.dsh/novel-write-engine` | 多项目注册表与会话绑定位置;**不要放进项目目录** |
742
772
 
743
773
  > `stateDir` 的默认值是代码按当前用户 home 拼出的 `~/.dsh/novel-write-engine`,不跟随 `DSH_HOME`。如果你改了 `DSH_HOME`,建议显式把它设成绝对路径。
744
774
  >
@@ -874,7 +904,7 @@ corepack pnpm test
874
904
  ~/.dsh/novel-write-engine/projects.yaml
875
905
  ```
876
906
 
877
- 只记录项目路径、标题、active;**不保存正文**。删掉它不会删任何书。面板在冷启动时用它找 active 项目。
907
+ `projects.yaml` 只记录项目路径、标题、可选 `created_at` 与 `active`;`session-bindings.yaml` 按 DSH `SessionId` 记录显式选择。两者都**不保存正文**,删掉不会删任何书。面板用 projects/active/配置找默认项目,不读会话绑定。
878
908
 
879
909
  ### 10.3 手工修改的边界
880
910
 
@@ -895,7 +925,21 @@ corepack pnpm test
895
925
 
896
926
  ## 十一、升级
897
927
 
898
- ### 11.0 从 0.2.0 升级到 0.2.1
928
+ ### 11.0 从 0.2.1 升级到 0.2.2
929
+
930
+ 0.2.2 不迁移项目数据,但改变多项目选择与面板契约:
931
+
932
+ 1. **面板 payload v4**:旧标签页显示刷新;刷新后出现项目选择器、choose/missing 状态和复制续写按钮。
933
+ 2. **`lastRoot` 删除**:工具不再跟随进程内“最近碰过谁”。A/B 会话分别绑定、重启后仍在;显式 `project_root` 成功后写入 `session-bindings.yaml`。
934
+ 3. **`active` 降级**:只影响面板默认与无 Agent 调用;有 Agent 且多项目无上下文时返回歧义,不静默选 active。
935
+ 4. **`novel_init` 省略 root**:只使用配置 `projectRoot`,不读会话绑定/工作区/active;创建成功后才绑定当前会话。
936
+ 5. **项目删除/移动/替换 fail-closed**:返回 MISSING/IDENTITY_MISMATCH,不自动回退、不自动重建、不清登记与绑定。
937
+ 6. **新增命令**:`/novel projects|current|use|unbind|scan|resume`;`scan`/`resume` 不是新工具,工具仍 28、操作仍 32。
938
+ 7. **面板完整卷**:0.2.1 只显示前 8 卷的 UI 截断已修复;全部卷在滚动容器中展示。
939
+
940
+ 回滚到 0.2.1 时,`session-bindings.yaml` 被旧版忽略;旧版恢复 `lastRoot` 行为。注册表新增的 `created_at` 对旧版可忽略。
941
+
942
+ ### 11.0.1 从 0.2.0 升级到 0.2.1
899
943
 
900
944
  0.2.1 不迁移项目数据,但改变几处契约:
901
945
 
@@ -911,7 +955,7 @@ corepack pnpm test
911
955
  10. **worldbook facts**:新增声明式数字事实,进入计数、brief 上下文、搜索与 P6 检查;`facts` 只做作者声明的 canon 对账,不等同外部史实库。
912
956
  11. **伏笔 anchor**:可读 `title` 与逐字 `anchor` 分离,anchor 至少 8 个字符;单字子串不再能绕过校验。
913
957
 
914
- ### 11.0.1 从 0.1.3 升级到 0.2.0
958
+ ### 11.0.2 从 0.1.3 升级到 0.2.0
915
959
 
916
960
  0.2.0 新增 `outline/outline.yaml` 与 `outline/worldbook.yaml`,新增 9 个工具与 PG-13;旧项目缺少两份规划文档时按空规划读取,不会报错。新项目默认写入两份空文档;严格大纲默认关闭,`novel_outline` 首次提交才开启。P6 语义项从 4 条变 5 条(新增 `L2-POV`),旧的 4 条 check 需要重跑。面板 payload v1 → v2,旧标签页显示刷新。所有升级后第一次取 brief 会因上下文新增 `book_plan`/`outline_card`/`worldbook` 而失效,重新 `novel_brief` 即可。
917
961
 
@@ -951,7 +995,7 @@ dsh plugin --profile web add dsh-novel-write-engine@latest
951
995
  也可以固定版本:
952
996
 
953
997
  ```bash
954
- dsh plugin --profile web add dsh-novel-write-engine@0.2.1
998
+ dsh plugin --profile web add dsh-novel-write-engine@0.2.2
955
999
  ```
956
1000
 
957
1001
  升级后核对:
@@ -993,7 +1037,7 @@ corepack pnpm pack
993
1037
 
994
1038
  ```bash
995
1039
  dsh plugin --profile web remove dsh-novel-write-engine
996
- dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.1.tgz
1040
+ dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.2.tgz
997
1041
  ```
998
1042
 
999
1043
  ### 11.5 在 Web / Desktop 里升级
@@ -1081,7 +1125,7 @@ dsh plugin --profile web remove dsh-novel-write-engine
1081
1125
  卸载插件不会删书。确认不再需要后,再手动清理:
1082
1126
 
1083
1127
  ```bash
1084
- # 多项目注册表(只影响「最近项目」提示,不影响书)
1128
+ # 多项目注册表与会话绑定(只影响选择/查看,不影响书)
1085
1129
  rm -rf "$HOME/.dsh/novel-write-engine"
1086
1130
 
1087
1131
  # 某个项目(先确认路径!)
@@ -1123,8 +1167,11 @@ dsh --profile web --dump-config | grep dsh-novel-write-engine
1123
1167
  | 会话里没有 `novel_*` 工具 | 插件未启用、旧会话未刷新、装错 profile | 新开会话;重启;`--dump-config` 核对 |
1124
1168
  | `ENGINE_STAGE_ORDER` | 跳过了阶段或顺序不对 | `/novel` / `novel_status` 看下一步;按 `novel_brief` 领任务书 |
1125
1169
  | `ENGINE_GATE_BLOCKED` | 门控未过 | 看 `blocking` 的 code/detail/hint;改正文或补证据;不要手改 state |
1126
- | `ENGINE_PROJECT_NOT_FOUND` | 项目根不对/未初始化 | `novel_init`,或传正确的 `project_root` |
1127
- | 面板空白/显示没有项目 | 当前没有项目,或 active 指向旧路径 | 先 `novel_init`;`/novel admin novel_registry_activate` 切到正确项目 |
1170
+ | `ENGINE_PROJECT_NOT_FOUND` | 项目根不对/未初始化 | `/novel scan` 发现项目;`novel_init`,或传正确的 `project_root` |
1171
+ | `ENGINE_PROJECT_AMBIGUOUS` | 有多个项目,无法从会话/cwd 唯一确定 | `/novel use <path|title>` 或传 `project_root`;不会默认 active |
1172
+ | `ENGINE_PROJECT_MISSING` | 当前会话绑定的目录被删除/未挂载 | 恢复目录后重试;或显式 `/novel use <新路径>`;不会自动换书 |
1173
+ | `ENGINE_PROJECT_IDENTITY_MISMATCH`| 原路径已被另一本书占用,`created_at` 不同 | 确认后用 `/novel use` 重新绑定;旧绑定仍 fail-closed |
1174
+ | 面板空白/显示没有项目 | 当前没有项目,或 active 指向旧路径 | 先 `novel_init`;多项目时用面板选择器或 `/novel use` 选择 |
1128
1175
  | 面板显示「需要刷新」 | 浏览器缓存了旧 client bundle | 刷新页面;重启 DSH |
1129
1176
  | 升级后仍是旧行为 | 源码目录没 `corepack pnpm build`,或旧会话未换 | 重新构建;重启;新开会话 |
1130
1177
  | 卸载后配置项还在 | profile 的 `cordis.patch.yml` 还留着覆盖 | 删除 `id: novel-write-engine` 的段落 |
@@ -1147,13 +1194,13 @@ dsh --profile web --dump-config | grep dsh-novel-write-engine
1147
1194
 
1148
1195
  DSH bundle 的 Host 半边在 DSH 进程内执行,不受工作区沙箱限制。安装本插件等于允许它在 DSH 进程权限下读写你给它的项目目录和 `stateDir`。只从你信任的 npm 包、源码或 tarball 安装。
1149
1196
 
1150
- ### 14.2 它不联网
1197
+ ### 14.2 它不向外联网
1151
1198
 
1152
- 插件本身没有 `fetch`、HTTP 客户端或遥测;运行期依赖只有 `yaml`。它不读取环境变量里的 API Key,也不会把正文发到任何服务。模型调用本身由 DSH 负责,与本插件无关。
1199
+ Host 侧没有 `fetch`、HTTP 客户端或遥测;运行期依赖只有 `yaml`。它不读取环境变量里的 API Key,也不会把正文发到任何第三方服务。Web 客户端只会向宿主自己的只读面板路由发同源 `GET`,不访问外部地址;模型调用本身由 DSH 负责,与本插件无关。
1153
1200
 
1154
1201
  ### 14.3 面板是只读的
1155
1202
 
1156
- `/api/novel-write-engine/panel` 只接受 `GET`/`HEAD`,并复用 DSH connection 的认证与 Origin 校验。它不能改项目;所有写操作仍然要经过工具/命令。
1203
+ `/api/novel-write-engine/panel` 只接受 `GET`/`HEAD`,并复用 DSH connection 的认证与 Origin 校验。项目选择器只改 `?root=` 与浏览器 `localStorage`,复制按钮只写剪贴板;它们不能改项目、注册表或会话绑定。所有写操作仍然要经过工具/命令。
1157
1204
 
1158
1205
  ### 14.4 状态文件不是「随便改的 YAML」
1159
1206
 
@@ -1180,6 +1227,7 @@ DSH bundle 的 Host 半边在 DSH 进程内执行,不受工作区沙箱限制
1180
1227
  - **规则包不可运行期替换**:要改词表/阈值,必须改源码、重新构建、重新安装。
1181
1228
  - **普通会话可能绕过引擎**:只有「网文工坊」预设不挂 bash/fs 写工具;在普通会话里手动改文件,事务与门控保证不再完整。
1182
1229
  - **预设会话开始后不能切换**。
1230
+ - **同一物理项目请固定一个路径**:注册表按规范化路径(`path.resolve`)去重;同一本书通过符号链接和真实路径分别登记会变成两个 root,可能显示两行或报歧义。请在 `/novel use`/`project_root` 中固定使用其中一个路径。
1183
1231
  - **单章轮次上限 3**:超过需要 `novel_rollback`,不是无限重试。
1184
1232
  - **面板只读**:没有在面板里直接改正文/归档的按钮。
1185
1233
  - **DSH 仍是 developer preview**:`0.3.0` 之前可能有破坏性变更;升级 DSH 前先看插件 `engines.dsh`。
@@ -1233,7 +1281,7 @@ DSH_BUNDLE_ROOT=/path/to/deepseek-harness/packages/bundle/web-app corepack pnpm
1233
1281
  6. `dsh plugin remove`,确认层与面板消失;
1234
1282
  7. 确认项目数据未被卸载动作删除。
1235
1283
 
1236
- `pnpm check:host` 覆盖第 1–4 与第 6 步的自动断言(含 28 个工具的宿主声明与面板结构);`/novel` 命令的注册与路由由 `tests/adapters` 覆盖;第 5 步仍是人工全流程(需要可用模型)。
1284
+ `pnpm check:host` 覆盖第 1–4 与第 6 步的自动断言(含 28 个工具的宿主声明、面板 v4 字段与空态结构)。`/novel` 命令的 handler 与路由由 `tests/adapters`、`tests/smoke` 的假宿主覆盖,真实宿主命令路由仍属于第 4 步的人工确认;第 5 步也是人工全流程(需要可用模型)。
1237
1285
 
1238
1286
  ### 16.4 项目结构
1239
1287
 
@@ -1257,16 +1305,17 @@ tests/ # 单测、集成、falsify、e2e
1257
1305
 
1258
1306
  ### 16.5 当前验证基线
1259
1307
 
1260
- 0.2.1 在 Node 24.19 / DSH 0.2.1-rc.3 上完成过:
1308
+ 0.2.2 在 Node 24.19 / DSH 0.2.1-rc.3 上完成过:
1261
1309
 
1262
- - `corepack pnpm test` 全绿(当前树 667 项,72 个测试文件);
1263
- - 最近一次覆盖率语句 93.86% / 分支 89.04% / 函数 97.81% / 行 93.86%(V8 分支/行计数会有 ±0.01 波动,门禁按单次真实结果判定),`test:coverage` 与逐文件 domain/book/insight 门禁全过;
1264
- - `typecheck`、`lint`、`check:hygiene`、`test:isolated`、`build`、`validate:rulepack`、`check:pack`、`check:artifacts`、`check:install` 全绿;
1265
- - `check:host` 在临时 DSH profile 中实测:插件层出现、预设可见、面板未认证 401、认证后 200、headless 真实触发 `novel_init` 工具并落盘、`remove` 后层消失;`check:preset` 从真实 bundle 解析 13 个包;
1266
- - `check:crash` 在真实子进程 SIGKILL 后恢复 pending journal:目标项目仍可读,无遗留 pending 事务。
1267
- - `agnes-2.5-flash` / `agnes-3.0-flash` 在临时项目完成 P1→P8、归档、六种导出、`novel_reopen` 与重新归档;详见 `docs/0.2.1-real-model-evidence.md`。
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)。
1268
1317
 
1269
- 具体数字会随代码变化;发布前请以重新跑出的结果为准,不要把上面的历史数字当保证。
1318
+ 具体数字会随代码变化;发布前请以重新跑出的结果为准,不要把上面的历史数字当保证。完整验收矩阵见 `docs/0.2.2-evidence.md`。
1270
1319
 
1271
1320
  ---
1272
1321
 
@@ -1277,6 +1326,9 @@ tests/ # 单测、集成、falsify、e2e
1277
1326
  | [DESIGN.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/DESIGN.md) | 完整设计书:架构、数据模型、规则、门控、事务、面板、预设 |
1278
1327
  | [PLAN.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/PLAN.md) | 0.1.0 实施计划(历史) |
1279
1328
  | [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 整书生命周期:蓝图/大纲/世界观/章卡/观测 |
1329
+ | [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
+ | [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
+ | [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 链路回执 |
1280
1332
  | [docs/0.2.1-plan.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.1-plan.md) | 0.2.1 可信账本、历史、备份、计划分权与质量门禁 |
1281
1333
  | [docs/0.2.0-real-model-evidence.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.0-real-model-evidence.md) | 真实模型全链路验证回执 |
1282
1334
  | [docs/0.1.2-plan.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.1.2-plan.md) | 0.1.2 长书可运行性与证据闭环方案(历史) |