dsh-novel-write-engine 0.2.3 → 0.2.5
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 +50 -0
- package/README.md +49 -11
- package/briefs/P2.md +1 -0
- package/briefs/P5.md +2 -0
- package/briefs/P6.md +3 -2
- package/briefs/P8.md +5 -4
- package/lib/client.js +18 -3
- package/lib/index.d.ts +102 -16
- package/lib/index.js +1398 -334
- package/lib/types/client/dto.d.ts +6 -0
- package/lib/types/client/viewmodel.d.ts +6 -0
- package/lib/types/constants.d.ts +2 -0
- package/lib/types/types.d.ts +14 -0
- package/package.json +1 -1
- package/skills/entry.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.5
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **语义证据跨句/跨段**:`novel_check` 的引文不再要求落在同一句;支持句末收尾引号、跨段证据,逐条返回 `pass|fail|missing|invalid|not_found|too_short|duplicate|overlap|same_sentence`,并在失败时给出 `nearest`/`conflict_with`。同一段被两条证据复用、或同一句不同子串仍按 `overlap`/`same_sentence` 拒绝;真实 r0076 的四段双句引文全部通过,r0075 的编造 POV 引文只报 `not_found`。
|
|
8
|
+
- **check submission/content 分流**:形状、缺项、引文不存在等提交错误改为 `ENGINE_CHECK_SUBMISSION_INVALID`,不写 check、不增加 blocking、不消耗 `rounds.check`;只有正文/机器门真正的 content failure 才写 check 并 `counted:true`。工具错误行会回传逐条 `items`。
|
|
9
|
+
- **world delta 双真源仲裁**:`summary.new_elements` 只作为意图声明;`state/world.yaml` 的存在性为真源。已有实体写 `created` 归一化为 `updated`,新实体写 `updated`/`no_change` 在 kind/正文可判定时归一化为 `created`;kind/detail 冲突仍拒绝。`archiveChapter` 返回 `delta_warnings` 与 `normalized_world`,warning 不再静默。
|
|
10
|
+
- **pending 只读诊断**:`inspectPending` 分类 `none|complete|untouched|partial`;status/doctor/audit/panel 对 complete/untouched 给 warning、partial 给 `ENGINE_PENDING_PARTIAL`,不再误报 hash mismatch。`novel_init` 会先恢复/拒绝崩溃 init,partial 不被 PROJECT_EXISTS 掩盖。
|
|
11
|
+
- **outline 归档不变量统一**:`novel_blueprint`、`novel_outline_import`(含 dry-run)、`novel_amend(outline)` 与 `novel_outline` 共用 `assertArchivedCardVolumes`/`assertArchivedCardsInVolumes` 和 active 卡保护;即使旧卡缺失或卷重排,归档章也必须由最终卡指向覆盖它的卷。`reindexed`/`would_reindex` 显式回显,omitted 归档卡不再伪造 `archived_at`。
|
|
12
|
+
- **summary-only 修复**:`novel_revise` 可只传 `summary`:正文 hash、章文件和历史不变,不消耗 revise 轮次,保持/推进到 P6;不要求 P4/P6 proof,接下来可直接 check/archive。summary-only 明确拒绝 `issues`/`proof`,避免静默吞掉评审/证明。P8 与工具文档已改为先 summary-only、只有正文需要改才走 P5。
|
|
13
|
+
- **PG-05 前置**:P3/P5 即检查 `summary.new_elements` 名字在正文出现、已在 world ledger 时报“remove/use updated”,并校验 `characters_appeared` 正文/叙述者别名,避免错误留到 P8。
|
|
14
|
+
- **资源余额**:`current_state.resources` 与旧余额深合并,保留兄弟资源;created/updated 都校验有限非负整数,负值/字符串/null/数组拒绝;P8 示例与错误提示改为非负。
|
|
15
|
+
|
|
16
|
+
### Hardened
|
|
17
|
+
|
|
18
|
+
- brief digest 绑定完整 project/outline/worldbook/plan/worldPlan,而不只是裁剪后的 context。
|
|
19
|
+
- characters/world 重复实体名在 load 与 amend 均拒绝;`strict_explicit` 只允许内部 provenance,公共 outline 写入拒绝该字段。
|
|
20
|
+
- FsStore 将 EACCES/EPERM/EROFS 包装为 `ENGINE_IO_PERMISSION`;rulepack 对每个 section/nested object 递归 knownKeys,包括 `reader_intrusion`/`god_view`、`genres.*` 与 `water_words.scene_thresholds`。
|
|
21
|
+
- worldbook 导出补 `数值事实`;duplicate paragraph 指回原章真实段落号;time regression 指向正文段落;timeline.characters 必须是已声明角色;export 在 artifact 缺失时使 replay 失效并重写。
|
|
22
|
+
- migration 备份 `backups/migrations/` 不参与 retention prune;无硬链接文件系统(FAT/SMB/WSL)用受控 rename fallback 回收 stale lock;面板选择中配置 projectRoot 优先于单行 registry;commands 错误行与 tools 一样保留 blocking/items。
|
|
23
|
+
|
|
24
|
+
### Migration
|
|
25
|
+
|
|
26
|
+
- check 文档 schema_version 不变;新增字段可选,旧 check 可读。旧项目已消耗的 rounds 不回滚。
|
|
27
|
+
- archive 遇到旧 `summary.new_elements` 与 ledger 冲突时按新规则成功并给 warning;不会回写旧 ledger。
|
|
28
|
+
- summary-only 是新增调用模式,旧 content/patch 修订语义不变。
|
|
29
|
+
- `strict_explicit` 若曾被外部写入,仍可被读取;新的公共写入必须改用 `strict`。
|
|
30
|
+
- 详细验收与真实项目/模型证据见 `docs/0.2.5-acceptance.md`、`docs/0.2.5-real-model-evidence.md`。
|
|
31
|
+
|
|
32
|
+
## 0.2.4
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- **BREAKING:`novel_outline` 改为按标识 upsert**:`chapters` 按 `num`、`volumes` 按 `title` 合并;省略的卡保持不动,不再因“提交集合就是全集”而静默删除。删除必须显式 `remove` / `remove_volumes`,活动章、归档章、不存在的卡和已完成卷都会被拒绝。
|
|
37
|
+
- **可解释写入**:`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`。
|
|
38
|
+
- **工具契约**:持久集合的数组参数声明 upsert/replace/remove 语义;`novel_blueprint` 的卷列表仍是 replace,但删除卷必须显式 `confirm_remove:true`。DSH 的强制 JSON Schema 子集不接受未知 `x-semantics` 关键字,因此语义落在参数描述与 `src/tools/semantics.ts` 契约表中,并由 `tests/tool-contract.spec.ts` 强制。
|
|
39
|
+
- **文档**:`novel_outline` 描述明确“upsert by key”,P2 brief 增加“未提交卡保持”的提示,工具文档与升级说明同步。
|
|
40
|
+
|
|
41
|
+
### Migration
|
|
42
|
+
|
|
43
|
+
- 老的**全量提交**调用在 upsert 下等价,无需改动。
|
|
44
|
+
- 老的**部分提交**以前会删掉未提交卡,现在只改提交的卡;这正是本次修正。
|
|
45
|
+
- 想清空计划卡:以前空数组可行,现在空数组是 no-op;必须显式 `remove: [3,4,...]`。数据格式不变,无需迁移文件。
|
|
46
|
+
- 同名卷会被新写入拒绝(upsert 以 `title` 为键,同名无法区分)。老项目只读加载仍兼容;若要提交 `volumes`/`remove_volumes`,先用 `novel_amend` 给其中一卷改名。
|
|
47
|
+
|
|
48
|
+
### Tests
|
|
49
|
+
|
|
50
|
+
- 新增 `tests/024.spec.ts`(部分 upsert、显式删除、active/unknown/submitted/archived 拒绝、dry_run 零写入、deprecated、outline 恢复、volumes upsert/remove、blueprint confirm)与 `tests/tool-contract.spec.ts`。
|
|
51
|
+
- 质量:80 个测试文件 / 848 项测试全绿;覆盖率 93.97 / 89.14 / 98.00 / 93.97,逐文件门禁通过。
|
|
52
|
+
|
|
3
53
|
## 0.2.3
|
|
4
54
|
|
|
5
55
|
### Changed
|
package/README.md
CHANGED
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
0.2.3 把“账本说真话”补到旧项目:`report` 从 journal 回填旧章 effort,quality 每章只算最终评审并区分 `samples`/`attempts`;briefs 按当前章隔离;归档失败可直接修订;advisory 按章去重并带证据;面板 v5 改为书桌式工作台,项目选择器分离写作/待修复/失效;`check:host` 不再污染真实注册表。
|
|
12
12
|
|
|
13
|
-
|
|
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.5 根治“证据/账本/恢复”三条真实链路:语义引文支持跨句/跨段并逐条解释;check 的 submission 错误不再消耗轮次;world delta 以 ledger 存在性仲裁并给 warning;pending 只读诊断与 init 崩溃恢复;归档卷/active 卡在 blueprint/import/amend/outline 全入口一致;新增 summary-only 修订(不改正文、不重跑 P4/P6);resources 深合并非负整数。P1–P3 及 S1–S8 硬化见 [0.2.5 验收](docs/0.2.5-acceptance.md)。
|
|
16
|
+
|
|
17
|
+
> **当前版本:0.2.5** · 中文界面 · MIT
|
|
14
18
|
>
|
|
15
19
|
> **宿主范围**:DSH `>=0.2.1-rc.2 <0.3.0-0`;Node `>=24.9.0`。
|
|
16
20
|
|
|
@@ -280,7 +284,7 @@ dsh plugin --profile web add dsh-novel-write-engine
|
|
|
280
284
|
固定版本:
|
|
281
285
|
|
|
282
286
|
```bash
|
|
283
|
-
dsh plugin --profile web add dsh-novel-write-engine@0.2.
|
|
287
|
+
dsh plugin --profile web add dsh-novel-write-engine@0.2.5
|
|
284
288
|
```
|
|
285
289
|
|
|
286
290
|
`dsh` 不在 PATH 时,用 `npx @deepseek-ai/dsh` 代替命令开头的 `dsh`。
|
|
@@ -335,13 +339,13 @@ corepack pnpm pack
|
|
|
335
339
|
会得到类似:
|
|
336
340
|
|
|
337
341
|
```text
|
|
338
|
-
dsh-novel-write-engine-0.2.
|
|
342
|
+
dsh-novel-write-engine-0.2.5.tgz
|
|
339
343
|
```
|
|
340
344
|
|
|
341
345
|
把这个 tarball 复制到目标机器,放在一个**稳定、不会随手删除**的路径,然后:
|
|
342
346
|
|
|
343
347
|
```bash
|
|
344
|
-
dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.
|
|
348
|
+
dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.5.tgz
|
|
345
349
|
```
|
|
346
350
|
|
|
347
351
|
tarball 安装不会运行源码构建,也不会要求 `allowBuilds`;包内已经包含 `lib/`、`rulepacks/`、`briefs/`、`skills/`、`presets/` 和两层 patch。它是最接近「发布产物」的安装方式。
|
|
@@ -356,9 +360,9 @@ tarball 安装不会运行源码构建,也不会要求 `allowBuilds`;包内
|
|
|
356
360
|
2. 点 **添加插件**(或旁边的箭头 → **安装第三方插件**)。
|
|
357
361
|
3. 在包名字段填入以下任一形式:
|
|
358
362
|
- npm 包名:`dsh-novel-write-engine`
|
|
359
|
-
- 精确版本:`dsh-novel-write-engine@0.2.
|
|
363
|
+
- 精确版本:`dsh-novel-write-engine@0.2.5`
|
|
360
364
|
- 源码目录:`/absolute/path/to/dsh-novel-write-engine`
|
|
361
|
-
- tarball:`/absolute/path/to/dsh-novel-write-engine-0.2.
|
|
365
|
+
- tarball:`/absolute/path/to/dsh-novel-write-engine-0.2.5.tgz`
|
|
362
366
|
4. npm 包名从 npm 官方源安装;本地目录和 tarball 直接使用宿主路径。
|
|
363
367
|
5. 点 **安装**,等待完成。
|
|
364
368
|
6. 如果出现 **立即启用**,点它;否则安装后到「已安装插件」里把 **dsh-novel-write-engine** 打开。
|
|
@@ -430,7 +434,7 @@ dsh --profile web --dump-config | grep -A8 '# == dsh-novel-write-engine'
|
|
|
430
434
|
先做全书蓝图、第一卷大纲和世界观设定,再写第一章的世界观约束和章节计划。
|
|
431
435
|
```
|
|
432
436
|
|
|
433
|
-
模型会调用 `novel_init` 建项目,然后按 `novel_guide` 的提示做书级规划;也可以跳过蓝图直接写第一章,严格大纲是可选开关。0.2.
|
|
437
|
+
模型会调用 `novel_init` 建项目,然后按 `novel_guide` 的提示做书级规划;也可以跳过蓝图直接写第一章,严格大纲是可选开关。0.2.5 推荐顺序:
|
|
434
438
|
|
|
435
439
|
```text
|
|
436
440
|
novel_init
|
|
@@ -546,7 +550,7 @@ novel_init
|
|
|
546
550
|
| `novel_audit` | 任意 | 读追加式 journal 审计记录,含被拒 attempt;支持 chapter/op/status/since/until/cursor | 否 |
|
|
547
551
|
| `novel_pipeline` | 任意 | 「下一步该做什么」的建议 | 否 |
|
|
548
552
|
| `novel_blueprint` | 任意 | 写书级蓝图:梗概、主题、基调、视角、读者、卖点、结局、卷结构 | 是 |
|
|
549
|
-
| `novel_outline` | 任意 |
|
|
553
|
+
| `novel_outline` | 任意 | 按 `num` upsert 章卡、按 `title` upsert 卷;省略不删,`remove`/`remove_volumes` 显式删除,`dry_run` 预览 | 是 |
|
|
550
554
|
| `novel_outline_import` | 任意 | 从 Markdown / YAML / JSON 导入大纲;`dry_run` 先预览 | 可选 |
|
|
551
555
|
| `novel_worldbook` | 任意 | 增量更新世界观设定集(设定/规则/势力/地点/角色/术语/前史) | 是 |
|
|
552
556
|
| `novel_search` | 任意 | 跨正文、摘要、大纲、世界观、台账搜索,带章号/段落号/片段 | 否 |
|
|
@@ -736,7 +740,8 @@ novel_blueprint {premise, logline, themes, tone, pov, audience,
|
|
|
736
740
|
selling_points, ending, strict, volumes}
|
|
737
741
|
|
|
738
742
|
# 2. 章卡;或从 Markdown/YAML/JSON 导入
|
|
739
|
-
novel_outline {chapters}
|
|
743
|
+
novel_outline {chapters: [{num: 1, ...}], volumes: [...]}
|
|
744
|
+
# 删除:novel_outline {remove: [2]};预览:加 dry_run:true
|
|
740
745
|
novel_outline_import {format:"markdown", content:"...", dry_run:true}
|
|
741
746
|
|
|
742
747
|
# 3. 世界观设定集(按段替换)
|
|
@@ -927,6 +932,17 @@ corepack pnpm test
|
|
|
927
932
|
|
|
928
933
|
## 十一、升级
|
|
929
934
|
|
|
935
|
+
### 11.0 从 0.2.3 升级到 0.2.4
|
|
936
|
+
|
|
937
|
+
0.2.4 不迁移 `outline.yaml` 数据,只反转 `novel_outline` 的写入口语义:
|
|
938
|
+
|
|
939
|
+
1. `chapters` 按 `num`、`volumes` 按 `title` upsert;省略的卡保持不动,不再被删除。
|
|
940
|
+
2. 删除必须显式 `remove` / `remove_volumes`;活动章、归档章、不存在的卡和已完成卷会被拒绝。
|
|
941
|
+
3. `dry_run:true` 可预演 `would_add/would_update/would_remove`;成功返回 `added/updated/removed`。
|
|
942
|
+
4. `novel_outline` 正常写入会保留 `outline/outline.deprecated.yaml`;`novel_restore {txid, paths:["outline/outline.yaml"]}` 可恢复整份大纲(要求与当前章索引一致)。
|
|
943
|
+
5. `novel_blueprint` 的卷列表仍是 replace;删卷必须 `confirm_remove:true`。
|
|
944
|
+
6. 同名卷会被新写入拒绝;老项目若已有同名卷,先用 `novel_amend` 改名再提交 volumes(只读加载仍兼容)。
|
|
945
|
+
|
|
930
946
|
### 11.0 从 0.2.2 升级到 0.2.3
|
|
931
947
|
|
|
932
948
|
0.2.3 不迁移项目数据,只改变读模型、面板 wire 版本与项目卫生:
|
|
@@ -1008,7 +1024,7 @@ dsh plugin --profile web add dsh-novel-write-engine@latest
|
|
|
1008
1024
|
也可以固定版本:
|
|
1009
1025
|
|
|
1010
1026
|
```bash
|
|
1011
|
-
dsh plugin --profile web add dsh-novel-write-engine@0.2.
|
|
1027
|
+
dsh plugin --profile web add dsh-novel-write-engine@0.2.5
|
|
1012
1028
|
```
|
|
1013
1029
|
|
|
1014
1030
|
升级后核对:
|
|
@@ -1050,7 +1066,7 @@ corepack pnpm pack
|
|
|
1050
1066
|
|
|
1051
1067
|
```bash
|
|
1052
1068
|
dsh plugin --profile web remove dsh-novel-write-engine
|
|
1053
|
-
dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.
|
|
1069
|
+
dsh plugin --profile web add /absolute/path/to/dsh-novel-write-engine-0.2.5.tgz
|
|
1054
1070
|
```
|
|
1055
1071
|
|
|
1056
1072
|
### 11.5 在 Web / Desktop 里升级
|
|
@@ -1318,6 +1334,22 @@ tests/ # 单测、集成、falsify、e2e
|
|
|
1318
1334
|
|
|
1319
1335
|
### 16.5 当前验证基线
|
|
1320
1336
|
|
|
1337
|
+
0.2.5 在 Node 24.19 / DSH 0.2.1-rc.3 上完成:
|
|
1338
|
+
|
|
1339
|
+
- `npm test` 全绿:**81 个测试文件 / 875 项**;
|
|
1340
|
+
- 覆盖率:**语句 93.51% / 分支 89.07% / 函数 98.05% / 行 93.51%**;`test:coverage` 与逐文件 domain/book/insight/viewmodel 门禁全过;
|
|
1341
|
+
- `typecheck`、`lint`、`build` 通过;`check:hygiene`(144 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` 全绿;
|
|
1342
|
+
- M1–M7、S1–S8 与 P2/P3 硬化见 [0.2.5 验收](docs/0.2.5-acceptance.md);
|
|
1343
|
+
- 真实模型:`agnes-2.5-flash` 与 `agnes-3.0-flash` 各输出一篇正文,经公开引擎 P3→P8/归档成功(score 145,stage done);P1/P2/P4/P6/P8 为确定性夹具,详见 [0.2.5 真实模型证据](docs/0.2.5-real-model-evidence.md)。
|
|
1344
|
+
|
|
1345
|
+
0.2.4 在 Node 24.19 / DSH 0.2.1-rc.3 上完成:
|
|
1346
|
+
|
|
1347
|
+
- `npm test` 全绿:**80 个测试文件 / 848 项**;
|
|
1348
|
+
- 覆盖率:**语句 93.97% / 分支 89.14% / 函数 98.00% / 行 93.97%**;`test:coverage` 与逐文件 domain/book/insight/viewmodel 门禁全过;
|
|
1349
|
+
- `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` 全绿;
|
|
1350
|
+
- 0.2.4 outline 契约、dry_run、deprecated、outline restore、volumes upsert/remove 与 blueprint confirm 见 [0.2.4 验收](docs/0.2.4-acceptance.md);
|
|
1351
|
+
- 真实模型链路:`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)。
|
|
1352
|
+
|
|
1321
1353
|
0.2.3 在 Node 24.19 / DSH 0.2.1-rc.3 上完成:
|
|
1322
1354
|
|
|
1323
1355
|
- `npm test` 全绿:**78 个测试文件 / 836 项**;
|
|
@@ -1339,6 +1371,12 @@ tests/ # 单测、集成、falsify、e2e
|
|
|
1339
1371
|
| [DESIGN.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/DESIGN.md) | 完整设计书:架构、数据模型、规则、门控、事务、面板、预设 |
|
|
1340
1372
|
| [PLAN.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/PLAN.md) | 0.1.0 实施计划(历史) |
|
|
1341
1373
|
| [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 整书生命周期:蓝图/大纲/世界观/章卡/观测 |
|
|
1374
|
+
| [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 与恢复路径的根治方案 |
|
|
1375
|
+
| [docs/0.2.5-plan.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.5-plan.md) | 0.2.5 语义证据、check 轮次、world delta、pending、outline、summary-only、资源与 P2/P3 硬化方案 |
|
|
1376
|
+
| [docs/0.2.5-acceptance.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.5-acceptance.md) | 0.2.5 验收矩阵、真实项目回归与门禁证据 |
|
|
1377
|
+
| [docs/0.2.5-real-model-evidence.md](https://github.com/ThzxxArt/dsh-novel-write-engine/blob/main/docs/0.2.5-real-model-evidence.md) | 0.2.5 agnes 2.5/3.0 正文经引擎 P3→P8/归档回执 |
|
|
1378
|
+
| [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 验收矩阵、质量门与边界 |
|
|
1379
|
+
| [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 链路回执 |
|
|
1342
1380
|
| [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 与项目卫生 |
|
|
1343
1381
|
| [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 验收矩阵、真实项目回归与质量门证据 |
|
|
1344
1382
|
| [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 与完整卷 |
|
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
|
@@ -39,6 +39,8 @@
|
|
|
39
39
|
|
|
40
40
|
`ready_to_archive` 阶段也允许直接 `novel_revise`:正文会退回 P4 重新评审,不需要先 `novel_rollback`,也不会多写一份废稿快照。
|
|
41
41
|
|
|
42
|
+
**summary-only 模式**:如果正文不需要改,只传 `{chapter, summary}`(不要传 content/patch/proof)。它只更新 draft summary,正文 hash、章历史与 revise 轮次都不变;校验 PG-05 后可继续 check/archive。summary 与正文事件不符时优先用这个模式。
|
|
43
|
+
|
|
42
44
|
## 常见返工原因
|
|
43
45
|
|
|
44
46
|
- 只改了措辞,没解决 issue 指出的信息缺口。
|
package/briefs/P6.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
]
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
五个 id 必须各出现一次;`verdict` 只能是 `pass`/`fail`;`evidence` 至少 8
|
|
25
|
+
五个 id 必须各出现一次;`verdict` 只能是 `pass`/`fail`;`evidence` 至少 8 字、必须逐字出现在正文里,可以跨句/跨段。五条引用必须互不相同、不得复用同一句,也不得与已接受引文的区间重叠;同一句不同子串、重复引用或区间重叠会被按 `same_sentence`/`overlap` 拒绝。引文不存在/太短/形状不合法属于 **submission error**(`ENGINE_CHECK_SUBMISSION_INVALID`):不写 check、不消耗轮次,修正 `semantic` 后仍调用 `novel_check`。
|
|
26
26
|
|
|
27
27
|
| id | 检查 |
|
|
28
28
|
|---|---|
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
|
|
39
39
|
- 调用时可以传 `scope`:默认 `["content","integrity"]`;只跑 `content` 或只跑 `integrity` 是合法的,空数组/未知值会被拒绝。只跑一类时另一类规则不参与本次 check,但**不会**进入 `ready_to_archive`:管线停在 P6 并返回 `CHECK_SCOPE_PARTIAL`,必须再跑一次完整 scope。
|
|
40
40
|
- 完整 scope 全过 → 管线进入 `ready_to_archive`,先 `novel_brief P8` 领取终审任务书,再调用 `novel_archive`。
|
|
41
|
-
- 任一项 `fail` →
|
|
41
|
+
- 任一项 `fail` 且引文有效 → content failure,写 check 并消耗一轮;先重新领取 `novel_brief P6`(check 已改变上下文,旧 digest 会失效),再调用 `novel_revise` 修订并回到 P4 重评。
|
|
42
|
+
- 如果只是 summary 字段与正文不符(正文无需改),用 `novel_revise {chapter, summary}` 做 summary-only 修复:不消耗 revise、不改正文,然后可直接继续 check/archive。
|
|
42
43
|
|
|
43
44
|
## 损坏与恢复
|
|
44
45
|
|
package/briefs/P8.md
CHANGED
|
@@ -81,23 +81,24 @@
|
|
|
81
81
|
}
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
错误会带上 `expected` 形态和收到的字段值,例如 `characters.陈嘉树.current_state must be an object; expected {"resources":{"港币"
|
|
84
|
+
错误会带上 `expected` 形态和收到的字段值,例如 `characters.陈嘉树.current_state must be an object; expected {"resources":{"港币":3},...}`。不要猜字段名,按报错补齐即可。`current_state.resources` 会与旧余额深合并,余额必须是有限非负整数。
|
|
85
85
|
|
|
86
86
|
## summary 与 delta 的联动(归档前必核)
|
|
87
87
|
|
|
88
88
|
`novel_archive` **不接受 summary**;它校验的是 P3/P5 已经固定的 draft summary。delta 必须与这份 summary 对齐:
|
|
89
89
|
|
|
90
90
|
- `characters` 里每个 `action:"created"` 的人物名,必须出现在 draft `summary.characters_appeared`。
|
|
91
|
-
- `world`
|
|
91
|
+
- `world` 以 `state/world.yaml` 的存在性为唯一真源:已有实体写 `created` 会按 `updated` 归一化并给 warning;新实体写 `updated` 且正文出现、kind 可确定时按 `created` 归一化;kind 冲突仍拒绝。`summary.new_elements` 只是意图声明,名字必须在正文出现,已存在实体不再强求 `created`。
|
|
92
92
|
- `timeline` 的键必须是 draft `summary.events` 的下标(字符串)。
|
|
93
93
|
- `relationships` 的两端必须已在 `characters` 里声明(本章 created 或台账已有),否则先声明人物。
|
|
94
|
+
- `timeline.<key>.characters` 里的每个名字必须已在 `characters` 台账里声明。
|
|
94
95
|
|
|
95
|
-
如果 summary
|
|
96
|
+
如果 summary 与本章事实不符,先走 **summary-only** 修复:`novel_revise {chapter, summary}`(不带 content/patch/proof)只更新 draft.summary,不消耗 revise 轮次、不改正文,然后按当前阶段继续;只有正文需要改时才走 P5 全文修订。
|
|
96
97
|
|
|
97
98
|
- **伏笔的 title 是可读标签,anchor 才是校验证据**:`title` 可以写「船费债」这样的短语;要归档,请把正文原句放进 `anchor`(summary 的 planted 项或 delta 项都行,至少 8 个字符)。没有 `anchor` 时,`title` 必须逐字出现在正文里,否则 `ENGINE_DELTA_UNVERIFIED`。示例:`{"action":"created","title":"船费债","anchor":"三百蚊的船费先欠着","description":"欠船费三百蚊"}`。
|
|
98
99
|
- 归档失败的报错会指出字段、值和缺失的锚点;不要把 title 改成碎片来“凑命中”,补 `anchor` 即可。
|
|
99
100
|
- 第一人称的叙述者若在 `narrator` 里声明为 `陈默(第一人称「我」)`,正文用「我」也视为出现,但角色键仍写 `陈默`。
|
|
100
|
-
-
|
|
101
|
+
- 每个新实体写 `action: "created"`(正文出现、kind 可确定);已存在实体写 `updated` 或 `no_change`,引擎会按台账归一化冲突并给 warning。
|
|
101
102
|
- `world` 新实体还要写 `kind: "location"|"ability"|"artifact"|"faction"|"rule"` 和 `description`。
|
|
102
103
|
- `state_delta` 可以是对象,也可以是 JSON 字符串;引擎两者都接受。
|
|
103
104
|
- 未知伏笔 key、未声明角色、`no_change` 缺原因都会被 `ENGINE_DELTA_UNVERIFIED` 拒绝,引擎不会猜。
|
package/lib/client.js
CHANGED
|
@@ -166,6 +166,7 @@ window.__ModuleLoader__.load({
|
|
|
166
166
|
advisories: [],
|
|
167
167
|
acknowledgedAdvisories: 0,
|
|
168
168
|
legacy: [],
|
|
169
|
+
pending: null,
|
|
169
170
|
nextTitle: "",
|
|
170
171
|
nextDetail: "",
|
|
171
172
|
nextTool: "",
|
|
@@ -392,6 +393,7 @@ window.__ModuleLoader__.load({
|
|
|
392
393
|
})),
|
|
393
394
|
acknowledgedAdvisories: summary.acknowledgedAdvisories ?? 0,
|
|
394
395
|
legacy: summary.legacy ?? [],
|
|
396
|
+
pending: summary.pending ?? null,
|
|
395
397
|
nextTitle: finished ? "" : next === void 0 ? "状态已同步" : toolLabel(next.tool),
|
|
396
398
|
nextDetail: finished ? "" : next === void 0 ? "没有待执行的下一步,刷新查看最新状态。" : NEXT_DETAILS[next.tool] ?? next.reason,
|
|
397
399
|
nextTool: next?.tool ?? "",
|
|
@@ -1005,6 +1007,11 @@ window.__ModuleLoader__.load({
|
|
|
1005
1007
|
color: var(--nwe-muted);
|
|
1006
1008
|
font-size: 11px;
|
|
1007
1009
|
}
|
|
1010
|
+
|
|
1011
|
+
.nwe-pending-section { border-left: 3px solid #c9a227; padding-left: 10px; }
|
|
1012
|
+
.nwe-pending-error { border-left-color: #b3261e; }
|
|
1013
|
+
.nwe-pending-detail { margin: 8px 0 0; color: var(--nwe-ink); font-size: 11px; line-height: 1.6; }
|
|
1014
|
+
.nwe-pending-hint { margin: 4px 0 0; color: var(--nwe-muted); font-size: 10px; line-height: 1.5; }
|
|
1008
1015
|
.nwe-legacy {
|
|
1009
1016
|
display: flex;
|
|
1010
1017
|
gap: 8px;
|
|
@@ -1171,7 +1178,7 @@ brief P6 → check → brief P8 → archive 的顺序推进。
|
|
|
1171
1178
|
如果当前会话还没绑定或不确定是哪本书:先用 /novel current 看会话绑定,再调用 novel_status 看 selection;需要切换时用 /novel use <路径|书名|.>,不要靠 active 猜。
|
|
1172
1179
|
第一次调用带 project_root 时,它会成为当前会话的持久绑定;后续无 root 调用会继续这本书。
|
|
1173
1180
|
|
|
1174
|
-
|
|
1181
|
+
如果下一章还没有章卡:用 novel_outline 只提交下一章的卡(按 num upsert),省略的已有卡会保持不动;不要重交全量,也不要用空数组清空。要删卡必须显式传 remove。
|
|
1175
1182
|
如果 targets.total_chapters 已经写满:先用 novel_amend 把 total_chapters 调到至少包含下一章,不能低于已归档章数。
|
|
1176
1183
|
|
|
1177
1184
|
之后严格按:novel_brief P1 → novel_plan_world → brief P2 → novel_plan_chapter → brief P3 → novel_write_draft → brief P4 → novel_review →(有 blocker)brief P5 → novel_revise → 回 P4 → brief P6 → novel_check → brief P8 → novel_archive 的顺序推进。
|
|
@@ -1266,6 +1273,14 @@ brief P6 → check → brief P8 → archive 的顺序推进。
|
|
|
1266
1273
|
key: `${item.code}-${item.detail}`
|
|
1267
1274
|
}, (0, react.createElement)("code", { className: "nwe-advisory-code" }, item.code), (0, react.createElement)("span", { className: "nwe-advisory-detail" }, item.detail), item.hint === "" ? null : (0, react.createElement)("span", { className: "nwe-advisory-hint" }, item.hint)))));
|
|
1268
1275
|
};
|
|
1276
|
+
const pendingSection = (view) => {
|
|
1277
|
+
if (view.pending === null) return null;
|
|
1278
|
+
const partial = view.pending.state === "partial";
|
|
1279
|
+
return (0, react.createElement)("section", {
|
|
1280
|
+
className: `nwe-section nwe-pending-section${partial ? " nwe-pending-error" : ""}`,
|
|
1281
|
+
"aria-label": "待恢复事务"
|
|
1282
|
+
}, (0, react.createElement)("div", { className: "nwe-section-head" }, (0, react.createElement)("h2", { className: "nwe-section-title" }, partial ? "事务部分完成" : "待恢复事务"), (0, react.createElement)("span", { className: "nwe-section-note" }, view.pending.state)), (0, react.createElement)("p", { className: "nwe-pending-detail" }, view.pending.detail), (0, react.createElement)("p", { className: "nwe-pending-hint" }, partial ? `检查 backups 与 .staging/${view.pending.txid}/before/,不要整项目恢复。` : "下一次写操作会自动收尾;只读面不会改动事务。"));
|
|
1283
|
+
};
|
|
1269
1284
|
const legacySection = (view) => {
|
|
1270
1285
|
if (view.legacy.length === 0) return null;
|
|
1271
1286
|
return (0, react.createElement)("section", {
|
|
@@ -1457,7 +1472,7 @@ brief P6 → check → brief P8 → archive 的顺序推进。
|
|
|
1457
1472
|
ref: closeRef,
|
|
1458
1473
|
onClick: onClose,
|
|
1459
1474
|
"aria-label": "关闭使用教程"
|
|
1460
|
-
}, (0, react.createElement)("span", { "aria-hidden": "true" }, "×"))), (0, react.createElement)("div", { className: "nwe-modal-body" }, (0, react.createElement)("div", { className: "nwe-tutorial-intro" }, (0, react.createElement)("p", null, "网文工坊不是一个“帮你想剧情”的按钮,而是一台有状态的写作引擎:你提供内容,引擎负责阶段、字数、门控、评审、归档和文件。"), (0, react.createElement)("p", null, "每一步都先领取当前阶段的任务书,再提交;提交后看引擎返回的 blocking 与下一步。面板只读,真正的写操作都在对话里的工具调用中。")), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "开始前:记住三件事"), (0, react.createElement)("div", { className: "nwe-tutorial-cards" }, (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "只用引擎入口写"), (0, react.createElement)("span", null, "正文只走 novel_write_draft / novel_revise;状态只走工具。手改 chapters/、state/、outline/ 会破坏事务与校验。")), (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "每一步先领 brief"), (0, react.createElement)("span", null, "任务书携带当前上下文和 digest;跳过它会撞 PG-01。任务书过期就重新领取,不要复用旧上下文。")), (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "失败先读 code"), (0, react.createElement)("span", null, "参数错改参数,门控没过改正文,系统故障查目录/稍后重试。blocking 会一次列全,一次改完再交。"))), (0, react.createElement)("p", { className: "nwe-tutorial-note" }, (0, react.createElement)("strong", null, "用哪个会话:"), " 想获得完整的引擎保证,请新开一个「网文工坊」预设会话;普通会话里模型可能还挂着文件工具,能绕过引擎直接改稿。")), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "一本书的完整流程"), (0, react.createElement)("p", null, "前四步是开书准备,后面是每章循环。第一次先按顺序走完一章,熟悉后再批量推进整本。"), (0, react.createElement)("ol", { className: "nwe-steps" }, ...TUTORIAL_STEPS.map((step, index) => tutorialStep(step, index)))), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "面板怎么看"), (0, react.createElement)("div", { className: "nwe-tutorial-cards" }, (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "顶部统计"), (0, react.createElement)("span", null, "已归档章、总字数、平均分、待处理门控。数字来自引擎,不是正文估算。")), (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "九阶段轨与下一步"), (0, react.createElement)("span", null, "亮起的阶段是当前进度,“下一步”卡会直接给出该调用的工具名。")), (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "章节与门控"), (0, react.createElement)("span", null, "章节卡展开可看字数、评分与状态;门控区按 code/detail/hint 列出所有阻塞。"))), (0, react.createElement)("p", { className: "nwe-tutorial-note" }, (0, react.createElement)("strong", null, "刷新:"), " 面板每 15 秒静默同步一次,也可以点右上角刷新。它是只读投影;不会替你改稿、归档或修状态。"), (0, react.createElement)("p", { className: "nwe-tutorial-note" }, (0, react.createElement)("strong", null, "项目状态:"), "选择器里 choose 表示发现多本书、还没选;项目不在了表示目录缺失,绑定仍会保留;不是项目表示目录还在但没有 novel.yaml 或项目状态;项目不可读表示权限或 IO 错误,先修权限或换一本。面板选择只影响查看,不改变任何会话的写作绑定。")), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "卡住时先看这里"), (0, react.createElement)("ul", { className: "nwe-faq" }, (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "为什么一直让我先领 brief?"), (0, react.createElement)("p", null, "这是正常门控。任务书决定当前阶段可用的字段与上下文 digest;先按返回值调用 novel_brief,再继续。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "门控失败,改一条还是全改?"), (0, react.createElement)("p", null, "一次看完所有 blocking,再一起改。逐条试会反复烧掉阶段轮次;错误里的 code 会告诉你属于参数、门控还是系统故障。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "字数和评分为什么对不上?"), (0, react.createElement)("p", null, "字数以 engine 返回为准,可用 novel_word_count 核对;review 的 total 必须等于 150 减全部扣分,不能另算一套。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "P4/P6 过不去怎么办?"), (0, react.createElement)("p", null, "P4 有 blocker 就领 P5 任务书修订,再回 P4 重评;P6 失败同样回修订。不要 waive blocker,也不要只跑半个 scope 就归档。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "想改已经归档的章节?"), (0, react.createElement)("p", null, "先 novel_reopen,带非空 reason。它会从归档备份恢复,重开后重新走 P3→P8;不要直接编辑归档卡或正文文件。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "状态文档坏了能手工修吗?"), (0, react.createElement)("p", null, "先用 novel_state 看 raw 和 next;需要修复时用 novel_amend,并在 reason 写清原因。手改 YAML 会丢失事务保护。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "有多个项目,面板看的是哪本?"), (0, react.createElement)("p", null, "面板用顶部项目选择器切换查看对象;选择只改 ?root= 和浏览器 localStorage,不调用 activate、不写会话绑定。写作项目请用 /novel use 或显式 project_root。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "选错书、想清掉绑定怎么办?"), (0, react.createElement)("p", null, "先用 /novel current 看当前会话绑定;要换书用 /novel use <路径|书名|.>,要清掉只影响当前会话的绑定用 /novel unbind。不要靠 active 猜,也不要手改 state/session-bindings.yaml。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "扫描到了,为什么还没切过去?"), (0, react.createElement)("p", null, "scan 只读发现;scan --register 只登记项目,不选择、不改 active、不绑会话。真正切换写作项目要用 /novel use,或第一次调用显式传 project_root。")))), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "可以直接复制给模型的起手话术"), (0, react.createElement)("pre", { className: "nwe-prompt" }, TUTORIAL_PROMPT)), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "续写下一章时,直接复制这段给 Agent"), (0, react.createElement)("p", null, "这段会先确认状态,再处理缺章卡和目标章数已满,最后按 P1→P8 推进;归档前会要求你确认 state_delta。"), (0, react.createElement)("pre", { className: "nwe-prompt" }, TUTORIAL_NEXT_PROMPT)), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "一章完成检查清单"), (0, react.createElement)("ul", { className: "nwe-checklist" }, (0, react.createElement)("li", null, "本章 P1/P2 计划已提交,P3 正文已通过格式与字数门控。"), (0, react.createElement)("li", null, "P4 评审 total 对账无误,没有未修 blocker。"), (0, react.createElement)("li", null, "P6 五个语义项都有不同句子的正文引用,完整 scope 通过。"), (0, react.createElement)("li", null, "P8 任务书当前,novel_archive 带完整 state_delta 成功。"), (0, react.createElement)("li", null, "面板出现已归档章节,门控区没有新的阻塞。")))), (0, react.createElement)("div", { className: "nwe-modal-foot" }, "教程基于 0.2.
|
|
1475
|
+
}, (0, react.createElement)("span", { "aria-hidden": "true" }, "×"))), (0, react.createElement)("div", { className: "nwe-modal-body" }, (0, react.createElement)("div", { className: "nwe-tutorial-intro" }, (0, react.createElement)("p", null, "网文工坊不是一个“帮你想剧情”的按钮,而是一台有状态的写作引擎:你提供内容,引擎负责阶段、字数、门控、评审、归档和文件。"), (0, react.createElement)("p", null, "每一步都先领取当前阶段的任务书,再提交;提交后看引擎返回的 blocking 与下一步。面板只读,真正的写操作都在对话里的工具调用中。")), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "开始前:记住三件事"), (0, react.createElement)("div", { className: "nwe-tutorial-cards" }, (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "只用引擎入口写"), (0, react.createElement)("span", null, "正文只走 novel_write_draft / novel_revise;状态只走工具。手改 chapters/、state/、outline/ 会破坏事务与校验。")), (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "每一步先领 brief"), (0, react.createElement)("span", null, "任务书携带当前上下文和 digest;跳过它会撞 PG-01。任务书过期就重新领取,不要复用旧上下文。")), (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "失败先读 code"), (0, react.createElement)("span", null, "参数错改参数,门控没过改正文,系统故障查目录/稍后重试。blocking 会一次列全,一次改完再交。"))), (0, react.createElement)("p", { className: "nwe-tutorial-note" }, (0, react.createElement)("strong", null, "用哪个会话:"), " 想获得完整的引擎保证,请新开一个「网文工坊」预设会话;普通会话里模型可能还挂着文件工具,能绕过引擎直接改稿。")), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "一本书的完整流程"), (0, react.createElement)("p", null, "前四步是开书准备,后面是每章循环。第一次先按顺序走完一章,熟悉后再批量推进整本。"), (0, react.createElement)("ol", { className: "nwe-steps" }, ...TUTORIAL_STEPS.map((step, index) => tutorialStep(step, index)))), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "面板怎么看"), (0, react.createElement)("div", { className: "nwe-tutorial-cards" }, (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "顶部统计"), (0, react.createElement)("span", null, "已归档章、总字数、平均分、待处理门控。数字来自引擎,不是正文估算。")), (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "九阶段轨与下一步"), (0, react.createElement)("span", null, "亮起的阶段是当前进度,“下一步”卡会直接给出该调用的工具名。")), (0, react.createElement)("div", { className: "nwe-tutorial-card" }, (0, react.createElement)("strong", null, "章节与门控"), (0, react.createElement)("span", null, "章节卡展开可看字数、评分与状态;门控区按 code/detail/hint 列出所有阻塞。"))), (0, react.createElement)("p", { className: "nwe-tutorial-note" }, (0, react.createElement)("strong", null, "刷新:"), " 面板每 15 秒静默同步一次,也可以点右上角刷新。它是只读投影;不会替你改稿、归档或修状态。"), (0, react.createElement)("p", { className: "nwe-tutorial-note" }, (0, react.createElement)("strong", null, "项目状态:"), "选择器里 choose 表示发现多本书、还没选;项目不在了表示目录缺失,绑定仍会保留;不是项目表示目录还在但没有 novel.yaml 或项目状态;项目不可读表示权限或 IO 错误,先修权限或换一本。面板选择只影响查看,不改变任何会话的写作绑定。")), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "卡住时先看这里"), (0, react.createElement)("ul", { className: "nwe-faq" }, (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "为什么一直让我先领 brief?"), (0, react.createElement)("p", null, "这是正常门控。任务书决定当前阶段可用的字段与上下文 digest;先按返回值调用 novel_brief,再继续。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "门控失败,改一条还是全改?"), (0, react.createElement)("p", null, "一次看完所有 blocking,再一起改。逐条试会反复烧掉阶段轮次;错误里的 code 会告诉你属于参数、门控还是系统故障。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "字数和评分为什么对不上?"), (0, react.createElement)("p", null, "字数以 engine 返回为准,可用 novel_word_count 核对;review 的 total 必须等于 150 减全部扣分,不能另算一套。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "P4/P6 过不去怎么办?"), (0, react.createElement)("p", null, "P4 有 blocker 就领 P5 任务书修订,再回 P4 重评;P6 失败同样回修订。不要 waive blocker,也不要只跑半个 scope 就归档。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "想改已经归档的章节?"), (0, react.createElement)("p", null, "先 novel_reopen,带非空 reason。它会从归档备份恢复,重开后重新走 P3→P8;不要直接编辑归档卡或正文文件。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "状态文档坏了能手工修吗?"), (0, react.createElement)("p", null, "先用 novel_state 看 raw 和 next;需要修复时用 novel_amend,并在 reason 写清原因。手改 YAML 会丢失事务保护。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "有多个项目,面板看的是哪本?"), (0, react.createElement)("p", null, "面板用顶部项目选择器切换查看对象;选择只改 ?root= 和浏览器 localStorage,不调用 activate、不写会话绑定。写作项目请用 /novel use 或显式 project_root。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "选错书、想清掉绑定怎么办?"), (0, react.createElement)("p", null, "先用 /novel current 看当前会话绑定;要换书用 /novel use <路径|书名|.>,要清掉只影响当前会话的绑定用 /novel unbind。不要靠 active 猜,也不要手改 state/session-bindings.yaml。")), (0, react.createElement)("li", null, (0, react.createElement)("strong", null, "扫描到了,为什么还没切过去?"), (0, react.createElement)("p", null, "scan 只读发现;scan --register 只登记项目,不选择、不改 active、不绑会话。真正切换写作项目要用 /novel use,或第一次调用显式传 project_root。")))), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "可以直接复制给模型的起手话术"), (0, react.createElement)("pre", { className: "nwe-prompt" }, TUTORIAL_PROMPT)), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "续写下一章时,直接复制这段给 Agent"), (0, react.createElement)("p", null, "这段会先确认状态,再处理缺章卡和目标章数已满,最后按 P1→P8 推进;归档前会要求你确认 state_delta。"), (0, react.createElement)("pre", { className: "nwe-prompt" }, TUTORIAL_NEXT_PROMPT)), (0, react.createElement)("section", { className: "nwe-tutorial-block" }, (0, react.createElement)("h3", null, "一章完成检查清单"), (0, react.createElement)("ul", { className: "nwe-checklist" }, (0, react.createElement)("li", null, "本章 P1/P2 计划已提交,P3 正文已通过格式与字数门控。"), (0, react.createElement)("li", null, "P4 评审 total 对账无误,没有未修 blocker。"), (0, react.createElement)("li", null, "P6 五个语义项都有不同句子的正文引用,完整 scope 通过。"), (0, react.createElement)("li", null, "P8 任务书当前,novel_archive 带完整 state_delta 成功。"), (0, react.createElement)("li", null, "面板出现已归档章节,门控区没有新的阻塞。")))), (0, react.createElement)("div", { className: "nwe-modal-foot" }, "教程基于 0.2.4 · 具体字段以当前任务书和工具返回为准 · 面板只读")));
|
|
1461
1476
|
const NovelHelp = () => {
|
|
1462
1477
|
const [open, setOpen] = (0, react.useState)(false);
|
|
1463
1478
|
const triggerRef = (0, react.useRef)(null);
|
|
@@ -1553,7 +1568,7 @@ brief P6 → check → brief P8 → archive 的顺序推进。
|
|
|
1553
1568
|
className: "nwe-stats",
|
|
1554
1569
|
role: "group",
|
|
1555
1570
|
"aria-label": "项目统计"
|
|
1556
|
-
}, stat("已归档", String(view.stats.chapters), "章"), stat("总字数", view.stats.words.toLocaleString("zh-CN"), "字"), stat("全书均分", view.stats.chapters === 0 ? "—" : String(view.stats.averageScore), "/150"), stat("待处理", String(view.stats.blocking), "项")) : null, view.state === "loading" ? loadingState() : null, view.state === "choose" ? choosePanel(view, onSelectProject) : null, view.state === "missing" ? missingPanel(view) : null, view.state === "error" || view.state === "empty" || view.state === "outdated" ? statePanel(view.state, view, loading, onRefresh) : null, ready ? (0, react.createElement)("div", { className: "nwe-desk" }, (0, react.createElement)("div", { className: "nwe-desk-main" }, (0, react.createElement)("div", { className: "nwe-desk-heading" }, (0, react.createElement)("span", { className: "nwe-desk-kicker" }, "当前一章"), (0, react.createElement)("span", { className: "nwe-desk-note" }, view.stageLabel)), phases(view), blockingSection(view), advisoriesSection(view), legacySection(view), chaptersSection(view)), (0, react.createElement)("aside", {
|
|
1571
|
+
}, stat("已归档", String(view.stats.chapters), "章"), stat("总字数", view.stats.words.toLocaleString("zh-CN"), "字"), stat("全书均分", view.stats.chapters === 0 ? "—" : String(view.stats.averageScore), "/150"), stat("待处理", String(view.stats.blocking), "项")) : null, view.state === "loading" ? loadingState() : null, view.state === "choose" ? choosePanel(view, onSelectProject) : null, view.state === "missing" ? missingPanel(view) : null, view.state === "error" || view.state === "empty" || view.state === "outdated" ? statePanel(view.state, view, loading, onRefresh) : null, ready ? (0, react.createElement)("div", { className: "nwe-desk" }, (0, react.createElement)("div", { className: "nwe-desk-main" }, (0, react.createElement)("div", { className: "nwe-desk-heading" }, (0, react.createElement)("span", { className: "nwe-desk-kicker" }, "当前一章"), (0, react.createElement)("span", { className: "nwe-desk-note" }, view.stageLabel)), phases(view), blockingSection(view), advisoriesSection(view), pendingSection(view), legacySection(view), chaptersSection(view)), (0, react.createElement)("aside", {
|
|
1557
1572
|
className: "nwe-desk-side",
|
|
1558
1573
|
"aria-label": "全书与活动"
|
|
1559
1574
|
}, bookSection(view), activitySection(view))) : null, footer());
|