Bläddra i källkod

merge: context minimal flow refactor

lingfengQAQ 2 veckor sedan
förälder
incheckning
a1c19456b5
25 ändrade filer med 1262 tillägg och 2226 borttagningar
  1. 162 0
      docs/architecture/phase0-slimming-and-read-audit-2026-06-06.md
  2. 228 0
      docs/architecture/phase0-tool-and-registration-audit-2026-06-06.md
  3. 33 124
      webnovel-writer/agents/context-agent.md
  4. 37 46
      webnovel-writer/agents/data-agent.md
  5. 79 243
      webnovel-writer/agents/deconstruction-agent.md
  6. 8 16
      webnovel-writer/agents/reviewer.md
  7. 5 0
      webnovel-writer/evals/fixtures/behavior/fast.json
  8. 2 3
      webnovel-writer/references/index/reference-gap-register.md
  9. 47 40
      webnovel-writer/references/index/reference-loading-map.md
  10. 34 0
      webnovel-writer/scripts/data_modules/tests/test_artifact_validator.py
  11. 192 22
      webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py
  12. 48 0
      webnovel-writer/scripts/data_modules/tests/test_write_gates.py
  13. 30 0
      webnovel-writer/scripts/run_behavior_evals.py
  14. 19 32
      webnovel-writer/skills/webnovel-dashboard/SKILL.md
  15. 9 30
      webnovel-writer/skills/webnovel-doctor/SKILL.md
  16. 77 251
      webnovel-writer/skills/webnovel-init/SKILL.md
  17. 14 40
      webnovel-writer/skills/webnovel-learn/SKILL.md
  18. 92 253
      webnovel-writer/skills/webnovel-plan/SKILL.md
  19. 37 33
      webnovel-writer/skills/webnovel-query/SKILL.md
  20. 41 101
      webnovel-writer/skills/webnovel-review/SKILL.md
  21. 60 24
      webnovel-writer/skills/webnovel-write/SKILL.md
  22. 2 224
      webnovel-writer/skills/webnovel-write/references/writing/combat-scenes.md
  23. 2 226
      webnovel-writer/skills/webnovel-write/references/writing/dialogue-writing.md
  24. 2 260
      webnovel-writer/skills/webnovel-write/references/writing/emotion-psychology.md
  25. 2 258
      webnovel-writer/skills/webnovel-write/references/writing/scene-description.md

+ 162 - 0
docs/architecture/phase0-slimming-and-read-audit-2026-06-06.md

@@ -0,0 +1,162 @@
+# Phase 0 审计:瘦身与读取方式清单(2026-06-06)
+
+> 只读审计 + 实测。本文件只「记录可追踪结论」,不修改任何 skill / agent / reference / 测试文件。
+> 目的:把 `context-minimal-writing-flow-plan-2026-06-05.md`(下称 Plan)第 4 节四条裁剪判据、§4.2 职责表、§6.2 读取方式,落成各瘦身 Phase 可直接照做的操作清单。
+> 不复述 Plan,只给「文件 → 读取方式 / 归属 / 处理 Task」的可执行行。
+>
+> 工作目录(git 仓库根 = worktree):`D:/wk/novel skill/webnovel-writer/.worktrees/context-minimal-flow`
+> 插件目录(嵌套):仓库根下 `webnovel-writer/` 子目录。下表路径均相对插件目录。
+> 行数列为 `wc -l` 实测(2026-06-06,本机 Bash),不沿用 Plan 任何预填数字。
+
+---
+
+## 用法
+
+- **Section A** 覆盖 Plan §6.2.1 reference-loading-map「直接 Read 的 md」全部条目,逐条定读取方式;Phase 4(Task #13/#14)按此改读取、清死文件。
+- **Section B** 覆盖 8 Skill + 4 Agent,按 §4.2 职责表给保留要点与可压缩项;Phase 1–5(Task #5–#16)按此瘦身。
+- 读取方式四分类(判据四 + §6.2.2):**全文读** / **区段读** / **检索读**(CSV,单列于 A.3) / **不读**(清理候选)。
+- 区段读锚点列给的是**文件里的真实标题原文**(已 Grep `^#{1,4} ` 核实)。Plan 正文锚点用了简写(去掉「、」、缩短标题),与真实标题**不逐字相等**;区段读时必须按本表真实锚点匹配,否则 `Grep ^## 一 ` 会匹配不到 `## 一、`。差异见 A.4「锚点校验与 FLAG」。
+
+---
+
+## Section A — 读取方式审计(逐个「直接 Read 的 md」)
+
+行数 = 实测。「处理 Task」:靶心大文件区段读 = #13;死文件清理 = #14;其余「按需 / 全文」随其消费 Skill 的瘦身 Task 走(见 Section B),读取方式本身不单独占 Task,但 Phase 4 登记 loading-map 时一并写入(#13)。
+
+### A.1 七个靶心大文件(区段读 → Task #13)
+
+`always` / 高频触发的大 md,是 init / plan / write 每跑必吞的常驻成本。锚点已逐个 Grep 核实存在。
+
+| 文件 | 实测行数 | 读取方式 | 区段:真实稳定标题锚点(原文) | 处理 Task |
+|---|---|---|---|---|
+| `references/genre-profiles.md` | 696 | 区段读 | 当前 genre 的**单个** `### 2.x`(`### 2.1`…`### 2.13`,13 题材各约 44 行);按需加 `## 一、Profile 字段说明`。单本书只用 1 题材 → 省约 90% | #13 |
+| `skills/webnovel-init/references/creativity/selling-points.md` | 687 | 区段读 | `## 9. 核心卖点定位模板`(骨架);按需补 `### 1.3 核心卖点黄金公式`、`## 7. 实战检查清单` | #13 |
+| `references/reading-power-taxonomy.md` | 361 | 区段读 | 按需取 `## 一、钩子类型` / `## 二、爽点模式` / `## 三、即时满足/微兑现` | #13 |
+| `skills/webnovel-plan/references/outlining/chapter-planning.md` | 322 | 区段读 | `## 10. 结构化节点规范(CBN/CPNs/CEN)`(L299,至文件末);需模板时加 `## 7. 章节规划模板` | #13 |
+| `skills/webnovel-init/references/creativity/creativity-constraints.md` | 327 | 区段读 | 展示评分取 `### 8.1 五维评分`(L262,约 10 行);创意采集读 `## 一、创意包 Schema (Idea Package)` / `## 六、硬约束驱动创意 (Hard Constraints)` / `## 八、评分系统 (Scoring System)` | #13 |
+| `skills/webnovel-write/references/polish-guide.md` | 351 | 区段读 | 主路径 `## 2. 执行顺序(必须按序)`;Anti-AI 词库单独区段 `## 98:Phase 1 增补:Anti-AI 规范`(另有 `## 2A. Anti-AI 检测细则`)。**不可条目化进 CSV(csv/README 硬边界)** | #13 |
+| `references/shared/cool-points-guide.md` | 313 | 区段读 | 所需爽点维度段;题材适配取 `## 九、题材适配`(L194)下对应题材 | #13 |
+
+> 区段读手法:先 `Grep` content 输出匹配 `^#{1,4} ` 拿锚点行号,再 `Read` offset/limit 取段。两者均 Claude Code 内置,平台无关。
+
+### A.2 其余「直接 Read 的 md」(全文 / 区段 / 不读)
+
+判据:≤ ~150 行且需整体理解 → 全文读;明显大且只取某节、且 Plan 标了「按需 / 需X」触发 → 区段读;当前非直接调用 → 不读。
+
+| 文件 | 实测行数 | 读取方式 | 区段:真实锚点(若区段读) | 处理 Task |
+|---|---|---|---|---|
+| **webnovel-init** | | | | |
+| `skills/webnovel-init/references/system-data-flow.md` | 43 | 全文读 | — | #10 |
+| `skills/webnovel-init/references/genre-tropes.md` | 183 | 区段读 | 当前题材段(题材套路库,按 genre 取对应小节) | #10 |
+| `skills/webnovel-init/references/worldbuilding/world-rules.md` | 86 | 全文读 | — | #10 |
+| `skills/webnovel-init/references/worldbuilding/faction-systems.md` | 179 | 区段读(按需) | always 触发但偏长;按当前世界观需要的小节取 | #10 |
+| `skills/webnovel-init/references/worldbuilding/power-systems.md` | 160 | 区段读(按需) | 仅「力量体系」相关项触发;取对应小节 | #10 |
+| `skills/webnovel-init/references/worldbuilding/character-design.md` | 111 | 全文读 | — | #10 |
+| `skills/webnovel-init/references/worldbuilding/setting-consistency.md` | 215 | 区段读 | always 触发但偏长;取一致性校验小节 | #10 |
+| `skills/webnovel-init/references/creativity/creative-combination.md` | 510 | 区段读 | **非 7 靶心但 510 行**:仅「需创意组合」触发,按当前混搭轴取小节,禁止全文 | #10 |
+| `skills/webnovel-init/references/creativity/inspiration-collection.md` | 298 | 区段读 | 仅 Step1.5 灵感来源询问触发;取所需采集小节 | #10 |
+| `skills/webnovel-init/references/creativity/anti-trope-game.md` | 170 | 区段读(按需) | 仅 game 题材 + 「需反套路」触发;取对应反套路项 | #10 |
+| `skills/webnovel-init/references/creativity/anti-trope-rules-mystery.md` | 214 | 区段读(按需) | 仅 rules-mystery 题材触发 | #10 |
+| `skills/webnovel-init/references/creativity/anti-trope-urban.md` | 169 | 区段读(按需) | 仅 urban 题材触发 | #10 |
+| `skills/webnovel-init/references/creativity/anti-trope-xianxia.md` | 159 | 区段读(按需) | 仅 xianxia 题材触发 | #10 |
+| **webnovel-plan** | | | | |
+| `templates/output/大纲-卷节拍表.md` | 38 | 全文读 | — 模板,须整体套用 | #11 |
+| `templates/output/大纲-卷时间线.md` | 51 | 全文读 | — 模板,须整体套用 | #11 |
+| `references/shared/strand-weave-pattern.md` | 111 | 全文读 | — Plan §6.2.2 明示短文件维持全文 | #11 |
+| `references/outlining/plot-signal-vs-spoiler.md` | 53 | 全文读 | — | #11 |
+| `skills/webnovel-plan/references/outlining/conflict-design.md` | 277 | 区段读 | 仅「需冲突」触发;取对应冲突类型小节 | #11 |
+| `skills/webnovel-plan/references/outlining/genre-volume-pacing.md` | 84 | 全文读 | — 短,卷级节奏整体读 | #11 |
+| **webnovel-write** | | | | |
+| `skills/webnovel-write/references/writing/typesetting.md` | 60 | 全文读 | — Step4 always,短 | #5 |
+| `skills/webnovel-write/references/style-adapter.md` | 71 | 全文读 | — Step4 always,短 | #5 |
+| **webnovel-review** | | | | |
+| `references/shared/core-constraints.md` | 111 | 全文读 | — always 铁律,须整体 | #8/#12 |
+| `references/review-schema.md` | 59 | 全文读 | — schema,须整体 | #8/#12 |
+| `references/review/blocking-override-guidelines.md` | 47 | 全文读 | — 按需,短 | #12 |
+| **webnovel-query** | | | | |
+| `skills/webnovel-query/references/system-data-flow.md` | 343 | 区段读 | 偏长,按查询类型取数据源优先级小节 | #15 |
+| `skills/webnovel-query/references/advanced/foreshadowing.md` | 120 | 全文读 | — 按需,伏笔查询整体读 | #15 |
+| `skills/webnovel-query/references/tag-specification.md` | 154 | 全文读(边界) | — 154 行、tag 规范须整体;如证明只用单段可降区段读 | #15 |
+
+> `cool-points-guide.md`(313)、`strand-weave-pattern.md`(111) 同时被 plan/review 直接 Read:前者已入 A.1 靶心区段读;后者全文读,跨 Skill 共用同一结论。
+
+### A.3 检索读(CSV-backed,不在「直接 Read md」清单,单列)
+
+Plan §6.2.1 明示 CSV 那条线「已做对,本轮不重做」。这里只登记,**不改造**;Phase 4 登记 loading-map 时保持现状。
+
+| 数据源 | 读取方式 | 调用 | 备注 |
+|---|---|---|---|
+| `references/csv/场景写法.csv` | 检索读 | `reference_search.py --table 场景写法 --query ... --genre ...` | write Step2 战斗/对峙/桥段;承接已下线的 combat-scenes 等 md |
+| `references/csv/写作技法.csv` | 检索读 | `reference_search.py --table 写作技法 --query ...` | write Step2 对话/情感;承接 dialogue-writing / emotion-psychology |
+| `references/csv/题材与调性推理.csv`、`裁决规则.csv` 等 8 表 | 检索读(间接) | `story-system` 内部 `_route()` / `_collect_tables()` / `_load_reasoning()` | init/write 经 story-system 间接消费,已按需 |
+
+> 现存 CSV:`裁决规则 / 场景写法 / 金手指与设定 / 命名规则 / 桥段套路 / 人设与关系 / 爽点与节奏 / 题材与调性推理 / 写作技法`(9 表)+ `genre-canonical.md` + `README.md`。
+
+### A.4 不读(Phase 4 清理候选 → Task #14)
+
+loading-map「当前非直接调用项」+ Plan §6.2.3 点名的 writing/* 迁移候选。合计约 1402 行死内容(实测)。处置:先核 CSV 覆盖,已覆盖则删或留空壳指向 CSV,未覆盖先补 CSV 再处置。
+
+| 文件 | 实测行数 | 现状 / CSV 承接 | 处理 Task |
+|---|---|---|---|
+| `skills/webnovel-write/references/writing/combat-scenes.md` | 229 | 战斗触发已由 `场景写法.csv` 承接,不直接 Read | #14 |
+| `skills/webnovel-write/references/writing/dialogue-writing.md` | 231 | 对话触发已由 `写作技法.csv` 承接 | #14 |
+| `skills/webnovel-write/references/writing/emotion-psychology.md` | 265 | 情感触发已由 `写作技法.csv` 承接 | #14 |
+| `skills/webnovel-write/references/writing/scene-description.md` | 263 | Plan §6.2.3 点名;不在直接 Read 清单,核 `场景写法.csv` 覆盖后处置 | #14 |
+| `skills/webnovel-write/references/writing/desire-description.md` | 311 | 同上;核 CSV 覆盖后处置 | #14 |
+| `skills/webnovel-write/references/writing/genre-hook-payoff-library.md` | 85 | Plan §6.2.3 点名;不在直接 Read 清单,核 CSV 覆盖后处置 | #14 |
+| `skills/webnovel-write/references/style-variants.md` | 38 | 非直接调用项 | #14 |
+| `skills/webnovel-review/references/common-mistakes.md` | 96 | 非直接调用项 | #14 |
+| `skills/webnovel-review/references/pacing-control.md` | 129 | 非直接调用项 | #14 |
+
+> `scene-description.md`(263)、`desire-description.md`(311)、`genre-hook-payoff-library.md`(85) 三者**存在且当前非直接 Read**,按 Plan 要求纳入清理核验。
+> 其余存在但本轮无显式处置的 reference(不直接 Read、Plan 未点名):`skills/webnovel-write/references/anti-ai-guide.md`(74,内容已并入 polish-guide §98/§2A,建议 #14 一并核验是否死)、`skills/webnovel-init/references/init-collection-schema.md`(74,Plan §6.2.4 指定 init 区段读真源,**保留**)、`skills/webnovel-init/references/creativity/market-positioning.md`(424,未登记直接 Read,建议 #14 核验)、`references/shared/naming-and-voice-gaps.md`(63)、`skills/webnovel-plan/references/outlining/outline-structure.md`、`plot-frameworks.md`(未登记直接 Read,建议 #13/#14 核验消费方)。
+
+### A.5 锚点校验与 FLAG
+
+所有 7 靶心锚点经 Grep `^#{1,4} ` 核实**真实存在**,无缺失锚点。需注意的「Plan 简写 vs 真实标题」差异(不是缺失,是匹配口径,区段读时必须用右列):
+
+| 文件 | Plan 正文写法 | 真实标题(区段读须匹配此) |
+|---|---|---|
+| genre-profiles | 「一、字段说明」 | `## 一、Profile 字段说明` |
+| reading-power-taxonomy | 「## 一 钩子类型 / ## 二 爽点模式 / ## 三 微兑现」 | `## 一、钩子类型` / `## 二、爽点模式` / `## 三、即时满足/微兑现` |
+| selling-points | 「### 1.3」「## 7」「## 9」 | `### 1.3 核心卖点黄金公式` / `## 7. 实战检查清单` / `## 9. 核心卖点定位模板` |
+| chapter-planning | 「## 10 …」「## 7」 | `## 10. 结构化节点规范(CBN/CPNs/CEN)` / `## 7. 章节规划模板` |
+| creativity-constraints | 「### 8.1」「一 Schema / 六 硬约束 / 八 评分」 | `### 8.1 五维评分` / `## 一、创意包 Schema (Idea Package)` / `## 六、硬约束驱动创意 (Hard Constraints)` / `## 八、评分系统 (Scoring System)` |
+| polish-guide | 「## 2 执行顺序」「Anti-AI 词库段」 | `## 2. 执行顺序(必须按序)` / `## 98:Phase 1 增补:Anti-AI 规范`(另 `## 2A. Anti-AI 检测细则`) |
+| cool-points-guide | 「## 九 题材适配」 | `## 九、题材适配` |
+
+**FLAG:无缺失锚点。** 唯一注意点:Plan 锚点去掉了中文序号点「、」并缩短标题,按字面 `Grep` 会漏匹配——Phase 4 区段读与 loading-map 登记一律以本表右列真实标题为准。
+
+---
+
+## Section B — 逐组件归属(8 Skill + 4 Agent)
+
+依 §4.2 职责表。「层」标主 agent 契约形状(Skill)/ subagent(Agent)/ runtime 边界。每格一句话,细节交叉引用 Plan,不复述。
+
+| 组件 | 层 | 保留要点(一句话) | 主要可压缩 / 下沉项(一句话) | 处理 Task |
+|---|---|---|---|---|
+| `skills/webnovel-write/SKILL.md`(202) | Skill:主 agent 契约形状 | 三模式 + 准备链 + 三 Agent 调用合同 + 三道 gate/commit/postcommit/backup(§3.4、§8.2),新增提交前只读 `git diff`(§5.2 B) | context/reviewer/data 内部教程、data payload schema、长润色教程下沉 Agent/reference(§8.3) | #5 |
+| `agents/context-agent.md`(181) | subagent | 五段写作任务书 + `memory-contract load-context` + 按需 query + `.story-system` 优先 + blocker(§9.1) | 长示例、过细推断说明、术语解释删(§9.1) | #6 |
+| `agents/data-agent.md`(120) | subagent(含完整 artifact schema 真源) | 三份 artifact 顶层/子项最小字段 + 禁写 state/projection(§9.2、§4.3);`tools` 含 `Write` | 各 event_type 长 payload 说明、长 JSON 示例、旧字段名解释压缩(§9.2) | #7 |
+| `agents/reviewer.md`(135) | subagent | 五维 `dimension_results`(pass 也写) + evidence/fix_hint + 严格 JSON、不评分(§9.3);不授 `Write`,只返 JSON | 删「思维链/ReAct」元叙述、删过长审查教程(§9.3、§12.2 松绑段号测试) | #8 |
+| `agents/deconstruction-agent.md`(296) | subagent | quick/deep/auto 路由 + 不写文件 + 不造 canon + `init_reference_research`/quality/防污染(§9.4、§3.2) | 长质量门控表、超长 schema、深度分阶段长说明压缩(§9.4) | #9 |
+| `skills/webnovel-init/SKILL.md`(402) | Skill:主 agent 契约形状 | §3.2 全链:Step1.5 灵感询问、deconstruction 调用边界、确认前不写 canon、root 安全化、idea_bank、patch 总纲、init 后 MASTER、验证回滚(§10.1) | 采集字段→`init-collection-schema.md` 区段读、题材列表收敛、CLI 长表收缩、创意/反套路/世界观按需读(§10.1、§6.2.4) | #10 |
+| `skills/webnovel-plan/SKILL.md`(394) | Skill:主 agent 契约形状 | §3.3 全链:placeholder-scan、跨卷状态、设定基线、节拍表/时间线/卷纲/批量章纲、设定写回、总纲写回 JSON、master-outline-sync、update-state、真实 CHAPTER_GOAL 刷合同(§10.2) | CBN/CPN/CEN 细则→`chapter-planning.md`§10 区段读、长 reference 表改按阶段触发、节点示例下沉(§10.2、§6.2.4) | #11 |
+| `skills/webnovel-review/SKILL.md`(170) | Skill:主 agent 契约形状 | §3.5 全链:合同缺失补 story-system、reviewer 调用、`review-pipeline --save-metrics`、`update-state --add-review`、blocking 用户裁决(§10.3) | reviewer 审查方法 / 证据查询过程不在 Skill 展开(§10.3) | #12 |
+| `skills/webnovel-query/SKILL.md`(109) | Skill:主 agent 契约形状(只读) | 只读 + root 保护 + 数据源优先级(`.story-system`→accepted commit→memory-contract→projection) + 降级说明(§11.1、§3.6) | 默认全量 `load-context` 改按查询类型用最窄工具(entity-state/relationships/query-rules/open-loop)(§11.1) | #15 |
+| `skills/webnovel-learn/SKILL.md`(82) | Skill:主 agent 契约形状 | root 保护 + 读当前章节号 + `project-memory add-pattern` + 不手写 JSON(§11.2) | 已极简,基本无可压缩;维持(§11.2) | #16 |
+| `skills/webnovel-dashboard/SKILL.md`(101) | Skill:主 agent 契约形状(只读) | 只读边界 + `story-runtime/health` + root 解析 + dist 校验(§11.3) | 不默认装依赖(缺依赖提示命令)、启动前轻量检查(§11.3) | #16 |
+| `skills/webnovel-doctor/SKILL.md`(70) | Skill:主 agent 契约形状(只读) | `project-status` 先行 + `doctor` 阶段感知 + 不修复/不装/不启 dashboard(§11.4、§3.6) | frontmatter description 改简洁中文触发型(§11.4) | #16 |
+
+> Runtime 层(`webnovel.py` 各命令、artifact_validator、write-gate、chapter-commit、projection)承载 schema/gate/commit/projection/backup/状态推进,不在本轮 prompt 瘦身改动范围;各 Skill/Agent 只保留「调用形状」,校验留给 runtime(§4.2、§4.5 写入所有权矩阵)。
+> Agent 单文件约束(§4.3):4 个 Agent 均为 `agents/*.md` 单文件,不得新增 `agents/references/*`;其产物 schema 作为单文件唯一真源(data-agent 尤为关键)。
+
+---
+
+## 体量小结(实测)
+
+- 8 Skill 合计 **1530 行**(write 202 / init 402 / plan 394 / review 170 / query 109 / learn 82 / dashboard 101 / doctor 70);init、plan 最大,是 §10 主战场。
+- 4 Agent 合计 **732 行**(context 181 / data 120 / reviewer 135 / deconstruction 296);deconstruction 最大。
+- 7 靶心 reference 合计 **3057 行**(区段读后单本书常驻锐减,genre-profiles 单题材约省 90%)。
+- 不读清理候选合计 **约 1402 行**(A.4 九个核心候选,待核 CSV 覆盖后处置)。
+- 读取方式分布:靶心区段读 7;A.2 区段读 11;全文读 15(含模板/schema/铁律/短文件);检索读 CSV 3 类(不改造);不读 9(+ 建议核验若干)。

+ 228 - 0
docs/architecture/phase0-tool-and-registration-audit-2026-06-06.md

@@ -0,0 +1,228 @@
+# Phase 0 审计:工具能力与插件注册名(2026-06-06)
+
+> 只读审计。本文件只「记录证据」,不修改任何 skill / agent / plugin.json / 测试文件。
+> 目的:为 `context-minimal-writing-flow-plan` 提供可信的运行基线,复核计划中假设的
+> `webnovel-writer:<agent>` 注册名、agent/skill frontmatter、以及计划依赖的官方工具行为结论。
+>
+> 工作目录(git 仓库根 = worktree):`D:/wk/novel skill/webnovel-writer/.worktrees/context-minimal-flow`
+> 插件目录(嵌套):仓库根下的 `webnovel-writer/` 子目录。
+
+---
+
+## 1. Claude Code 版本与环境
+
+| 项 | 值 | 证据 |
+|----|----|------|
+| 宿主 | Claude Code(固定,本插件唯一目标宿主) | — |
+| `claude --version` | `2.1.161 (Claude Code)` | Bash 直接执行成功(非「subagent 不可用」) |
+| 平台 | Windows(win32),PowerShell + Bash 双可用 | 环境说明 |
+| 本审计执行体 | subagent(Task 1);本机 `claude --version` 可正常返回 | — |
+
+结论:版本/环境均可确认,无降级记录。
+
+---
+
+## 2. 插件注册(真实名)
+
+### 2.1 plugin.json
+
+文件:`webnovel-writer/.claude-plugin/plugin.json`(全文 18 行)。
+
+```json
+{
+  "name": "webnovel-writer",
+  "version": "6.1.0",
+  "description": "长篇网文创作系统(skills + agents + data chain + RAG)",
+  "author": { "name": "lingfengQAQ" },
+  "homepage": "https://github.com/lingfengQAQ/webnovel-writer",
+  "repository": "https://github.com/lingfengQAQ/webnovel-writer",
+  "license": "GPL-3.0",
+  "keywords": ["webnovel", "claude-code", "skills", "agents", "rag"]
+}
+```
+
+- 插件名(namespace 前缀来源):**`webnovel-writer`**。
+- manifest **没有**显式 `agents` / `skills` / `commands` 路径数组。
+  → 因此 agents 与 skills **全部走约定式 auto-discovery**:
+  - 官方 `plugin-structure/SKILL.md:11` —「automatic component discovery」;
+  - 同文件第 28-32 行:`agents/`(`.md` 文件)与 `skills/`(子目录,每个含 `SKILL.md`)。
+  - 官方 `skill-development/SKILL.md:271-273` —「Claude Code automatically discovers skills: Scans `skills/` directory; Finds subdirectories containing `SKILL.md`」。
+
+### 2.2 真实 on-disk agent 名(4 个)
+
+每个 agent 是单一 `.md` 文件,frontmatter `name:` 与文件名(去 `.md`)一致:
+
+| 文件 | frontmatter `name:` (行 2) | 一致? |
+|------|---------------------------|--------|
+| `webnovel-writer/agents/context-agent.md` | `context-agent` | ✅ |
+| `webnovel-writer/agents/data-agent.md` | `data-agent` | ✅ |
+| `webnovel-writer/agents/deconstruction-agent.md` | `deconstruction-agent` | ✅ |
+| `webnovel-writer/agents/reviewer.md` | `reviewer` | ✅ |
+
+另有 `webnovel-writer/agents/evals/`(含 `evals.json` + `files/`)——非 agent 定义,是评测夹具,auto-discovery 不会把它当 agent(不是顶层 `.md`)。无 `agents/references/` 目录。
+
+### 2.3 真实 on-disk skill 名(8 个)
+
+每个 skill 是含 `SKILL.md` 的子目录,frontmatter `name:`(均在行 2)与目录名一致:
+
+| 目录 | frontmatter `name:` | 一致? |
+|------|--------------------|--------|
+| `skills/webnovel-write/` | `webnovel-write` | ✅ |
+| `skills/webnovel-init/` | `webnovel-init` | ✅ |
+| `skills/webnovel-plan/` | `webnovel-plan` | ✅ |
+| `skills/webnovel-review/` | `webnovel-review` | ✅ |
+| `skills/webnovel-query/` | `webnovel-query` | ✅ |
+| `skills/webnovel-learn/` | `webnovel-learn` | ✅ |
+| `skills/webnovel-dashboard/` | `webnovel-dashboard` | ✅ |
+| `skills/webnovel-doctor/` | `webnovel-doctor` | ✅ |
+
+(路径前缀省略 `webnovel-writer/`。)
+
+### 2.4 注册名 ↔ 计划引用名 复核(关键)
+
+计划 `docs/architecture/context-minimal-writing-flow-plan-2026-06-05.md` 在多处用
+`webnovel-writer:<agent>` 形式引用 4 个 agent,例如:
+
+- L181 `必须调用 webnovel-writer:context-agent`
+- L195 `... webnovel-writer:reviewer`
+- L228 `... webnovel-writer:data-agent`
+- L97 `... webnovel-writer:deconstruction-agent`
+- L463/L698/L713/L728 `Use the Agent tool to run webnovel-writer:context-agent` 等
+- L430 计划自带告警:「plugin scoped agent 的真实注册名必须在 Phase 0 复核」(即本节)。
+
+复核结论:
+
+- **on-disk agent 名 = `context-agent` / `data-agent` / `deconstruction-agent` / `reviewer`**,
+  与计划里冒号后半段完全一致,**无错名**。
+- 关于前缀:官方 `agent-development/SKILL.md:281-285`「Namespacing」原文:
+  - 「Agents are namespaced automatically: Single plugin: `agent-name`; With subdirectories: `plugin:subdir:agent-name`」。
+  - 即官方文档把「单插件、无子目录」的 agent 标识写作**裸名 `agent-name`**,把带前缀的形态留给「子目录」场景(`plugin:subdir:agent`)。
+  - 命令侧旁证:`command-development` 与 `plugin-features-reference.md:30` 显示插件组件在 `/help` 里带 `(plugin:plugin-name)` 标签,说明「插件名:组件名」是 marketplace 场景下的实际可见限定形态。
+  - ⚠️ **轻微风险(命名形态,非错名)**:官方 agent-development 文档对「单插件无子目录」给出的范式是裸 `agent-name`,并未把 `plugin:agent`(无 subdir)列为 canonical 形态。计划统一用 `webnovel-writer:context-agent` 这类**带插件前缀、无 subdir** 的写法,是合理且更显式的(在多插件并存时消歧),但官方文档**没有把这一形态写成单插件的标准范式**。
+  - 建议:Phase 1 真正接线时,以**运行时实测**(`Agent`/Task 工具能否用 `webnovel-writer:context-agent` 解析到该 agent)为准;若实测裸名 `context-agent` 才解析得到、带前缀失败,则按裸名修正计划。本审计无法在 subagent 内实跑 Agent 工具来终判,故标注为「待运行时验证」。
+
+---
+
+## 3. Agent frontmatter 审计(4 个)
+
+逐项抄录 frontmatter(行号以各文件为准;所有 4 个 agent 的 frontmatter 块均为第 1-7 行):
+
+| agent | name | description(摘) | model | color | tools |
+|-------|------|------------------|-------|-------|-------|
+| context-agent | `context-agent` | 写前 research,输出写作任务书。 | `inherit` | `blue` | `Read, Grep, Bash` |
+| data-agent | `data-agent` | 从正文提取事实,生成 commit artifacts。 | `inherit` | `green` | `Read, Write, Bash` |
+| deconstruction-agent | `deconstruction-agent` | /webnovel-init 的参考书拆解子代理…… | `inherit` | `purple` | `Read, Grep, Bash` |
+| reviewer | `reviewer` | 统一审查 agent。逐维度检查…… | `inherit` | `yellow` | `Read, Grep, Bash` |
+
+对照 agent-development 规则:
+
+- **单一 `.md` 文件**:✅ 全部满足。
+- **必备字段 name/description/model/color**:✅ 4 个全齐。
+  - `model: inherit` 符合官方推荐(`agent-development/SKILL.md:100,105`)。
+  - `color` 取值:`blue/green/purple/yellow`。⚠️ 官方 `agent-development/SKILL.md:111` 列出的合法色为
+    `blue, cyan, green, yellow, magenta, red`,**未列 `purple`**。`deconstruction-agent` 的
+    `color: purple` 不在官方枚举内(应为 `magenta`)。属**轻微规范偏差**,不影响功能/注册(color 仅 UI 着色)。
+- **`tools` 最小集**:✅ 都是 3 个工具的小集合(least privilege,符合 `SKILL.md:134`)。
+- **FLAG:`tools` 含 `Agent` 或 `AskUserQuestion`?** → ❌ **均不含**。
+  4 个 agent 的 tools 仅为 `{Read, Grep/Write, Bash}` 组合,**没有任何一个**带 `Agent`/`AskUserQuestion`/`Task`。
+  → 与计划「subagent 不得用 `Agent`/`AskUserQuestion`」一致,**无违例**。
+- **FLAG:缺必备 frontmatter 字段?** → 无。4 个 agent 字段齐全。
+- **FLAG:依赖外部 `agents/references/*` 当隐藏手册?** → 无。`agents/references/` 目录不存在;
+  4 个 agent 正文未引用 `agents/references/...`。(注:正文大量引用 `${SCRIPTS_DIR}/webnovel.py` 子命令,
+  那是运行时脚本调用,不是「隐藏 manual 文件」。)
+- **`skills:` frontmatter 字段?** → 4 个 agent **均无** `skills:` 字段(Grep `^(name|skills):` 仅命中
+  `name:`)。即当前没有任何 agent 预载 Skill 内容。
+
+---
+
+## 4. Skill frontmatter 审计(8 个)
+
+逐项抄录(`name`/`allowed-tools` 行号见下;所有 skill `name:` 均在行 2):
+
+| skill | name | description 触发型 | allowed-tools |
+|-------|------|-------------------|---------------|
+| webnovel-write | `webnovel-write` | 产出可发布章节,完整执行上下文→起草→审查→润色→提交→备份。 | `Read Write Edit Grep Bash Agent AskUserQuestion` |
+| webnovel-init | `webnovel-init` | 深度初始化网文项目。分阶段交互收集…… | `Read Write Edit Grep Bash Agent AskUserQuestion WebSearch WebFetch` |
+| webnovel-plan | `webnovel-plan` | 基于总纲生成卷纲、时间线和章纲…… | `Read Write Edit Bash AskUserQuestion` |
+| webnovel-review | `webnovel-review` | 使用审查 Agent 评估章节质量…… | `Read Grep Write Edit Bash Agent AskUserQuestion` |
+| webnovel-query | `webnovel-query` | 查询项目设定、角色、力量体系、势力、伏笔…… | `Read Grep Bash` |
+| webnovel-learn | `webnovel-learn` | 从当前会话提取成功模式并写入 project_memory.json | `Read Bash` |
+| webnovel-dashboard | `webnovel-dashboard` | 启动只读小说管理面板…… | `Bash Read` |
+| webnovel-doctor | `webnovel-doctor` | This skill should be used when the user asks to "/webnovel-doctor", "检查项目环境"…… | `Read Bash` |
+
+对照 skill-development 规则:
+
+- **目录 + SKILL.md 结构**:✅ 8 个目录均含 `SKILL.md`(逐一 `test -f` 通过)。
+- **frontmatter 含 `name` + 触发型 `description`**:✅ 全部有 `name` + `description`。
+  - 官方只强制 `name` + `description`(`skill-development/SKILL.md:33-34`)。
+  - 触发表述:7 个用中文动作/场景式触发短语(计划偏好),`webnovel-doctor` 用英文官方范式
+    「This skill should be used when…」并把中文触发词内嵌其中——两类都满足「具体触发」要求。
+  - `webnovel-doctor` 还带 `version: 0.1.0`(额外字段,无害)。
+- **`allowed-tools` 是否存在及其值**:✅ **8 个 skill 全部声明了 `allowed-tools`**(值见上表)。
+  - 4 个编排型 skill(write/init/plan/review)的 `allowed-tools` 含 **`Agent`**(write/init/review)
+    与 **`AskUserQuestion`**(write/init/plan/review)——这是**顶层 skill / 命令层**用来「预批准」调度子代理
+    与向用户提问的工具集,**不是 subagent 的 tools**。与计划角色分工(编排在 skill 层、执行在 agent 层)一致。
+  - 只读型 skill(query/learn/dashboard/doctor)的 `allowed-tools` 收敛为 `Read/Bash(/Grep)`,无 `Agent`/`AskUserQuestion`。
+
+> 说明:官方 `skill-development/SKILL.md` 通篇**未出现** `allowed-tools` 字段名(仅强制 `name`+`description`)。
+> 因此「`allowed-tools` 是预批准而非限制」这一语义**无法从本地官方 skill 文档直接证实**,见 §5。
+
+---
+
+## 5. 计划依赖的官方工具行为结论(运行基线)
+
+交叉核对源(实际使用路径):
+**`C:/Users/lcy/.claude/plugins/marketplaces/claude-plugins-official/plugins/plugin-dev/skills/`**
+(primary 路径存在;fallback `.tmp/plugin-dev-official/...` **不存在**,未使用)。
+核对的三个 skill:`agent-development/`、`skill-development/`、`plugin-structure/`(含其 `references/`、`examples/`)。
+
+逐条结论与本地可证实性:
+
+| # | 计划依赖的结论 | 本地官方文档可证实? | 证据 / 标注 |
+|---|----------------|---------------------|-------------|
+| (a) | **subagent 不能再生成另一个 subagent** | ❌ 未找到 | 在 plugin-dev 全树 Grep `spawn / another (sub)?agent / nested / recursi` 仅命中 MCP 进程「spawn」,**无 subagent 嵌套禁令**条文。→ **「计划断言,本地未证实」**。(与之相容的旁证:`command-development/SKILL.md:720`「Claude uses **Task tool** to launch agent」——launch agent 是上层/命令侧动作;官方未给出 subagent 自身可再调 Task 的任何示例,但「未给出」≠「明文禁止」。) |
+| (b) | **`Agent` / `AskUserQuestion` 不作为 subagent 的 tools** | ⚠️ 部分(间接) | 官方 agent `tools` 文档(`agent-development/SKILL.md:122-140`)给的「常用 tool set」全是 `Read/Write/Grep/Glob/Bash`,**从不**把 `Agent`/`AskUserQuestion` 列进 agent 的 `tools`。`AskUserQuestion` 出现处均在**命令/skill 层**的 `allowed-tools`(`plugin-dev/commands/create-plugin.md:12`、`plugin-settings/examples/create-settings-command.md:3`),不在任何 agent frontmatter。→ 官方**惯例支持**该结论,但**无一句明文「禁止」**。标注为「官方惯例支持,无明文禁令」。本仓 4 个 agent 实测也无人含 `Agent`/`AskUserQuestion`(§3)。 |
+| (c) | **`tools` / `disallowedTools` 控制 subagent 工具边界** | ⚠️ 一半 | `tools` 控制边界:✅ 明文——`agent-development/SKILL.md:124`「Restrict agent to specific tools」、`:132`「If omitted, agent has access to all tools」、`:134` least-privilege。`disallowedTools`:❌ 在 plugin-dev 全树 **Grep 无任何命中**。→ `tools` 部分「本地已证实」;`disallowedTools` 部分「计划断言,本地未证实」。 |
+| (d) | **agent `skills:` frontmatter 会把整份 Skill 内容预载进 subagent** | ❌ 未找到 | 官方 agent frontmatter 文档只列 `name/description/model/color/tools`(`SKILL.md:122` 区段 + `:340-342` 表),**无 `skills:` 字段**说明;全树 Grep `skills:`(agent 语境)无命中。→ **「计划断言,本地未证实」**。当前本仓也无 agent 使用该字段(§3),故即便成立也属「未来才会用到」的能力。 |
+| (e) | **Skill 的 `allowed-tools` 是预批准、不是限制** | ❌ 未找到 | `skill-development/SKILL.md` 全篇**无** `allowed-tools` 字样;官方只把 `allowed-tools` 用在**命令** frontmatter(`command-development` / `plugin-settings` 示例)。其「预批准/非限制」语义在本地官方文档中**无直接定义**。→ **「计划断言,本地未证实」**。(注:经验上 skill/command 的 `allowed-tools` 行为确为预批准,但本审计按要求不凭记忆造引用,仅据本地文档判定为「未证实」。) |
+
+补充已证实的注册/调度事实(支撑计划接线):
+
+- 自动发现:agents 扫 `agents/*.md`、skills 扫 `skills/*/SKILL.md`(`plugin-structure/SKILL.md:11,28-32`;`skill-development/SKILL.md:271-273`)。✅
+- agent 命名空间:单插件裸名 `agent-name`,带子目录 `plugin:subdir:agent-name`(`agent-development/SKILL.md:283-285`)。✅
+- 上层用 **Task 工具** 启动 agent(`command-development/SKILL.md:720`)。✅
+
+---
+
+## 6. 结论 / 风险清单
+
+### 与计划假设一致的部分(无阻断)
+
+1. 4 个 agent 的 on-disk 名(`context-agent` / `data-agent` / `deconstruction-agent` / `reviewer`)与计划
+   `webnovel-writer:<agent>` 引用的冒号后半段**完全一致**,无错名、无缺失。
+2. 8 个 skill 目录名与各自 `name:` 一致,结构合规(目录 + SKILL.md,name + 触发型 description)。
+3. **没有任何 agent 的 `tools` 含 `Agent` / `AskUserQuestion` / `Task`** → 计划「subagent 不持调度工具」的红线
+   在现状下**已天然满足**,无需先修。
+4. 没有 agent 缺必备 frontmatter 字段;没有 agent 依赖 `agents/references/*` 隐藏手册(该目录不存在)。
+5. 没有 agent 使用 `skills:` 预载字段(即 (d) 即便成立,也不会与现状冲突)。
+6. 8 个 skill 均显式声明 `allowed-tools`,且编排型/只读型分工与计划角色划分吻合。
+
+### 会动摇计划假设的风险点(需后续处理 / 实测)
+
+- **R1(命名形态,待运行时验证)**:官方 agent-development 文档把「单插件无子目录」agent 的 canonical 标识写成
+  **裸 `agent-name`**,未把 `plugin:agent`(无 subdir)列为标准范式。计划统一用 `webnovel-writer:context-agent`
+  这类带前缀写法。**名字本身没错**,但前缀形态能否被 Agent/Task 工具解析到,需 Phase 1 **运行时实测**确认;
+  若实测仅裸名可解析,应回改计划为裸名。本审计(subagent 内)无法实跑 Agent 工具终判。
+- **R2(工具行为,本地未证实——见 §5)**:计划依赖的 (a) subagent 不能再开 subagent、(d) agent `skills:` 预载整份
+  Skill、(e) skill `allowed-tools` 为预批准——这三条在本地官方 plugin-dev 文档中**找不到出处**;(c) 的
+  `disallowedTools`、(b) 的「明文禁止」也**无出处**。这些应作为**「计划断言、本地未证实」**对待:
+  计划据此设计无妨,但任何「以 `skills:` 预载替代 reference 大块文本」「靠 subagent 链式分发」的关键改动,
+  应在实现期以小样例**实测**,不能仅以官方文档为据。
+- **R3(轻微规范偏差,不阻断)**:`deconstruction-agent` 的 `color: purple` 不在官方合法色枚举
+  (`blue/cyan/green/yellow/magenta/red`)内。仅 UI 着色,无功能影响;如要严格合规,应改 `magenta`。
+  本审计为只读,不修改。
+
+### 一句话总览
+
+注册名与 frontmatter 现状**与计划高度一致、无错名、无 `Agent`/`AskUserQuestion` 违例**;唯一需在实现期落实的是
+**R1 的 `webnovel-writer:<agent>` 前缀形态运行时实测** 与 **R2 中那几条「本地未证实」的工具行为以实测兜底**。

+ 33 - 124
webnovel-writer/agents/context-agent.md

@@ -10,172 +10,81 @@ color: blue
 
 ## 1. 身份
 
-你是写前组装员。先 research,再输出一份写作任务书给 Step 2
+你是上下文压缩器。先 research,再输出一份五段写作任务书给起草阶段。只返回任务书,不落盘,不暴露系统术语
 
-原则:按需召回,不灌全量;章纲 > 合同 > CSV 参考;只输出任务书,不暴露系统术语。
-
-数据权重(高→低):用户要求 > 章纲原文 > MASTER_SETTING > reasoning 裁决 > CHAPTER_COMMIT > CSV 检索
+数据权重(高→低):用户要求 > 章纲原文 / `chapter_directive.goal` > MASTER_SETTING > reasoning 裁决 > CHAPTER_COMMIT > CSV 检索。
 
 ## 2. 工具
 
-`Read`/`Grep`/`Bash`。
+`Read` / `Grep` / `Bash`。
 
-### 核心命令
+主入口(一次性拿全基础包):
 
 ```bash
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" where
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" memory-contract load-context --chapter {NNNN}
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" memory-contract query-entity --id "{entity_id}"
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" memory-contract query-rules --domain "{domain}"
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" memory-contract get-timeline --from {N} --to {M}
 ```
 
-### 按需命令
+按需补查(基础包不足时才调,已含的不重复查):
 
 ```bash
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" memory-contract query-entity --id "{entity_id}"
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" memory-contract query-rules --domain "{domain}"
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" memory-contract get-timeline --from {N} --to {M}
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" index get-reader-signals --limit 5 --last-n 20
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" index get-core-entities
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" knowledge query-entity-state --entity "{entity_id}" --at-chapter {N}
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" knowledge query-relationships --entity "{entity_id}" --at-chapter {N}
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" extract-context --chapter {NNNN} --format json
 ```
 
-### load-context 已包含的数据(不要重复查)
-
-`story_contracts`(MASTER/volume/chapter/review 合同)、`recent_summaries`(近 2 章摘要)、`urgent_loops`(前 3 条紧急伏笔)、`active_rules`(前 5 条世界规则)、`protagonist`(主角状态)、`memory_pack`(追读力数据)、`genre_profile_excerpt`(当前题材画像)。
-
-只有 load-context 返回空 contracts 时才直接 Read `.story-system/*.json`。
-
-### 裁决层(在 chapter 合同的 `reasoning` 对象中)
-
-- `style_priority`:风格优先级(如"冷硬算计 > 超然物外")
-- `pacing_strategy`:节奏策略
-- `genre`:命中题材
-
-必须在任务书第 4 段消费。`chapter_focus` 仅为 CSV 派生参考,本章目标以章纲为准。
-
-### 写作铁律
+load-context 已含(不要重复查):`story_contracts`(MASTER/volume/chapter/review)、`recent_summaries`、`urgent_loops`、`active_rules`、`protagonist`、`memory_pack`(追读力)、`genre_profile_excerpt`。只有返回空 contracts 时才直接 Read `.story-system/*.json`。
 
-**三大定律**:大纲即法律、设定即物理(能力≤已有记录)、新实体由 data-agent 提取。
-
-**硬约束**:每章必须有推进(目标/代价/关系变化至少一项);上章有钩子本章必须回应;禁止占位正文。
-
-**文风/反 AI**:本段不灌输 anti-AI 细则——文风与去 AI 味统一由写章 Step 4 润色阶段处理。任务书只给题材基调、节奏与本章情绪走向。
+裁决层(chapter 合同的 `reasoning` 对象):`style_priority`、`pacing_strategy`、`genre`,必须在第 4 段消费。`chapter_focus` / `dynamic_context` 等 CSV 派生项仅作写法参考,不得覆盖章纲与 `chapter_directive.goal` 约束。
 
 ## 3. 执行流程
 
-### A:基础包(1 Bash + 1 Read)
-
-1. `load-context --chapter {NNNN}` 获取基础包
-2. `Read` 章纲原文(load-context 的 outline 可能截断)
-3. 确定卷号(优先 runtime contracts / latest commit;必要时兼容读取 state.json 投影)
-4. 若用户明确提供额外的项目级文风/反 AI 味规则文件,读取并只消费规则,不在任务书暴露文件名。
-
-### B:按需深查(只查基础包不足的)
-
-- 配角细节 → `query-entity`
-- 特定规则 → `query-rules --domain`
-- 时间跨度 → `get-timeline` 或 Read 时间线文件
-
-时间规则:跨夜须过渡,倒计时不跳跃,不回跳。
+1. `load-context --chapter {NNNN}` 取基础包;`Read` 章纲原文(load-context 的 outline 可能截断)。
+2. 确定卷号:优先 runtime contracts / latest commit;必要时兼容读取 `state.json` 投影。
+3. 按需深查:配角 → `query-entity`;规则 → `query-rules`;时间跨度 → `get-timeline` 或读时间线文件。时间规则:跨夜须过渡、倒计时不跳跃、不回跳。
+4. 伏笔:`urgent_loops` 已在基础包;`remaining ≤ 5` 或超期的必须处理,可选伏笔最多 5 条。
+5. 组装:动机 = 目标+处境+钩子压力;情绪底色 = 上章结尾+走向;可用能力 = 境界+设定禁用。合并 `reasoning` + `anti_patterns` + 用户明确提供的项目级文风规则(只消费、不暴露文件名)。
+6. 红线校验(第 6 段),任一 fail 回第 5 步重组。
 
-### C:补充(可选)
+## 4. 写作铁律
 
-追读力已在 memory_pack 中。仅需精确统计时调 `index get-reader-signals`。
+- **三大定律**:大纲即法律、设定即物理(能力 ≤ 已有记录)、新实体由 data-agent 提取。
+- **硬约束**:每章必须有推进(目标/代价/关系变化至少一项);上章有钩子本章必须回应;禁止占位正文。
+- **文风 / Anti-AI**:本段不灌细则——去 AI 味由起草后的润色阶段处理。任务书只给题材基调、节奏与本章情绪走向。
 
-伏笔:`urgent_loops` 已在基础包中。`remaining ≤ 5` 或超期的必须处理,可选伏笔最多 5 条。
-
-### D:组装
-
-1. 推断:动机 = 目标+处境+钩子压力;情绪底色 = 上章结尾+走向;可用能力 = 境界+设定禁用
-2. 从 `story_contracts` 取 `reasoning`(style_priority/pacing_strategy)+ `anti_patterns`,并合并用户明确提供的项目级文风规则
-3. 组装五段任务书
-4. 红线校验
-
-## 4. 输入
+## 5. 输入
 
 ```json
 {"chapter": 100, "project_root": "D:/wk/斗破苍穹", "storage_path": ".webnovel/", "state_file": ".webnovel/state.json"}
 ```
 
-## 5. 边界
+`state.json` 仅作兼容 / read-model 读取;写前合同以 `.story-system/`(`story_contracts`)为准。
 
-- 不改大纲,不造数据,不改节点
-- 不整库搬运记忆
-- 追读力不覆盖大纲主任务
-- 不把合同/规则来源原样输出
+## 6. 边界与校验
 
-## 6. 校验清单
+边界:不改大纲、不造数据、不改节点;不整库搬运记忆;追读力不覆盖大纲主任务;不把合同 / 规则来源原样输出。
 
-任一 fail 回 D 重组:事实无冲突、时空有承接、能力有来源、动机不断裂、合同与任务书一致、时间正确、记忆未遗漏、节点不冲突、任务书可独立支撑起草、五段完整语气自然、角色动机非空、有差异化建议、伏笔已按紧急度输出。
+校验清单(任一 fail 回第 3 段重组):事实无冲突、时空有承接、能力有来源、动机不断裂、合同与任务书一致、时间正确、记忆未遗漏、节点不冲突、五段完整可独立支撑起草、角色动机非空、伏笔已按紧急度输出。
 
 ## 7. 输出格式
 
-只输出一份五段任务书。
-
-### 1. 开篇委托
-书名、章号、标题、一句话目标。
-
-### 2. 这章的故事
-综合:前文摘要、本章目标/阻力、情节节点(CBN/CPNs/CEN)、必须覆盖/禁区、跨章约束、RAG 线索。
-
-### 3. 这章的人物
-每人一段:状态、驱动力、本章作用、说话倾向。
-
-### 4. 怎么写更顺
-最关键的一段。翻译裁决层的风格/节奏为具体指导;题材基调;writing_guidance;anti_patterns 翻为自然提醒;审查得分趋势。
-
-### 5. 收在哪里
-结尾停在什么感觉,留什么未完感。
-
-**不要输出**:合同条目、检查清单、文件路径、"Anti-AI""blocking_rules"等词。
-
-### 示例
-
-你现在要写《凡人修仙传》第47章《坊市试探》。
-
-这一章主要写韩立进入坊市,试探那条关于"天灵根弟子失踪"的消息到底是真是假。
-
-上章结尾韩立刚从禁地脱出,身上还带着墨蛟的气息没散干净,回到住处才发现陈巧倩留了一封短信,说坊市那边有人在高价收购蕴灵丹的原料,而且收购者指名要"外门新晋弟子"来接头。这个条件太针对他了,他不确定是机会还是陷阱。
-
-所以这章的核心不是去坊市买东西,而是一次有预谋的试探。韩立要弄清三件事:谁在收购、为什么指名新晋弟子、这件事跟天灵根弟子失踪有没有关系。但他不能暴露自己真实的修为(他一直在藏,对外只展示练气九层的水平),也不能让人发现他身上的墨蛟残息。
-
-中间大致这么走:韩立先到坊市外围转了一圈摸情况,接着通过陈巧倩搭上收购者的线,然后在接头时发现对方的修为和身份都不简单。
-
-其中"试探消息真伪"和"发现对方身份不简单"是这章绕不开的,别漏掉。不能让韩立在这章就摊牌或起冲突,这章是铺垫。
-
-跨章硬线索:第38章埋的伏笔——韩立在藏经阁翻到过"灵根置换术"残页。如果失踪事件跟灵根有关,他会闪过这个念头,点到为止。
-
----
-
-韩立——筑基初期(对外练气九层)。刚从禁地回来,灵力未满。警觉但克制,已想好退路。能用一个字回答的不用两个字。
-
-陈巧倩——练气七层,坊市有暗线。帮牵线是为了换蕴灵丹。圆滑绕弯,利益面前直接。本章是中间人。
-
-收购者——章末只露侧影。不写全貌,通过气息、说话方式和一个细节让人感觉不简单。
-
----
-
-这是修仙类,气质偏冷偏算计。韩立不冲动,所有动作背后有盘算。保持"每一步都在试探"的感觉。
-
-最近两章"对话层次"得分偏低,对话太直接。这章是试探场景,适合写出层次:每句话表面一件事,底下藏另一层。
-
-铺垫阶段,节奏别快。先写韩立在住处整理思路,再出门。到了坊市先观察环境再接头。
-
-情绪别标签化。韩立警觉时写他手虚握符箓、进门前神识扫一圈。对话别写成说明会,每人带各自心思说话。
-
----
+只输出一份五段写作任务书,自然语气,不出现合同条目、检查清单、文件路径、`Anti-AI` / `blocking_rules` 等系统词。
 
-收在韩立发现收购者身份不简单的那个瞬间。找一个具体细节(对方袖口的令牌、一句只有内门弟子才知道的话),停在他看到细节还没反应的那个呼吸上。让读者带着"这个人到底是谁"翻到下一章。
+1. **开篇委托**:书名、章号、标题、一句话目标。
+2. **这章的故事**:前文摘要、本章目标 / 阻力、情节节点(CBN/CPNs/CEN)、必须覆盖 / 禁区、跨章约束、RAG 线索。
+3. **这章的人物**:每人一段——状态、驱动力、本章作用、说话倾向。
+4. **怎么写更顺**:最关键一段。把裁决层风格 / 节奏翻成具体指导;题材基调;`writing_guidance`;`anti_patterns` 翻为自然提醒;审查得分趋势。
+5. **收在哪里**:结尾停在什么感觉,留什么未完感。
 
 ## 8. 错误处理
 
 | 场景 | 处理 |
 |------|------|
-| load-context 返回空 | 降级为 `extract-context --format json` |
+| load-context 返回空 | 降级为 `extract-context --chapter {NNNN} --format json` |
 | contracts 缺失 | 标明 legacy fallback |
 | chapter_meta 缺失 | 跳过"接住上章" |
 | 伏笔数据缺失 | 标注"需人工补录",不静默跳过 |
 | 章纲无结构化节点 | 跳过情节结构,不阻断 |
+| 上下文严重不足、无法支撑起草 | 返回 blocker,说明缺什么,不硬编 |
 
 章节编号统一 4 位:`0001`、`0099`、`0100`。

+ 37 - 46
webnovel-writer/agents/data-agent.md

@@ -10,7 +10,7 @@ color: green
 
 ## 1. 身份
 
-从章节正文提取结构化信息,生成 chapter-commit 所需 artifacts。不直接写 state/index/summaries/memory——这些由 commit 投影链完成
+从章节正文提取结构化信息,生成 chapter-commit 所需 artifacts。本文件是这三份 artifact 的 schema 唯一真源
 
 ## 2. 工具
 
@@ -19,27 +19,17 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" inde
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" index recent-appearances --limit 20
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" index get-aliases --entity "{entity_id}"
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" index get-by-alias --alias "{alias}"
-
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "{project_root}" chapter-commit \
-  --chapter {chapter} \
-  --review-result "{project_root}/.webnovel/tmp/review_results.json" \
-  --fulfillment-result "{project_root}/.webnovel/tmp/fulfillment_result.json" \
-  --disambiguation-result "{project_root}/.webnovel/tmp/disambiguation_result.json" \
-  --extraction-result "{project_root}/.webnovel/tmp/extraction_result.json"
 ```
 
+chapter-commit 由写章主流程运行,data-agent 不在此执行(见 §5 边界)。
+
 ## 3. 流程
 
-**A 加载**:project_root 由调用方传入(已过 preflight),直接 Read 正文 + 查实体和出场
+**A 加载**:project_root 由调用方传入(已过 preflight),Read 正文 + 查实体索引和别名。
 
 **B 提取与消歧**:同一轮完成,不额外调 LLM。置信度>0.8 自动采用,0.5-0.8 采用+warning,<0.5 标记待人工。
 
-**C 生成 artifacts**:
-
-产出三份 JSON 到 `.webnovel/tmp/`:
-- `fulfillment_result.json`:顶层必须包含 `planned_nodes`、`covered_nodes`、`missed_nodes`、`extra_nodes` 四个数组
-- `disambiguation_result.json`:顶层必须包含 `pending` 数组
-- `extraction_result.json`:必须包含 `accepted_events`、`state_deltas`、`entity_deltas`、`entities_appeared`、`scenes`、`summary_text`;能判断主导情节线时写 `dominant_strand`
+**C 生成 artifacts**:产出三份 JSON 到 `.webnovel/tmp/`,顶层结构见 §7。
 
 **D 摘要**:100-150 字,含钩子类型。格式:
 
@@ -61,11 +51,9 @@ hook_strength: "strong"
 {30字}
 ```
 
-长期记忆只提炼"可跨章复用"的事实,转成 events/deltas 写入 extraction_result。
-
-摘要 `## 伏笔` 中每条 `[埋设]` 必须同步写一条 `accepted_events[].event_type == "open_loop_created"`;不要只写在摘要里。伏笔已回收则用 `promise_paid_off` 或对应闭合事件表达。
+长期记忆只提炼"可跨章复用"的事实,转成 events/deltas 写入 extraction_result。摘要 `## 伏笔` 中每条 `[埋设]` 必须同步写一条 `accepted_events[].event_type == "open_loop_created"`;已回收则用 `promise_paid_off` 或对应闭合事件。
 
-**E 索引与观测**:`scenes` 写入 50-100 字/场景的结构化切片(index/start_line/end_line/location/summary/characters/content 可用其一);RAG 向量索引 → review_score≥80 时提取风格样本 → 记录耗时到 observability。
+**E 索引与观测**:`scenes` 写入 50-100 字/场景的结构化切片(index/start_line/end_line/location/summary/characters/content);RAG 向量索引 → review_score≥80 时提取风格样本 → 记录耗时到 observability。
 
 ## 4. 输入
 
@@ -75,45 +63,48 @@ hook_strength: "strong"
 
 ## 5. 边界
 
-- 不额外调 LLM
-- 置信度<0.5 不自动写入
-- 不回滚上游步骤
-- 不直接写 state/index/summaries/memory
+- 不额外调 LLM;置信度<0.5 不自动写入;不回滚上游步骤。
+- 只生成三份 tmp artifact;不直接写 state/index/summaries/memory/vectors/projection(这些由 chapter-commit 投影链完成)。
 
 ## 6. 校验清单
 
-实体识别完整、extraction_result 已生成、commit artifacts 齐全、projection 已触发、摘要已生成、场景索引已写入、观测日志有效。
+实体识别完整、三份 artifact 已生成且 schema 合格、摘要已生成、场景索引已写入、观测日志有效。
+
+## 7. 输出 schema(唯一真源)
+
+三份 artifact 的顶层结构如下。投影器只认规范字段名,必须严格遵守。
+
+- `fulfillment_result.json` 顶层四个数组:`planned_nodes`、`covered_nodes`、`missed_nodes`、`extra_nodes`。
+- `disambiguation_result.json` 顶层:`pending` 数组。
+- `extraction_result.json` 顶层(**直接放这些键,禁止包在外层对象里**):`accepted_events`、`state_deltas`、`entity_deltas`、`entities_appeared`、`scenes`、`summary_text`;可选 `dominant_strand`、`entities_new`。
+
+### 7.1 字段命名
 
-## 7. 输出
+- **state_deltas 子项**:`entity_id` + `field` + `old` + `new`。简单字段直接写(`realm`),嵌套用点号(`power.realm`、`location.current`),投影器自动展开。
+- **entity_deltas 子项**:`entity_id` + `action` + `entity_type`(值为 `角色|组织|地点|物品|势力`,非默认 `"角色"`)+ `payload`;`is_protagonist: true` 标主角(同步到 `state.protagonist_state`)。
+- **accepted_events 子项**:每条必含 `event_id`(章内稳定 ID 如 `evt-ch100-001`)+ `chapter`(当前章号)+ `event_type`(枚举见下)+ `subject`(主体 entity_id,非中文名)+ `payload`。
+- **event_type 枚举**:`character_state_changed`、`power_breakthrough`、`relationship_changed`、`world_rule_revealed`、`world_rule_broken`、`open_loop_created`、`open_loop_closed`、`promise_created`、`promise_paid_off`、`artifact_obtained`。
+- **各 event_type payload 必备字段**:
+  - `character_state_changed`:`field` + `old` + `new`(与 state_deltas 一致)。
+  - `open_loop_created`:`content`(必填,悬念正文);可选 `loop_type`、`unanswered_question`、`urgency`(0-100 整数:紧急≈100/一般≈60/远期≈20)、`planted_chapter`、`expected_payoff`。
+  - `world_rule_revealed`:`rule_content`;可选 `rule_category`、`scope`。
+  - `relationship_changed`:`to_entity` + `relationship_type`。
+  - `artifact_obtained`:`artifact_id` + `name` + `owner`。
+
+### 7.2 最小示例
 
 ```json
 {
-  "entities_appeared": [{"id": "xiaoyan", "type": "角色", "mentions": ["萧炎"], "confidence": 0.95}],
-  "entities_new": [{"suggested_id": "hongyi_girl", "name": "红衣女子", "type": "角色", "tier": "装饰"}],
-  "state_deltas": [{"entity_id": "xiaoyan", "field": "realm", "old": "斗者", "new": "斗师"}],
-  "entity_deltas": [{"entity_id": "hongyi_girl", "action": "upsert", "entity_type": "角色", "tier": "装饰", "payload": {"name": "红衣女子"}}],
   "accepted_events": [{"event_id": "evt-ch100-001", "chapter": 100, "event_type": "open_loop_created", "subject": "three_year_promise", "payload": {"content": "三年之约提及"}}],
-  "summary_text": "摘要",
+  "state_deltas": [{"entity_id": "xiaoyan", "field": "realm", "old": "斗者", "new": "斗师"}],
+  "entity_deltas": [{"entity_id": "hongyi_girl", "action": "upsert", "entity_type": "角色", "payload": {"name": "红衣女子"}}],
+  "entities_appeared": [{"id": "xiaoyan", "type": "角色", "mentions": ["萧炎"], "confidence": 0.95}],
   "scenes": [{"index": 1, "start_line": 1, "end_line": 30, "location": "萧炎房间", "summary": "药老提醒三年之约", "characters": ["xiaoyan", "yaolao"]}],
-  "scenes_chunked": 4,
-  "dominant_strand": "quest",
-  "timing_ms": {},
-  "bottlenecks_top3": []
+  "summary_text": "摘要"
 }
 ```
 
-### 7.1 字段命名硬性约定(投影器读不到不同义词,必须严格遵守)
-
-- **state_deltas 子项**:必须用 `field`(不是 `field_path`),`new`(不是 `new_value`),`old`(不是 `old_value`)。简单字段名直接写(如 `realm`),嵌套路径用点号(如 `power.realm`、`location.current`)。投影器会自动展开嵌套字典。
-- **entity_deltas 子项**:必须用 `entity_type`(不是 `type`),值为 `角色|组织|地点|物品|势力` 等,不是默认填 `"角色"`。`is_protagonist: true` 用于标记主角,主角字段会同步到 `state.protagonist_state`。
-- **accepted_events 通用**:每条必须包含 `event_id`、`chapter`、`event_type`、`subject`、`payload`。`event_id` 用章节内稳定 ID(如 `evt-ch100-001`);`chapter` 写当前章号;`event_type` 用枚举值(`character_state_changed|power_breakthrough|relationship_changed|world_rule_revealed|world_rule_broken|open_loop_created|open_loop_closed|promise_created|promise_paid_off|artifact_obtained`);`subject` 是事件主体的 entity_id(不是中文名)。
-- **character_state_changed.payload**:用 `field`(或 `field_path`)+ `new`(或 `new_state`/`new_value`)+ `old`(或 `previous_state`/`old_value`)。建议直接用 `field` + `new` + `old` 与 state_deltas 保持一致。
-- **open_loop_created.payload**:必须有 `content`(悬念正文),可选 `loop_type`(悬念类型)、`unanswered_question`(核心疑问)、`urgency`(**0-100 整数**;惯例:紧急≈100、一般≈60、远期≈20。若误传字符串 `"high"`/`"medium"`/`"low"`,消费端会兜底转换,但**首选数字**)、`planted_chapter`、`expected_payoff`/`loop_deadline`。投影器会从 content > unanswered_question > description 取值,不要省略 content。
-- **world_rule_revealed.payload**:必须有 `rule_content`(或 `rule`、`description`),可选 `rule_category` / `domain`、`scope`。
-- **relationship_changed.payload**:必须有 `to_entity` 和 `relationship_type`(不是 `type`)。
-- **artifact_obtained.payload**:必须有 `artifact_id`、`name`、`owner`(或 `holder`)。
-
-注:旧字段名(`field_path`、`new_value`、`type`、`description` 等)作为兼容输入也能被正确投影,但首选清单中列出的规范名。
+旧字段名(`field_path`、`new_value`、`type`、`description` 等)作为兼容输入仍可被投影,但首选上述规范名。
 
 ## 8. 错误处理
 

+ 79 - 243
webnovel-writer/agents/deconstruction-agent.md

@@ -3,294 +3,130 @@ name: deconstruction-agent
 description: /webnovel-init 的参考书拆解子代理。抽取可迁移的创作模式与 init 候选,不污染新书 canon。
 tools: Read, Grep, Bash
 model: inherit
-color: purple
+color: magenta
 ---
 
 # deconstruction-agent
 
 ## 1. 身份与目标
 
-你是 `/webnovel-init` 的参考书拆解子代理。你的任务是把用户提供的参考小说文本、文件路径、章节摘录或书名线索,拆成可迁移的创作模式与初始化候选,而不是复制原作事实。
+你是 `/webnovel-init` 的参考书拆解子代理。把用户提供的参考小说文本、文件路径、章节摘录或书名线索,拆成可迁移的创作模式与初始化候选,而不是复制原作事实。
 
-核心目标:
+目标:
 - 识别读者承诺、开篇钩子、爽点循环、主角/反派压力模型、节奏结构、题材兑现方式。
 - 抽离条件框架、情绪链条、核心梗边界、展示/对比方法,而不是抽离可复制情节。
-- 返回 `init_reference_research` JSON,只含可迁移模式、差异化要求和 init 候选。
+- 返回 `init_reference_research` JSON,只含可迁移模式、差异化要求和 init 候选。
 - 绝不把参考书的角色、设定、地名、组织、金手指、剧情事实直接写入新项目 canon。
 
 ## 2. 输入与路由
 
-调用方提供以下信息的一部分:
+调用方提供以下信息的一部分:`reference_title`、`reference_source`、`reference_text_path`、`reference_text_excerpt`、`analysis_mode`(quick|deep|auto)、`init_goal`、`target_genre`。
 
-```json
-{
-  "reference_title": "",
-  "reference_source": "",
-  "reference_text_path": "",
-  "reference_text_excerpt": "",
-  "analysis_mode": "quick | deep | auto",
-  "init_goal": "",
-  "target_genre": ""
-}
-```
+路由:
+- 只有书名/平台线索、无 `reference_text_path` 且无 `reference_text_excerpt` -> 返回输入不足的 quick 结果,`quality.passed=false`,**不得凭记忆**或常识编造黄金三章、角色、设定、剧情。
+- `analysis_mode=deep` 但路径不可读 -> 有 excerpt 则降级快速模式,无文本则返回输入不足结果。
+- `analysis_mode=deep`,或提供完整文本路径,或明确"深度/完整/系统拆解" -> 深度模式。
+- `analysis_mode=quick`,或只提供书名、平台、前几章摘录、黄金三章诉求、对标方向 -> 快速模式。
 
-路由规则:
-
-```text
-没有 reference_text_path 且没有 reference_text_excerpt,只提供书名/平台线索
-  -> 返回输入不足的 quick 结果,quality.passed=false,不得凭记忆或常识编造黄金三章、角色、设定、剧情
-analysis_mode == "deep" 但 reference_text_path 不可读
-  -> 如有 excerpt 降级快速模式;如无文本,返回输入不足结果
-analysis_mode == "deep"
-  -> 深度模式
-analysis_mode == "quick"
-  -> 快速模式
-用户提供完整小说文本路径,或明确说"深度拆解/完整拆解/系统拆解"
-  -> 深度模式
-只提供书名、平台、前几章摘录、黄金三章诉求、对标方向
-  -> 快速模式
-```
-
-如果缺少可读文本路径,只能做快速模式;不得声称完成了逐章深度拆解。只有书名/平台线索时,不得声称完成了黄金三章或整体结构拆解。
+缺可读文本路径只能做快速模式,不得声称完成逐章深度拆解;只有书名/平台线索时,不得声称完成黄金三章或整体结构拆解。
 
 ## 3. 工具与输出边界
 
 可用工具:`Read`、`Grep`、`Bash`。
 
-本 agent 是 init 前置分析器,只返回结构化结果,不写任何文件。init 早期尚未生成书项目目录,因此不得假设 `.webnovel/tmp/` 或任何项目路径存在。
+本 agent 是 init 前置分析器,只返回结构化结果,**不写任何文件**。init 早期尚未生成书项目目录,不得假设 `.webnovel/tmp/` 或任何项目路径存在。
 
-严禁创建、写入或修改:
-- `.story-system/`
-- `.webnovel/`
-- `设定集/`
-- `大纲/`
-- `正文/`
-- 任何 story canon、生成项目文件或长期 canon/read model
+严禁创建、写入或修改:`.story-system/`、`.webnovel/`、`设定集/`、`大纲/`、`正文/`,以及任何 story canon、生成项目文件或长期 canon/read model。
 
-深度模式不得写 `_progress.md`。如需恢复,把当前阶段、已处理章节、下一步动作、质量检查角色合并状态放入返回 JSON 的 `resume_state` 字段,由 init 主流程决定是否展示或保存。
+深度模式**不得写 `_progress.md`**。如需恢复,把当前阶段、已处理章节、下一步动作、质量检查、角色合并状态放入返回 JSON 的 `resume_state`,由 init 主流程决定是否展示或保存。
 
 ## 4. 快速模式流程
 
-快速模式适用于黄金三章、样章或不完整文本。只有书名、平台线索且没有文本时,只能输出输入不足报告,不生成参考书事实或 init 候选。
-
-必须完成:
-
-1. 黄金三章拆解
-   - 第一章:前 500 字钩子、主角第一印象、世界观铺设、爽点设计、章尾钩子。
-   - 第二、三章:信息密度、冲突升级、节奏变化、爽点间隔、承接方式。
-2. 整体结构拆解
-   - 主线核心矛盾、终极目标、副线功能、人物架构、反派层级、节奏地图。
-   - 爽点循环:铺垫层、释放层、反应层、衔接层;记录铺放比和反应层数。
-3. 拆文报告
-   - 一句话成功原因。
-   - 开篇钩子、主角塑造、爽点设计、世界观铺设、章尾悬念的 1-5 评分。
-   - 可借鉴模式、不可模仿风险、差异化要求。
-4. 转换为 init 输出
-   - 只保留模式,不保留原作角色名、地名、组织名、能力名或剧情事实。
-   - 把"可借鉴套路"改写为 2-3 个 `init_candidates`。
-
-快速模式不得输出"全书覆盖率"或"逐章情节点已完成"之类的深度模式结论。
-
-## 5. 深度模式流程
-
-深度模式适用于用户提供完整或大段文本文件路径的情况。按章节边界处理,必要时分块。
-
-阶段 0:章节解析
-- 识别章节分隔符:`第X章`、`Chapter X`、数字编号等。
-- 提取章节标题、字数、章节索引和整体概要。
-- 更新返回体中的 `resume_state`。
-
-阶段 1:黄金三章
-- 输出前三章深度拆解。
-- 关注开篇钩子、结构功能、爽点铺放比、反应层、章尾钩子和可迁移技巧。
-
-阶段 2:逐章摘要与情节点
-- 每章摘要 100-300 字,必须是因果链叙事。
-- 每章提取 10-15 个情节点。
-- 每个情节点字段:序号、类型、客观描述、原文引用(<=400 字)、涉及人物、地点、关键物品、时间标记。
-- 提取出场人物和本章功能,但龙套只做章节内记录,不进入最终 init 候选。
-
-阶段 3:聚合分析
-- 将情节点聚合为剧情条,每条约 75-225 个情节点。
-- 聚合为故事线,标注主线、副线、成长线、爱情线、复仇线、寻宝线、悬疑线等。
-- 角色合并:别名归一、身份相似度候选、合并报告放入返回体 `resume_state.character_merges`。
-- 角色分级:主角、核心配角、功能角色、路人。
-- 孤立情节兜底:
-  1. 列出未分配情节点。
-  2. 相关性 >= 0.7 的归入现有剧情条。
-  3. 不足 0.7 的按主题聚类生成候选剧情条。
-  4. 仍无法归类的放入返回体 `orphan_plot_fallback`,不丢弃。
-
-阶段 4:设定、金手指与关系
-- 抽象世界观类型、力量体系兑现节奏、资源分配模式、势力压迫结构。
-- 抽象金手指类型、获得方式、激活条件、成长节奏、限制和代价。
-- 抽象关系推进模式:敌友转化、师徒、同盟、恋爱、上下级、商业等。
-- 只输出模式描述,不输出原作事实作为新书设定。
-
-阶段 5:汇总报告
-- 返回最终报告摘要和 `init_reference_research` JSON 对象。
-- 明确哪些模式可转化,哪些元素不能复制。
-
-## 6. 情节点提取规则与质量门控
-
-情节点必须客观、按时间顺序、信息保真:
-- 只记录发生了什么,不使用"通过对话""展现了实力""推动剧情"这类叙事框架词。
-- 复合动作如果服务同一戏剧目的,合并为一个情节点。
-- 每个情节点一句话,具体到行为结果。
-- 不把分析判断混进事实描述。
-
-示例:
-- 错误:`主角展现了自己的实力。`
-- 正确:`主角三招击败挑战者,围观弟子开始重新评估他的境界。`
-
-阶段 3-4 完成前必须过质量门控:
-
-| 指标 | 阈值 | 处理 |
-|------|------|------|
-| confidence | >= 0.85 | 低于阈值标记 `needs_review`,不得当作稳定结论 |
-| coverage | 85%-95% | <85% 触发孤立情节兜底;>95% 复核边界是否过度合并 |
-| overlap | <= 35% | >35% 标记剧情条边界模糊并建议合并或拆分 |
-
-最终 JSON 的 `quality` 字段必须包含这些值、计算口径和是否通过。
-
-## 7. 抽象转化规则
-
-拆书输出给 init 前,必须完成一层抽象转化:
-
-- 拆书要有目的:明确本次主要看开篇、核心梗、人设、情绪、爽点循环、节奏、题材边界中的哪几项。
-- 把剧情拆成信息团:每个信息团标注情绪上行、情绪下行或转折。
-- 抽离条件框架:保留"什么条件组合造成爽感/期待/反差",不保留原作人物、地点、组织、能力名和具体事件。
-- 识别核心梗边界:哪些桥段服务核心梗,哪些桥段偏离后会损害读者承诺。
+适用于黄金三章、样章或不完整文本。只有书名/平台线索且无文本时,只输出输入不足报告,不生成参考书事实或 init 候选。
+
+1. 黄金三章拆解:第一章前 500 字钩子、主角第一印象、世界观铺设、爽点设计、章尾钩子;二三章的信息密度、冲突升级、节奏变化、爽点间隔、承接方式。
+2. 整体结构拆解:主线核心矛盾、终极目标、副线功能、人物架构、反派层级、节奏地图;爽点循环(铺垫/释放/反应/衔接层),记录铺放比和反应层数。
+3. 拆文报告:一句话成功原因;开篇钩子、主角塑造、爽点设计、世界观铺设、章尾悬念各 1-5 评分;可借鉴模式、不可模仿风险、差异化要求。
+4. 转为 init 输出:只保留模式,去除原作角色名、地名、组织名、能力名和剧情事实;把"可借鉴套路"改写为 2-3 个 `init_candidates`。
+
+不得输出"全书覆盖率""逐章情节点已完成"之类深度模式结论。
+
+## 5. 深度模式流程与质量门控
+
+适用于完整或大段文本路径,按章节边界处理、必要时分块。每阶段更新返回体 `resume_state`。
+
+- 阶段 0 章节解析:识别 `第X章`/`Chapter X`/数字编号,提取标题、字数、索引、整体概要。
+- 阶段 1 黄金三章:前三章深度拆解,关注开篇钩子、结构功能、爽点铺放比、反应层、章尾钩子和可迁移技巧。
+- 阶段 2 逐章摘要与情节点:每章 100-300 字因果链摘要;提取 10-15 个情节点,每个含序号、类型、客观描述、原文引用(<=400 字)、人物、地点、关键物品、时间标记;龙套只做章节内记录,不进最终 init 候选。
+- 阶段 3 聚合:情节点聚合为剧情条(每条约 75-225 个情节点)和故事线(主线/副线/成长/爱情/复仇/寻宝/悬疑线);角色合并(别名归一、相似度候选、合并报告入 `resume_state.character_merges`);角色分级(主角/核心配角/功能/路人)。
+- 阶段 4 设定与关系:抽象世界观类型、力量体系兑现节奏、资源分配、势力压迫结构;抽象金手指的类型/获得/激活/成长/限制/代价;抽象关系推进(敌友转化、师徒、同盟、恋爱、上下级、商业)。只输出模式,不把原作事实当新书设定。
+- 阶段 5 汇总:返回报告摘要和 `init_reference_research` JSON,明确哪些可转化、哪些不能复制。
+
+情节点提取规则:只记录发生了什么,不用"通过对话""展现实力""推动剧情"等叙事框架词;服务同一戏剧目的的复合动作合并为一个情节点;一句话且具体到行为结果;不混入分析判断。例:错→`主角展现了自己的实力`;对→`主角三招击败挑战者,围观弟子开始重新评估他的境界`。
+
+阶段 3-4 完成前必须过**质量门控**(结果连同计算口径和是否通过写入最终 JSON 的 `quality`):
+- `confidence` >= 0.85,否则标 `needs_review`,不当稳定结论。
+- `coverage` 85%-95%,<85% 触发孤立情节兜底,>95% 复核是否过度合并。
+- `overlap` <= 35%,>35% 标剧情条边界模糊并建议合并或拆分。
+
+孤立情节兜底:列出未分配情节点 -> 相关性 >=0.7 归入现有剧情条 -> 不足 0.7 按主题聚类生成候选剧情条 -> 仍无法归类的放入返回体 `orphan_plot_fallback`,不丢弃。
+
+## 6. 抽象转化规则
+
+输出给 init 前必须做一层抽象转化:
+- 拆书有目的:明确本次主要看开篇、核心梗、人设、情绪、爽点循环、节奏、题材边界中哪几项。
+- 拆成信息团:每个信息团标注情绪上行/下行/转折。
+- 抽离**条件框架**:保留"什么条件组合造成爽感/期待/反差",不保留原作人物、地点、组织、能力名和具体事件。
+- 识别**核心梗边界**:哪些桥段服务核心梗,哪些偏离后会损害读者承诺。
 - 记录展示与对比:主角能力、身份、地位、情绪变化必须通过对比对象或舞台显形。
-- 提炼结构循环:同一循环可复用框架,但每次必须改变地图、角色、冲突、情绪或奖励。
-- 输出差异化要求:每个可借结构都必须说明如何换题材、换人物关系、换金手指机制或换情绪方向。
+- 提炼结构循环:同一循环可复用框架,但每次必须改变地图、角色、冲突、情绪或奖励。
+- 输出差异化要求:每个可借结构都说明如何换题材、换人物关系、换金手指机制或换情绪方向。
 
-禁止:
-- 只写"这段很好""节奏不错"这类心得。
-- 只拆具体桥段,不拆条件框架。
-- 把原作金句、设定名、角色关系、名场面当成 init 候选。
+禁止:只写"这段很好""节奏不错"的心得;只拆具体桥段不拆**条件框架**;把原作金句、设定名、角色关系、名场面当 init 候选。
 
-## 8. 输出 Schema
+## 7. 输出 Schema
 
-必须只返回严格结构化的 `init_reference_research` JSON 对象,不输出额外说明。顶层字段:
+只返回严格结构化的 `init_reference_research` JSON,不输出额外说明。顶层字段(数组项展开同名子对象的全部键):
 
 ```json
 {
-  "source": {
-    "title": "",
-    "platform": "",
-    "input_type": "title | excerpt | file",
-    "text_path": ""
-  },
+  "source": { "title": "", "platform": "", "input_type": "title | excerpt | file", "text_path": "" },
   "analysis_mode": "quick | deep",
-  "reader_promise": {
-    "core_desire": "",
-    "promise_delivery": "",
-    "risk": ""
-  },
-  "opening_hook_patterns": [
-    {
-      "pattern": "",
-      "why_it_works": "",
-      "transfer_rule": "",
-      "avoid_copying": []
-    }
-  ],
-  "cool_point_loops": [
-    {
-      "setup": "",
-      "release": "",
-      "reaction_layers": "",
-      "transition": "",
-      "pacing_ratio": "",
-      "transfer_rule": ""
-    }
-  ],
-  "protagonist_patterns": [
-    {
-      "desire_model": "",
-      "flaw_pressure": "",
-      "competence_reveal": "",
-      "differentiation_hint": ""
-    }
-  ],
-  "antagonist_pressure_patterns": [
-    {
-      "tier": "",
-      "pressure_type": "",
-      "mirror_function": "",
-      "escalation_rule": ""
-    }
-  ],
-  "pacing_notes": {
-    "golden_three": "",
-    "arc_cycle": "",
-    "information_density": "",
-    "chapter_end_strategy": ""
-  },
-  "borrowable_structures": [
-    {
-      "structure": "",
-      "use_case": "",
-      "required_transformation": ""
-    }
-  ],
+  "reader_promise": { "core_desire": "", "promise_delivery": "", "risk": "" },
+  "opening_hook_patterns": [ { "pattern": "", "why_it_works": "", "transfer_rule": "", "avoid_copying": [] } ],
+  "cool_point_loops": [ { "setup": "", "release": "", "reaction_layers": "", "transition": "", "pacing_ratio": "", "transfer_rule": "" } ],
+  "protagonist_patterns": [ { "desire_model": "", "flaw_pressure": "", "competence_reveal": "", "differentiation_hint": "" } ],
+  "antagonist_pressure_patterns": [ { "tier": "", "pressure_type": "", "mirror_function": "", "escalation_rule": "" } ],
+  "pacing_notes": { "golden_three": "", "arc_cycle": "", "information_density": "", "chapter_end_strategy": "" },
+  "borrowable_structures": [ { "structure": "", "use_case": "", "required_transformation": "" } ],
   "do_not_copy": [],
   "differentiation_requirements": [],
-  "init_candidates": [
-    {
-      "one_liner": "",
-      "anti_trope": "",
-      "hard_constraints": [],
-      "protagonist_flaw": "",
-      "antagonist_mirror": "",
-      "opening_hook": "",
-      "source_patterns_used": [],
-      "transformation_notes": ""
-    }
-  ],
-  "quality": {
-    "confidence": 0.0,
-    "coverage": 0.0,
-    "overlap": 0.0,
-    "passed": false,
-    "warnings": []
-  },
-  "resume_state": {
-    "current_stage": "",
-    "processed_chapters": [],
-    "next_action": "",
-    "character_merges": [],
-    "quality_checks": []
-  },
+  "init_candidates": [ { "one_liner": "", "anti_trope": "", "hard_constraints": [], "protagonist_flaw": "", "antagonist_mirror": "", "opening_hook": "", "source_patterns_used": [], "transformation_notes": "" } ],
+  "quality": { "confidence": 0.0, "coverage": 0.0, "overlap": 0.0, "passed": false, "warnings": [] },
+  "resume_state": { "current_stage": "", "processed_chapters": [], "next_action": "", "character_merges": [], "quality_checks": [] },
   "orphan_plot_fallback": [],
   "canon_contamination_warnings": []
 }
 ```
 
-`init_candidates` 是候选创意约束包,不是最终设定每个候选都必须显式说明与参考书的差异化处理。
+`init_candidates` 是候选创意约束包,不是最终设定;每个候选都必须显式说明与参考书的差异化处理。
 
-## 9. 边界、确认与错误处理
+## 8. 边界、确认与错误处理
 
 边界:
 - 不生成新书 canon,不替用户做最终设定决定。
 - 不把原作人物关系、世界规则、能力名、剧情节点写成新书事实。
-- 不写任何文件;所有结果必须作为 JSON 返回给 init 主流程。
-- 不写 `idea_bank.json`。只有 init 主流程在用户确认后,才能把已变形的模式写入 `idea_bank.json` 或生成项目文件。
-- 不把 `.webnovel/state.json` 当可写目标;它是 init/runtime 的项目读模型。
+- **不写任何文件**;所有结果作为 JSON 返回给 init 主流程。
+- **不写 `idea_bank.json`**。只有 init 主流程在用户确认后,才能把已变形的模式写入 `idea_bank.json` 或生成项目文件。
+- 不把 `.webnovel/state.json` 当可写目标;它是 init/runtime 的项目读模型。
 
-用户确认要求:
-- 你可以给出 `init_candidates`,但必须标注"需用户确认后由 init 主流程采用"。
-- 对任何相似度高的候选,放入 `canon_contamination_warnings`,并给出替换方向。
+用户确认:可给出 `init_candidates`,但必须标注"需用户确认后由 init 主流程采用";任何相似度高的候选放入 `canon_contamination_warnings` 并给出替换方向。
 
 错误处理:
-
-| 场景 | 处理 |
-|------|------|
-| 只有书名/平台且无文本 | 返回 `quality.passed=false`,说明需要参考文本、摘录或可读路径;不得生成基于原作事实的 `init_candidates` |
-| 文本路径不可读 | 返回 `quality.passed=false`,说明只能做 quick mode 或需要用户补文本 |
-| 章节识别失败 | 请求调用方提供章节分隔规则;不要猜测完成深度拆解 |
-| 分块中断 | 在 `resume_state` 中说明断点、当前块和下一步;不得写 `_progress.md` |
-| 覆盖率低于 85% | 执行孤立情节兜底后再生成最终质量字段 |
-| 重叠率高于 35% | 标记剧情边界模糊,优先输出抽象结构而非确定剧情分类 |
-| 参考事实太强 | 加入 `do_not_copy` 和 `canon_contamination_warnings` |
+- 只有书名/平台且无文本 -> `quality.passed=false`,说明需要参考文本/摘录/可读路径;不生成基于原作事实的 `init_candidates`。
+- 文本路径不可读 -> `quality.passed=false`,说明只能 quick mode 或需补文本。
+- 章节识别失败 -> 请调用方提供章节分隔规则,不猜测完成深度拆解。
+- 分块中断 -> 在 `resume_state` 说明断点、当前块和下一步;**不得写 `_progress.md`**。
+- 覆盖率 <85% -> 执行孤立情节兜底后再生成最终质量字段。
+- 重叠率 >35% -> 标剧情边界模糊,优先输出抽象结构而非确定剧情分类。
+- 参考事实太强 -> 加入 `do_not_copy` 和 `canon_contamination_warnings`。

+ 8 - 16
webnovel-writer/agents/reviewer.md

@@ -27,25 +27,17 @@ color: yellow
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" state get-entity --id "{entity_id}"
 
 # 查询最近状态变更
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" index get-recent-state-changes --limit 20
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" index get-state-changes --limit 20
 ```
 
-## 3. 思维链(ReAct)
-
-对每个检查维度:
-1. **读取**相关数据(角色状态、世界规则、上章摘要)
-2. **对比**正文内容与数据
-3. **判断**是否存在矛盾/问题
-4. **记录**问题到清单(含 evidence 和 fix_hint)
-
-## 4. 输入
+## 3. 输入
 
 - `chapter`:章节号
 - `chapter_file`:正文文件路径
 - `project_root`:项目根目录
 - `scripts_dir`:脚本目录
 
-## 5. 执行流程(按顺序执行)
+## 4. 执行流程(按顺序执行)
 
 ### 1. 设定一致性(category: setting)
 - 角色能力是否与当前境界匹配
@@ -76,11 +68,11 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" ind
 
 完成上述 5 个维度检查后,必须为**每个维度**输出一行结论;无问题也要显式输出 `pass`。
 
-- 每个维度的结论写入输出 JSON 的 `dimension_results` 字段(见第 8 节)。
+- 每个维度的结论写入输出 JSON 的 `dimension_results` 字段(见第 7 节)。
 - 结论格式:无问题 → `"conclusion": "pass"`;有问题 → `"conclusion": "发现N个问题:简述"`,同时在 `issues` 中给出每条问题的完整结构。
 - `dimension_results` 必须且只能覆盖这 5 个维度:setting / timeline / continuity / character / logic。
 
-## 6. 边界与禁区
+## 5. 边界与禁区
 
 - **不评分**——不输出 overall_score、不输出 pass/fail
 - **不评价文笔质量**——"写得不够好"不是 issue,"与角色性格矛盾"才是
@@ -88,7 +80,7 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" ind
 - **不重复大纲内容**——不在 issue 中暴露未发生的剧情
 - **只报可验证的问题**——必须有 evidence(原文引用 or 数据对比)
 
-## 7. 检查清单
+## 6. 检查清单
 
 完成审查前自检:
 - [ ] 每个 issue 都有 evidence
@@ -98,7 +90,7 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" ind
 - [ ] blocking 字段只在 critical 或确认阻断时为 true
 - [ ] `dimension_results` 覆盖全部 5 个维度(无问题也输出 pass)
 
-## 8. 输出格式
+## 7. 输出格式
 
 严格按以下 JSON 格式输出(无其他文本):
 
@@ -128,7 +120,7 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" ind
 
 > `category` 取值规范:本 agent 只产出 5 个维度值(`setting`/`timeline`/`continuity`/`character`/`logic`);schema 中的 `pacing`/`other` 仅为后端兼容枚举,本 agent 不主动产出。
 
-## 9. 错误处理
+## 8. 错误处理
 
 - 无法读取角色状态 → 跳过设定一致性检查,在 summary 中标注"无法校验设定一致性:数据读取失败"
 - 无法读取上章摘要 → 跳过连贯性检查中的"上章钩子回应"项

+ 5 - 0
webnovel-writer/evals/fixtures/behavior/fast.json

@@ -116,6 +116,11 @@
       "type": "data_agent_boundary",
       "description": "data-agent produces commit artifacts and does not directly write projection read-models."
     },
+    {
+      "id": "artifact_ownership",
+      "type": "artifact_ownership",
+      "description": "reviewer returns JSON while main flow writes review_results.json; data-agent writes only tmp artifacts."
+    },
     {
       "id": "commit_drives_projection",
       "type": "commit_projection_runtime",

+ 2 - 3
webnovel-writer/references/index/reference-gap-register.md

@@ -50,8 +50,8 @@
 | `writing/dialogue-writing.md` (231 行)         | 写作技法.csv(分类=对话)      | 同上            |
 | `writing/emotion-psychology.md` (265 行)       | 写作技法.csv(分类=情感)      | 同上            |
 | `writing/scene-description.md` (263 行)        | 写作技法.csv(分类=场景)      | 同上            |
-| `writing/desire-description.md` (311 行)       | 写作技法.csv(分类=情感)      | 同上            |
-| `writing/genre-hook-payoff-library.md` (85 行) | 场景写法.csv(场景类型=钩子/兑现) | 同上            |
+| `writing/desire-description.md` (311 行)       | 写作技法.csv(分类=情感)      | 保守保留原文;CSV 尚未覆盖欲念描写细节 |
+| `writing/genre-hook-payoff-library.md` (85 行) | 场景写法.csv(场景类型=钩子/兑现) | 保守保留原文;CSV 仅覆盖部分钩子/兑现条目 |
 
 
 ### 延迟(当前不纳入,条件触发后补回)
@@ -91,4 +91,3 @@
 | 言情核心场景扩展 | P1 | 继续补 `场景写法.csv` 中暧昧、误会、重逢、分手、追妻等场景 |
 | 悬疑推理技法扩展 | P1 | 继续补 `写作技法.csv` 与 `桥段套路.csv` 中线索、公平误导、真相揭露 |
 | shared md 可迁移审查 | P2 | `cool-points-guide.md`、`naming-and-voice-gaps.md` 的可条目化内容留待人工逐条录入 |
-

+ 47 - 40
webnovel-writer/references/index/reference-loading-map.md

@@ -8,43 +8,47 @@
 
 ## 直接 Read 的 md/template
 
-| Skill | 阶段 | 触发 | Reference |
-|-------|------|------|-----------|
-| webnovel-init | Step 1 | always | `skills/webnovel-init/references/system-data-flow.md` |
-| webnovel-init | Step 1 | always | `skills/webnovel-init/references/genre-tropes.md` |
-| webnovel-init | 卖点/题材采集 | always | `references/genre-profiles.md` |
-| webnovel-init | Step 2 | 用户人物扁平 | `skills/webnovel-init/references/worldbuilding/character-design.md` |
-| webnovel-init | Step 4 | always | `skills/webnovel-init/references/worldbuilding/faction-systems.md` |
-| webnovel-init | Step 4 | 涉及修仙/玄幻/高武/异能 | `skills/webnovel-init/references/worldbuilding/power-systems.md` |
-| webnovel-init | Step 4 | always | `skills/webnovel-init/references/worldbuilding/world-rules.md` |
-| webnovel-init | Step 5 | always | `skills/webnovel-init/references/creativity/creativity-constraints.md` |
-| webnovel-init | Step 5 | always | `skills/webnovel-init/references/creativity/selling-points.md` |
-| webnovel-init | Step 5 | 复合题材 | `skills/webnovel-init/references/creativity/creative-combination.md` |
-| webnovel-init | Step 5 | 卡顿 | `skills/webnovel-init/references/creativity/inspiration-collection.md` |
-| webnovel-init | Step 5 | 题材映射命中 | `skills/webnovel-init/references/creativity/anti-trope-*.md` |
-| webnovel-init | Step 6 | always | `skills/webnovel-init/references/worldbuilding/setting-consistency.md` |
-| webnovel-plan | Step 4 | always | `templates/output/大纲-卷节拍表.md` |
-| webnovel-plan | Step 5 | always | `templates/output/大纲-卷时间线.md` |
-| webnovel-plan | Step 6 | always | `references/genre-profiles.md` |
-| webnovel-plan | Step 6 | always | `references/shared/strand-weave-pattern.md` |
-| webnovel-plan | 章纲拆分 | always | `references/outlining/plot-signal-vs-spoiler.md` |
-| webnovel-plan | Step 6 | 需要爽点设计 | `references/shared/cool-points-guide.md` |
-| webnovel-plan | Step 6/7 | 需要冲突设计 | `skills/webnovel-plan/references/outlining/conflict-design.md` |
-| webnovel-plan | Step 7 | 需要追读力分析 | `references/reading-power-taxonomy.md` |
-| webnovel-plan | Step 7 | 需要章纲细化 | `skills/webnovel-plan/references/outlining/chapter-planning.md` |
-| webnovel-plan | Step 6/7 | 特定题材节奏 | `skills/webnovel-plan/references/outlining/genre-volume-pacing.md` |
-| webnovel-write | Step 4 | always | `skills/webnovel-write/references/polish-guide.md` |
-| webnovel-write | Step 4 | always | `skills/webnovel-write/references/writing/typesetting.md` |
-| webnovel-write | Step 4 | always | `skills/webnovel-write/references/style-adapter.md` |
-| webnovel-review | Step 2 | always | `references/shared/core-constraints.md` |
-| webnovel-review | Step 2 | always | `references/review-schema.md` |
-| webnovel-review | Step 2 | 审查涉及爽点或钩子分析 | `references/shared/cool-points-guide.md` |
-| webnovel-review | Step 2 | 审查涉及多线交织 | `references/shared/strand-weave-pattern.md` |
-| webnovel-review | Step 6 | blocking issue 需用户决策 | `references/review/blocking-override-guidelines.md` |
-| webnovel-query | 查询识别后 | 所有查询 | `skills/webnovel-query/references/system-data-flow.md` |
-| webnovel-query | 查询识别后 | 伏笔分析 | `skills/webnovel-query/references/advanced/foreshadowing.md` |
-| webnovel-query | 查询识别后 | 节奏分析 | `references/shared/strand-weave-pattern.md` |
-| webnovel-query | 查询识别后 | 格式查询 | `skills/webnovel-query/references/tag-specification.md` |
+> 「读取方式」:**区段** = 先 `Grep` 匹配 `^#{1,4} ` 定位真实标题锚点行号,再 `Read` offset/limit 取段(七个靶心大文件,锚点见末列);**全文** = 短文件整体读。锚点用文件里的真实标题原文(含中文顿号「、」),不是计划简写。靶心文件与锚点出处见 `docs/architecture/phase0-slimming-and-read-audit-2026-06-06.md` §A.1。
+
+| Skill | 阶段 | 触发 | Reference | 读取方式 | 区段锚点(区段读时匹配此真实标题) |
+|-------|------|------|-----------|---------|-----------|
+| webnovel-init | Step 1 | always | `skills/webnovel-init/references/system-data-flow.md` | 全文 | — |
+| webnovel-init | Step 1 | always | `skills/webnovel-init/references/genre-tropes.md` | 区段 | 当前题材段 |
+| webnovel-init | 卖点/题材采集 | always | `references/genre-profiles.md` | 区段 | 当前 genre 的单个 `### 2.x`;按需加 `## 一、Profile 字段说明` |
+| webnovel-init | Step 2 | 用户人物扁平 | `skills/webnovel-init/references/worldbuilding/character-design.md` | 全文 | — |
+| webnovel-init | Step 4 | always | `skills/webnovel-init/references/worldbuilding/faction-systems.md` | 区段 | 当前世界观所需小节 |
+| webnovel-init | Step 4 | 涉及修仙/玄幻/高武/异能 | `skills/webnovel-init/references/worldbuilding/power-systems.md` | 区段 | 力量体系对应小节 |
+| webnovel-init | Step 4 | always | `skills/webnovel-init/references/worldbuilding/world-rules.md` | 全文 | — |
+| webnovel-init | Step 5 | always | `skills/webnovel-init/references/creativity/creativity-constraints.md` | 区段 | 采集读 `## 一、创意包 Schema (Idea Package)` / `## 六、硬约束驱动创意 (Hard Constraints)` / `## 八、评分系统 (Scoring System)`;评分展示读 `### 8.1 五维评分` |
+| webnovel-init | Step 5 | always | `skills/webnovel-init/references/creativity/selling-points.md` | 区段 | `## 9. 核心卖点定位模板` 骨架;按需补 `### 1.3 核心卖点黄金公式` / `## 7. 实战检查清单` |
+| webnovel-init | Step 5 | 复合题材 | `skills/webnovel-init/references/creativity/creative-combination.md` | 区段 | 当前混搭轴对应小节 |
+| webnovel-init | Step 5 | 卡顿 | `skills/webnovel-init/references/creativity/inspiration-collection.md` | 区段 | 所需采集小节 |
+| webnovel-init | Step 5 | 题材映射命中 | `skills/webnovel-init/references/creativity/anti-trope-*.md` | 区段 | 对应反套路项 |
+| webnovel-init | Step 6 | always | `skills/webnovel-init/references/worldbuilding/setting-consistency.md` | 区段 | 一致性校验小节 |
+| webnovel-plan | Step 4 | always | `templates/output/大纲-卷节拍表.md` | 全文 | — |
+| webnovel-plan | Step 5 | always | `templates/output/大纲-卷时间线.md` | 全文 | — |
+| webnovel-plan | Step 6 | always | `references/genre-profiles.md` | 区段 | 当前 genre 的单个 `### 2.x`;按需加 `## 一、Profile 字段说明` |
+| webnovel-plan | Step 6 | always | `references/shared/strand-weave-pattern.md` | 全文 | — |
+| webnovel-plan | 章纲拆分 | always | `references/outlining/plot-signal-vs-spoiler.md` | 全文 | — |
+| webnovel-plan | Step 6 | 需要爽点设计 | `references/shared/cool-points-guide.md` | 区段 | 所需爽点维度段;题材适配取 `## 九、题材适配` |
+| webnovel-plan | Step 6/7 | 需要冲突设计 | `skills/webnovel-plan/references/outlining/conflict-design.md` | 区段 | 对应冲突类型小节 |
+| webnovel-plan | Step 7 | 需要追读力分析 | `references/reading-power-taxonomy.md` | 区段 | 按需取 `## 一、钩子类型` / `## 二、爽点模式` / `## 三、即时满足/微兑现` |
+| webnovel-plan | Step 7 | 需要章纲细化 | `skills/webnovel-plan/references/outlining/chapter-planning.md` | 区段 | `## 10. 结构化节点规范(CBN/CPNs/CEN)`;需模板时加 `## 7. 章节规划模板` |
+| webnovel-plan | Step 6/7 | 特定题材节奏 | `skills/webnovel-plan/references/outlining/genre-volume-pacing.md` | 全文 | — |
+| webnovel-write | Step 4 | always | `skills/webnovel-write/references/polish-guide.md` | 区段 | 主路径 `## 2. 执行顺序(必须按序)`;Anti-AI 终检 `## 2A. Anti-AI 检测细则` / `## Phase 1 增补:Anti-AI 规范(7层,原版)` |
+| webnovel-write | Step 4 | always | `skills/webnovel-write/references/writing/typesetting.md` | 全文 | — |
+| webnovel-write | Step 4 | always | `skills/webnovel-write/references/style-adapter.md` | 全文 | — |
+| webnovel-review | Step 2 | always | `references/shared/core-constraints.md` | 全文 | — |
+| webnovel-review | Step 2 | always | `references/review-schema.md` | 全文 | — |
+| webnovel-review | Step 2 | 审查涉及爽点或钩子分析 | `references/shared/cool-points-guide.md` | 区段 | 所需爽点维度段;题材适配取 `## 九、题材适配` |
+| webnovel-review | Step 2 | 审查涉及多线交织 | `references/shared/strand-weave-pattern.md` | 全文 | — |
+| webnovel-review | Step 6 | blocking issue 需用户决策 | `references/review/blocking-override-guidelines.md` | 全文 | — |
+| webnovel-query | 查询识别后 | 所有查询 | `skills/webnovel-query/references/system-data-flow.md` | 区段 | 按查询类型取数据源优先级小节 |
+| webnovel-query | 查询识别后 | 伏笔分析 | `skills/webnovel-query/references/advanced/foreshadowing.md` | 全文 | — |
+| webnovel-query | 查询识别后 | 节奏分析 | `references/shared/strand-weave-pattern.md` | 全文 | — |
+| webnovel-query | 查询识别后 | 格式查询 | `skills/webnovel-query/references/tag-specification.md` | 全文 | — |
+
+> 七个靶心大文件(`references/genre-profiles.md`、`skills/webnovel-init/references/creativity/selling-points.md`、`references/reading-power-taxonomy.md`、`skills/webnovel-plan/references/outlining/chapter-planning.md`、`skills/webnovel-init/references/creativity/creativity-constraints.md`、`skills/webnovel-write/references/polish-guide.md`、`references/shared/cool-points-guide.md`)一律区段读,避免 init/plan/write 每跑全量吞入;短文件维持全文读。
 
 ## CSV 检索:直接调用 `reference_search.py`
 
@@ -94,8 +98,11 @@
 | 文件 | 现状 |
 |------|------|
 | `skills/webnovel-write/references/style-variants.md` | 未在当前 write 流程中直接加载 |
-| `skills/webnovel-write/references/writing/combat-scenes.md` | 由 CSV `场景写法` 承担战斗触发,不直接 Read |
-| `skills/webnovel-write/references/writing/dialogue-writing.md` | 由 CSV `写作技法` 承担对话触发,不直接 Read |
-| `skills/webnovel-write/references/writing/emotion-psychology.md` | 由 CSV `写作技法` 承担情感触发,不直接 Read |
+| `skills/webnovel-write/references/writing/combat-scenes.md` | 已 stub 化,正文迁至 CSV `场景写法` |
+| `skills/webnovel-write/references/writing/dialogue-writing.md` | 已 stub 化,正文迁至 CSV `写作技法` |
+| `skills/webnovel-write/references/writing/emotion-psychology.md` | 已 stub 化,正文迁至 CSV `写作技法` |
+| `skills/webnovel-write/references/writing/scene-description.md` | 已 stub 化,正文迁至 CSV `写作技法` |
+| `skills/webnovel-write/references/writing/desire-description.md` | 保守保留原文;CSV 尚未覆盖欲念描写细节,当前未被 SKILL.md 直接加载 |
+| `skills/webnovel-write/references/writing/genre-hook-payoff-library.md` | 保守保留原文;CSV 仅覆盖部分钩子/兑现条目,当前未被 SKILL.md 直接加载 |
 | `skills/webnovel-review/references/common-mistakes.md` | 未在当前 review 流程中直接加载 |
 | `skills/webnovel-review/references/pacing-control.md` | 未在当前 review 流程中直接加载 |

+ 34 - 0
webnovel-writer/scripts/data_modules/tests/test_artifact_validator.py

@@ -159,3 +159,37 @@ def test_validate_chapter_commit_reports_projection_failure(tmp_path):
 
     assert report["ok"] is False
     assert any(item["type"] == ERROR_PROJECTION_FAILURE for item in report["errors"])
+
+
+def test_artifact_validator_rejects_missing_required_top_level_fields(tmp_path):
+    """precommit 负向用例:缺关键顶层字段时 runtime validator 必须拦截。
+
+    取代已退役的 test_webnovel_write_data_agent_prompt_requires_extraction_schema
+    (plan §12.2):字段保障由 runtime schema 强制,而非主 Skill 文案锚定。
+    """
+    # fulfillment_result 缺 missed_nodes
+    fulfillment = _write_json(
+        tmp_path / "fulfillment_result.json",
+        {"planned_nodes": [], "covered_nodes": [], "extra_nodes": []},
+    )
+    report = validate_fulfillment_result(fulfillment)
+    assert report["ok"] is False
+    assert report["errors"][0]["type"] == ERROR_SCHEMA
+    assert "missed_nodes" in report["errors"][0]["message"]
+
+    # disambiguation_result 缺 pending
+    disambiguation = _write_json(tmp_path / "disambiguation_result.json", {})
+    report = validate_disambiguation_result(disambiguation)
+    assert report["ok"] is False
+    assert report["errors"][0]["type"] == ERROR_SCHEMA
+    assert "pending" in report["errors"][0]["message"]
+
+    # extraction_result 缺核心字段 accepted_events
+    extraction = _write_json(
+        tmp_path / "extraction_result.json",
+        {"state_deltas": [], "entity_deltas": []},
+    )
+    report = validate_extraction_result(extraction)
+    assert report["ok"] is False
+    assert report["errors"][0]["type"] == ERROR_SCHEMA
+    assert "accepted_events" in report["errors"][0]["message"]

+ 192 - 22
webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py

@@ -110,7 +110,7 @@ def test_skill_frontmatter_complete(skill_file: Path):
 
 
 # ---------------------------------------------------------------------------
-# 2. Agent 模板结构(9 段)
+# 2. Agent 模板结构(≥4 段)
 # ---------------------------------------------------------------------------
 
 EXPECTED_AGENT_SECTIONS = [
@@ -118,16 +118,12 @@ EXPECTED_AGENT_SECTIONS = [
     "2.",
     "3.",
     "4.",
-    "5.",
-    "6.",
-    "7.",
-    "8.",
 ]
 
 
 @pytest.mark.parametrize("agent_file", AGENT_FILES, ids=lambda f: f.name)
 def test_agent_template_structure(agent_file: Path):
-    """每个 agent 至少包含 8 个编号段。"""
+    """每个 agent 至少包含 4 个编号段(§12.2 松绑:不强制 8 段,避免为过测试留空段)。"""
     text = _read_text(agent_file)
     missing = []
     for section in EXPECTED_AGENT_SECTIONS:
@@ -241,8 +237,8 @@ def test_webnovel_review_skill_uses_unified_reviewer_pipeline():
     skill_text = _read_text(SKILLS_DIR / "webnovel-review" / "SKILL.md")
 
     assert "`reviewer`" in skill_text
-    assert "Agent(" in skill_text
-    assert 'subagent_type: "webnovel-writer:reviewer"' in skill_text
+    assert "Use the Agent tool to run `webnovel-writer:reviewer`" in skill_text
+    assert "subagent_type:" not in skill_text
     assert "review-pipeline" in skill_text
     assert ".webnovel/tmp/review_results.json" in skill_text
     assert ".webnovel/tmp/review_metrics.json" in skill_text
@@ -272,14 +268,14 @@ def test_active_skills_use_agent_tool_name_not_legacy_task():
 
 
 def test_webnovel_write_skill_uses_explicit_agent_invocation_templates():
-    """webnovel-write 的关键 subagent 必须用显式 Agent(subagent_type=...) 调用模板。"""
+    """关键 subagent 必须经 Agent 工具按注册名 webnovel-writer:X 显式调用;不再用伪函数 subagent_type 块(plan §4.4.2/§8.4)。"""
     text = _read_text(SKILLS_DIR / "webnovel-write" / "SKILL.md")
     fm = _extract_frontmatter(text)
 
     assert "Agent" in fm.get("allowed-tools", "")
     for subagent in ("context-agent", "reviewer", "data-agent"):
-        assert f'subagent_type: "webnovel-writer:{subagent}"' in text
-        assert f'subagent_type: "{subagent}"' not in text
+        assert f"webnovel-writer:{subagent}" in text, f"缺少 {subagent} 的注册名显式调用"
+    assert "subagent_type:" not in text, "不应再使用伪函数 subagent_type 调用块"
     assert "不得用主流程口头代替 subagent 输出" in text
 
 
@@ -345,18 +341,16 @@ def test_data_agent_is_described_as_extraction_only_not_direct_write_mainline():
     assert "event_type" in text
     assert "subject" in text
     assert "直接写入 index.db 和 state.json" not in text
+    # data-agent 不得携带可运行的 chapter-commit 命令(commit 是主流程的事实提交入口,data-agent 只产 artifact)
+    assert not re.search(r"webnovel\.py[^\n]+chapter-commit", text), (
+        "data-agent.md 不应出现可运行的 webnovel.py ... chapter-commit 命令"
+    )
 
 
-def test_webnovel_write_data_agent_prompt_requires_extraction_schema():
-    text = (SKILLS_DIR / "webnovel-write" / "SKILL.md").read_text(encoding="utf-8")
-    assert "webnovel-writer:data-agent" in text
-    assert "fulfillment_result.json 必须顶层" in text
-    assert "planned_nodes/covered_nodes/missed_nodes/extra_nodes" in text
-    assert "disambiguation_result.json 必须顶层包含 pending" in text
-    assert "extraction_result.json 必须严格" in text
-    assert "accepted_events/state_deltas/entity_deltas" in text
-    assert "禁止包在 chapter/fulfillment/disambiguation/extraction" in text
-    assert "event_id/chapter/event_type/subject/payload" in text
+# (已按 plan §12.2 退役) test_webnovel_write_data_agent_prompt_requires_extraction_schema:
+# 该测试逐字要求主 Skill 写出 data artifact 的 schema 字段名,与判据一冲突。schema 字段保障已迁到
+# data-agent.md 生产方(test_data_agent_is_described_as_extraction_only_not_direct_write_mainline)
+# + precommit 负向用例(Task 7)。主 Skill 不再内联长 schema。
 
 
 def test_dashboard_and_plan_skills_surface_story_runtime_mainline():
@@ -460,7 +454,8 @@ def test_webnovel_init_deconstruction_wiring_keeps_confirmation_gate():
     """init may consume only confirmed, transformed reference patterns."""
     text = _read_text(SKILLS_DIR / "webnovel-init" / "SKILL.md")
 
-    assert 'subagent_type: "webnovel-writer:deconstruction-agent"' in text
+    assert "Use the Agent tool to run `webnovel-writer:deconstruction-agent`" in text
+    assert "subagent_type:" not in text
     assert "Step 1.5:灵感来源询问" in text
     assert "进入故事核采集前" in text
     assert "不要默认拆书" in text
@@ -501,3 +496,178 @@ def test_webnovel_init_deconstruction_wiring_keeps_confirmation_gate():
     assert "用户确认前" in text
     assert "Step 2-6 只能使用用户确认过、并已变形为本书差异化表达的模式" in text
     assert "汇总 Step 1.5 已确认的灵感来源" in text
+
+
+# ---------------------------------------------------------------------------
+# 7. A 类跨层红线:行为/契约级断言(Phase 0 守护)
+#    这些断言守护「已实现」的业务红线,全部应为绿。优先断言结构不变量
+#    (命令存在/顺序、节点 schema、变量化的真实参数),不做脆弱的文案匹配。
+# ---------------------------------------------------------------------------
+
+# A 类红线 2:placeholder-scan 必须出现在 plan 与 write 两层的关键节点。
+def test_placeholder_scan_runs_in_both_plan_and_write_skills():
+    """红线 2:plan 与 write 都必须显式调用 placeholder-scan CLI。"""
+    plan_text = _read_text(SKILLS_DIR / "webnovel-plan" / "SKILL.md")
+    write_text = _read_text(SKILLS_DIR / "webnovel-write" / "SKILL.md")
+    for name, text in (("webnovel-plan", plan_text), ("webnovel-write", write_text)):
+        cmds = _extract_cli_subcommands(text)
+        assert "placeholder-scan" in cmds, (
+            f"{name}: 关键节点缺少 placeholder-scan CLI 调用"
+        )
+
+
+# A 类红线 3:story-system 章级刷新必须传入真实 CHAPTER_GOAL 变量,
+# 不得把 {章纲目标} / 第N章章纲目标 这类占位文本当作 positional query。
+@pytest.mark.parametrize("skill_name", ["webnovel-plan", "webnovel-write"])
+def test_story_system_chapter_refresh_uses_real_goal_not_placeholder_query(skill_name: str):
+    """红线 3:story-system 的 query 实参是 ${CHAPTER_GOAL} 变量,且禁占位文本写在命令里。"""
+    text = _read_text(SKILLS_DIR / skill_name / "SKILL.md")
+    # 命令必须用变量化的真实目标作为 query 实参
+    assert 'story-system "${CHAPTER_GOAL}"' in text, (
+        f"{skill_name}: story-system 未使用真实 ${{CHAPTER_GOAL}} 作为 query 实参"
+    )
+    # 占位 query 绝不能作为 story-system 的 positional 实参出现
+    for placeholder in ("{章纲目标}", "第N章章纲目标"):
+        assert f'story-system "{placeholder}"' not in text, (
+            f"{skill_name}: story-system 不得把占位文本 {placeholder} 当作 query"
+        )
+    # 必须显式声明「禁止占位 query」这一约束(断言事实存在,不锁具体措辞)
+    assert "{章纲目标}" in text and "第N章章纲目标" in text, (
+        f"{skill_name}: 缺少对占位 query 的明确禁止说明"
+    )
+
+
+# A 类红线 4:story-system 章级刷新必须 --persist 且 --emit-runtime-contracts。
+@pytest.mark.parametrize("skill_name", ["webnovel-plan", "webnovel-write"])
+def test_story_system_chapter_refresh_persists_runtime_contracts(skill_name: str):
+    """红线 4:章级 story-system 刷新必须同时 --persist 与 --emit-runtime-contracts。"""
+    text = _read_text(SKILLS_DIR / skill_name / "SKILL.md")
+    cmd_start = text.find('story-system "${CHAPTER_GOAL}"')
+    assert cmd_start >= 0, f"{skill_name}: 缺少章级 story-system 调用"
+    # 取该调用所在的命令行(到下一空行/段落结束),断言两个关键开关都在
+    cmd_tail = text[cmd_start:cmd_start + 400]
+    assert "--persist" in cmd_tail, f"{skill_name}: 章级 story-system 缺少 --persist"
+    assert "--emit-runtime-contracts" in cmd_tail, (
+        f"{skill_name}: 章级 story-system 缺少 --emit-runtime-contracts"
+    )
+    assert "--chapter" in cmd_tail, f"{skill_name}: 章级 story-system 缺少 --chapter"
+
+
+# A 类红线 5:write-gate 三道闸门必须齐全且顺序为 prewrite→precommit→postcommit。
+def test_write_skill_gate_stages_ordered_prewrite_precommit_postcommit():
+    """红线 5:write-gate 三道 gate 顺序不可乱。"""
+    text = _read_text(SKILLS_DIR / "webnovel-write" / "SKILL.md")
+    prewrite = text.find("write-gate --chapter {chapter_num} --stage prewrite")
+    precommit = text.find("write-gate --chapter {chapter_num} --stage precommit")
+    postcommit = text.find("write-gate --chapter {chapter_num} --stage postcommit")
+    assert prewrite >= 0, "缺少 prewrite gate"
+    assert precommit >= 0, "缺少 precommit gate"
+    assert postcommit >= 0, "缺少 postcommit gate"
+    assert prewrite < precommit < postcommit, (
+        "write-gate 三道 gate 顺序必须为 prewrite→precommit→postcommit"
+    )
+
+
+# A 类红线 7:reviewer 原始 JSON 必须经 review-pipeline --save-metrics 落库(write 与 review 两层)。
+@pytest.mark.parametrize("skill_name", ["webnovel-write", "webnovel-review"])
+def test_review_pipeline_persists_metrics_in_review_chain(skill_name: str):
+    """红线 7:reviewer JSON 经 review-pipeline --save-metrics 落库。"""
+    text = _read_text(SKILLS_DIR / skill_name / "SKILL.md")
+    cmds = _extract_cli_subcommands(text)
+    assert "review-pipeline" in cmds, f"{skill_name}: 缺少 review-pipeline CLI 调用"
+    assert "--save-metrics" in text, f"{skill_name}: review-pipeline 未带 --save-metrics 落库"
+
+
+# A 类红线 10:postcommit 必须验证 projection 五项;失败只 projections retry。
+def test_write_skill_postcommit_verifies_five_projections_and_retry_only():
+    """红线 10:projection 五项(state/index/summary/memory/vector)验证,失败只 retry。"""
+    text = _read_text(SKILLS_DIR / "webnovel-write" / "SKILL.md")
+    assert "state/index/summary/memory/vector" in text, (
+        "缺少 projection 五项(state/index/summary/memory/vector)验证说明"
+    )
+    # 失败兜底唯一手段是 projections retry(命令以续行书写,直接断言字面调用)
+    assert "projections retry --chapter {chapter_num}" in text, (
+        "projection 失败兜底必须是 projections retry --chapter {chapter_num}"
+    )
+
+
+# A 类红线 12:plan 必须覆盖节拍表/时间线/结构化章纲节点/结构化总纲写回/状态更新。
+def test_plan_skill_covers_outline_writeback_and_state_sync_contract():
+    """红线 12:plan 的节拍表/时间线/章纲节点/总纲写回 JSON/master-outline-sync/update-state。"""
+    text = _read_text(SKILLS_DIR / "webnovel-plan" / "SKILL.md")
+    # 节拍表 / 时间线 输出物
+    assert "大纲/第{volume_id}卷-节拍表.md" in text
+    assert "大纲/第{volume_id}卷-时间线.md" in text
+    # 结构化章纲节点
+    for node in ("CBN", "CPNs", "CEN", "必须覆盖节点", "本章禁区"):
+        assert node in text, f"plan 缺少结构化章纲节点标记 {node}"
+    # 结构化总纲写回文件(不可从自由文本推断伏笔)
+    assert "大纲/第{volume_id}卷-总纲写回.json" in text
+    # 设定写回 + 状态同步命令
+    cmds = _extract_cli_subcommands(text)
+    assert "master-outline-sync" in cmds, "plan 缺少 master-outline-sync 写回命令"
+    assert "update-state" in cmds, "plan 缺少 update-state 状态更新命令"
+
+
+# ---------------------------------------------------------------------------
+# 8. B 类跨层新契约(plan §5.2-B / §4.5 写入所有权矩阵)
+#    tools↔落盘一致性现状已满足 → 作通过型守护;
+#    提交前只读 git diff 变更面校验现状缺失 → xfail,Task 5(Phase 1)落地后移除标记转正。
+# ---------------------------------------------------------------------------
+
+def _agent_tools(agent_name: str) -> list[str]:
+    """解析某 agent frontmatter 的 tools 列表。"""
+    fm = _extract_frontmatter(_read_text(AGENTS_DIR / f"{agent_name}.md"))
+    return [t.strip() for t in fm.get("tools", "").split(",") if t.strip()]
+
+
+# B 类红线(写入所有权 ↔ tools 一致,单一写入者):
+# data-agent 是三份 tmp artifact 的唯一写入者 → 必须持 Write;
+# reviewer/context-agent/deconstruction-agent 只返回结果、由主流程落盘 → 不得持 Write。
+def test_agent_write_ownership_matches_tools_frontmatter():
+    """红线(写入所有权):仅 data-agent 持 Write,其余三个 agent 不持 Write。"""
+    assert "Write" in _agent_tools("data-agent"), (
+        "data-agent 必须持有 Write(它是三份 tmp artifact 的唯一写入者)"
+    )
+    for agent_name in ("reviewer", "context-agent", "deconstruction-agent"):
+        assert "Write" not in _agent_tools(agent_name), (
+            f"{agent_name} 不得持有 Write(它只返回结果,由主流程落盘)"
+        )
+
+
+# B 类红线(提交前变更面校验):write SKILL 在 chapter-commit 前必须执行只读 git diff 变更面校验。
+# 现状 write SKILL 尚无此步 → 标 xfail;Task 5(Phase 1)实现后移除本标记,转为硬守护。
+# B 类红线(提交前变更面校验):write SKILL 在 chapter-commit 前必须执行只读 git diff 变更面校验。
+# Phase 1 (Task 5) 已落地 → 转为硬守护(移除 xfail 标记)。
+def test_write_skill_has_readonly_git_diff_change_surface_check():
+    """红线(提交前变更面校验):write SKILL 在 chapter-commit 前执行只读 git diff 校验。"""
+    text = _read_text(SKILLS_DIR / "webnovel-write" / "SKILL.md")
+    assert "diff --name-status" in text, (
+        "write SKILL 缺少提交前只读 git diff --name-status 变更面校验"
+    )
+    assert "diff --check" in text, (
+        "write SKILL 缺少 git diff --check 空白/冲突标记校验"
+    )
+
+
+# B 类红线(写入所有权·prompt 层):write/review 必须在文本层声明所有权,
+# 与 frontmatter(test_agent_write_ownership_matches_tools_frontmatter)+ behavior eval(artifact_ownership)三处互守。
+def test_write_review_skills_state_artifact_ownership():
+    """reviewer 返回 JSON、主流程落盘 review_results.json、data-agent 唯一写入者。"""
+    write_text = _read_text(SKILLS_DIR / "webnovel-write" / "SKILL.md")
+    review_text = _read_text(SKILLS_DIR / "webnovel-review" / "SKILL.md")
+    for name, text in (("webnovel-write", write_text), ("webnovel-review", review_text)):
+        assert "主流程" in text and ".webnovel/tmp/review_results.json" in text, (
+            f"{name}: 缺 reviewer→主流程落盘 review_results.json 的所有权说明"
+        )
+    assert "唯一写入者" in write_text, "webnovel-write 缺 data-agent 唯一写入者说明"
+    assert "主流程只检查文件存在与 schema" in write_text
+    assert "不直接写 state/index/summaries/memory/vectors/projection" in write_text
+
+
+# §9.3/§12.3:reviewer 删除 ReAct/思维链 元叙述后的正向守护(审查只给输出合同,不教它怎么想)。
+def test_reviewer_has_no_react_meta_narrative():
+    """reviewer.md 不得保留 ReAct/思维链 元叙述。"""
+    text = _read_text(AGENTS_DIR / "reviewer.md")
+    assert "ReAct" not in text, "reviewer 不应出现 ReAct 字样"
+    assert "思维链" not in text, "reviewer 不应保留思维链元叙述"

+ 48 - 0
webnovel-writer/scripts/data_modules/tests/test_write_gates.py

@@ -82,6 +82,54 @@ def test_precommit_gate_accepts_valid_artifacts(tmp_path):
     assert report["details"]["artifact_report"]["ok"] is True
 
 
+def test_precommit_gate_rejects_fulfillment_missing_missed_nodes(tmp_path):
+    _make_init_ready(tmp_path)
+    _make_contracts(tmp_path, chapter=1)
+    (tmp_path / "正文" / "第0001章.md").write_text("正文\n", encoding="utf-8")
+    _write_valid_artifacts(tmp_path)
+    _write_json(
+        tmp_path / ".webnovel" / "tmp" / "fulfillment_result.json",
+        {"planned_nodes": [], "covered_nodes": [], "extra_nodes": []},
+    )
+
+    report = run_write_gate(tmp_path, chapter=1, stage="precommit")
+
+    assert report["ok"] is False
+    assert any(item["code"] == "artifact.schema_error" for item in report["errors"])
+    assert any("missed_nodes" in item["message"] for item in report["errors"])
+
+
+def test_precommit_gate_rejects_disambiguation_missing_pending(tmp_path):
+    _make_init_ready(tmp_path)
+    _make_contracts(tmp_path, chapter=1)
+    (tmp_path / "正文" / "第0001章.md").write_text("正文\n", encoding="utf-8")
+    _write_valid_artifacts(tmp_path)
+    _write_json(tmp_path / ".webnovel" / "tmp" / "disambiguation_result.json", {"warnings": []})
+
+    report = run_write_gate(tmp_path, chapter=1, stage="precommit")
+
+    assert report["ok"] is False
+    assert any(item["code"] == "artifact.schema_error" for item in report["errors"])
+    assert any("pending" in item["message"] for item in report["errors"])
+
+
+def test_precommit_gate_rejects_extraction_missing_accepted_events(tmp_path):
+    _make_init_ready(tmp_path)
+    _make_contracts(tmp_path, chapter=1)
+    (tmp_path / "正文" / "第0001章.md").write_text("正文\n", encoding="utf-8")
+    _write_valid_artifacts(tmp_path)
+    _write_json(
+        tmp_path / ".webnovel" / "tmp" / "extraction_result.json",
+        {"state_deltas": [], "entity_deltas": [], "summary_text": "摘要"},
+    )
+
+    report = run_write_gate(tmp_path, chapter=1, stage="precommit")
+
+    assert report["ok"] is False
+    assert any(item["code"] == "artifact.schema_error" for item in report["errors"])
+    assert any("accepted_events" in item["message"] for item in report["errors"])
+
+
 def test_precommit_gate_blocks_projection_failed_phase(tmp_path):
     _make_init_ready(tmp_path)
     _make_contracts(tmp_path, chapter=1)

+ 30 - 0
webnovel-writer/scripts/run_behavior_evals.py

@@ -152,6 +152,35 @@ def _eval_data_agent_boundary(root: Path, case: dict[str, Any]) -> dict[str, Any
     )
 
 
+def _eval_artifact_ownership(root: Path, case: dict[str, Any]) -> dict[str, Any]:
+    plugin_root = _plugin_root(root)
+    write_text = _read(plugin_root / "skills" / "webnovel-write" / "SKILL.md")
+    review_text = _read(plugin_root / "skills" / "webnovel-review" / "SKILL.md")
+    reviewer_tools = _frontmatter(_read(plugin_root / "agents" / "reviewer.md")).get("tools", "")
+    data_tools = _frontmatter(_read(plugin_root / "agents" / "data-agent.md")).get("tools", "")
+    missing: list[str] = []
+    if "Write" in reviewer_tools:
+        missing.append("reviewer 不应持 Write(review_results.json 由主流程落盘)")
+    if "Write" not in data_tools:
+        missing.append("data-agent 应持 Write(它是 tmp artifact 的唯一写入者)")
+    for text, owner in ((write_text, "webnovel-write"), (review_text, "webnovel-review")):
+        if "主流程" not in text or ".webnovel/tmp/review_results.json" not in text:
+            missing.append(f"{owner}: 缺 reviewer→主流程落盘 review_results.json 的所有权说明")
+    for item in (
+        "唯一写入者",
+        "主流程只检查文件存在与 schema",
+        "不直接写 state/index/summaries/memory/vectors/projection",
+    ):
+        if item not in write_text:
+            missing.append(f"webnovel-write 缺写入所有权红线:{item}")
+    return _result(
+        case,
+        passed=not missing,
+        reason="artifact ownership matches tools and prompts" if not missing else "artifact ownership drifted",
+        evidence=missing or ["reviewer→主流程 review_results.json;data-agent→tmp artifacts"],
+    )
+
+
 def _eval_commit_projection_runtime(root: Path, case: dict[str, Any]) -> dict[str, Any]:
     scripts_dir = _plugin_root(root) / "scripts"
     if str(scripts_dir) not in sys.path:
@@ -206,6 +235,7 @@ EVALUATORS = {
     "skill_contract": _eval_skill_contract,
     "write_blocking_gate": _eval_write_blocking_gate,
     "data_agent_boundary": _eval_data_agent_boundary,
+    "artifact_ownership": _eval_artifact_ownership,
     "commit_projection_runtime": _eval_commit_projection_runtime,
     "dashboard_read_only": _eval_dashboard_read_only,
 }

+ 19 - 32
webnovel-writer/skills/webnovel-dashboard/SKILL.md

@@ -8,10 +8,9 @@ allowed-tools: Bash Read
 
 ## 目标
 
-- 在本地启动只读 Web 面板。
-- 实时查看创作进度、设定词典、关系图谱、章节内容与追读力数据。
-- 显式查看 Story Runtime 主链状态,包括 `story-runtime/health`、latest commit 与 fallback 情况。
-- 允许监听 `.webnovel/` 变化,但不得修改项目内容。
+- 在本地启动只读 Web 面板,查看创作进度、设定词典、关系图谱、章节内容与追读力数据。
+- 暴露 Story Runtime 主链状态:`/api/story-runtime/health`、latest commit、fallback 情况。
+- 可监听 `.webnovel/` 变化,但不修改任何项目文件。
 
 ## 执行流程
 
@@ -26,22 +25,19 @@ if [ -z "${CLAUDE_PLUGIN_ROOT}" ] || [ ! -d "${CLAUDE_PLUGIN_ROOT}/dashboard" ];
 fi
 
 export DASHBOARD_DIR="${CLAUDE_PLUGIN_ROOT}/dashboard"
+export SCRIPTS_DIR="${CLAUDE_PLUGIN_ROOT}/scripts"
 ```
 
-### Step 2:安装依赖并解析项目根目录
+### Step 2:解析项目根目录
 
 ```bash
-python -m pip install -r "${DASHBOARD_DIR}/requirements.txt" --quiet
-export SCRIPTS_DIR="${CLAUDE_PLUGIN_ROOT}/scripts"
 export PROJECT_ROOT="$(python "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" where)"
 echo "项目路径: ${PROJECT_ROOT}"
 ```
 
-补充要求:
-- `PROJECT_ROOT` 必须解析成功
-- 若依赖已安装,可重复执行,不视为错误
+`PROJECT_ROOT` 必须解析成功。
 
-### Step 3:准备 Python 模块路径并校验前端产物
+### Step 3:校验前端产物与依赖
 
 ```bash
 if [ -n "${PYTHONPATH:-}" ]; then
@@ -51,51 +47,42 @@ else
 fi
 
 if [ ! -f "${DASHBOARD_DIR}/frontend/dist/index.html" ]; then
-  echo "ERROR: 缺少前端构建产物 ${DASHBOARD_DIR}/frontend/dist/index.html" >&2
+  echo "ERROR: 缺少前端构建产物 ${DASHBOARD_DIR}/frontend/dist/index.html(dist 应随插件打包,确认插件完整安装)" >&2
   exit 1
 fi
 ```
 
-### Step 4:启动 Dashboard
+不默认安装依赖。仅当 Step 4 因缺依赖启动失败时,提示用户手动执行:
 
 ```bash
-python -m dashboard.server --project-root "${PROJECT_ROOT}"
+python -m pip install -r "${DASHBOARD_DIR}/requirements.txt"
 ```
 
-如不需要自动打开浏览器:
+### Step 4:启动 Dashboard
 
 ```bash
-python -m dashboard.server --project-root "${PROJECT_ROOT}" --no-browser
+python -m dashboard.server --project-root "${PROJECT_ROOT}"
 ```
 
-启动后优先确认以下接口可用:
-- `/api/story-runtime/health`
-- `/api/preflight`
-
-## 注意事项
+不自动打开浏览器时加 `--no-browser`;自定义端口加 `--port 9000`。
 
-- Dashboard 为纯只读面板,不提供修改接口。
-- 文件读取必须限制在 `PROJECT_ROOT` 范围内。
-- 如需自定义端口,使用 `--port 9000`。
+启动后优先确认接口可用:`/api/story-runtime/health`、`/api/preflight`。
 
 ## 成功标准
 
-- Dashboard 进程已启动且输出了可访问的 URL
-- 浏览器可正常打开页面(或 `--no-browser` 模式下 URL 可手动访问)
-- 页面显示项目数据(章节列表、实体图谱等)
+- Dashboard 进程已启动并输出可访问 URL;页面显示项目数据(章节列表、实体图谱等)。
 
 ## 失败恢复
 
 | 故障 | 恢复方式 |
 |------|---------|
-| 依赖安装失败 | 检查 Python 版本和网络,手动 `pip install -r requirements.txt` |
+| 启动报缺依赖 | 手动 `pip install -r "${DASHBOARD_DIR}/requirements.txt"`,检查 Python 版本与网络 |
 | 前端 `dist/` 缺失 | 确认插件完整安装,dist 应随插件打包 |
 | 项目根解析失败 | 检查 `.webnovel/state.json` 是否存在,确认 `WORKSPACE_ROOT` 正确 |
-| 端口占用 | 使用 `--port <其他端口>` 或关闭占用进程 |
+| 端口占用 | 用 `--port <其他端口>` 或关闭占用进程 |
 | 页面空白/数据缺失 | 确认 `.webnovel/` 下有 state.json、index.db 等数据文件 |
 
 ## 安全边界
 
-- 只读操作,不修改任何项目文件
-- 文件访问限制在 `PROJECT_ROOT` 范围内
-- 不暴露外部网络(默认 localhost)
+- 纯只读面板,不提供修改接口,不修改任何项目文件。
+- 文件访问限制在 `PROJECT_ROOT` 范围内,默认仅监听 localhost。

+ 9 - 30
webnovel-writer/skills/webnovel-doctor/SKILL.md

@@ -1,6 +1,6 @@
 ---
 name: webnovel-doctor
-description: This skill should be used when the user asks to "/webnovel-doctor", "检查项目环境", "体检网文项目", "排查 RAG 配置", "检查缺失文件", "项目状态不对", or needs a read-only diagnosis of webnovel-writer project files, databases, dependencies, and runtime configuration.
+description: 对网文项目做只读体检/诊断(/webnovel-doctor)——检查目录、文件、JSON、SQLite、RAG 配置、依赖与 Dashboard 构建产物是否完整。
 version: 0.1.0
 allowed-tools: Read Bash
 argument-hint: "[--chapter N] [--deep]"
@@ -10,15 +10,14 @@ argument-hint: "[--chapter N] [--deep]"
 
 ## 目标
 
-运行只读项目体检,确认当前书项目在所处阶段应该具备的目录、文件、JSON、SQLite、RAG 配置、Python 依赖和 Dashboard 构建产物是否完整。
+只读诊断当前书项目:确认所处阶段应有的目录、文件、JSON、SQLite、RAG 配置、Python 依赖与 Dashboard 构建产物是否完整。
 
 ## 原则
 
-1. 只读诊断,不写入项目文件,不自动修复,不安装依赖,不启动 Dashboard。
-2. 先运行 `project-status` 获取短状态,再运行 `doctor` 获取详细检查。
-3. 使用 `python -X utf8`,避免 Windows 中文路径和中文文件名编码问题。
-4. 保留旧 `status` 命令语义;需要短状态时使用 `project-status`,需要宏观创作健康报告时才使用 `status`。
-5. 根据 doctor 输出说明影响和修复建议;缺失项不要自行猜测为终态要求,阶段由 runtime 推导。
+1. 只读诊断:不写项目文件、不自动修复、不安装依赖、不启动 Dashboard。
+2. 先 `project-status` 取短状态,再 `doctor` 做阶段感知检查。
+3. 统一用 `python -X utf8`,避免中文路径编码问题。
+4. 缺失项按 runtime 推导的阶段解释影响与修复建议,不把 init 刚结束的项目按已写多章项目检查。
 
 ## 执行
 
@@ -41,30 +40,10 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" p
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" doctor --format text
 ```
 
-指定章节:
-
-```bash
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" doctor --chapter {chapter_num} --format text
-```
-
-深度体检:
-
-```bash
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" doctor --deep --format text
-```
+指定章节加 `--chapter {chapter_num}`,深度体检加 `--deep`。
 
 ## 输出方式
 
-汇报时包含:
-
-- 当前 `phase` 和 `target_chapter`。
-- 是否有 blocker。
-- 缺失或异常文件的路径。
-- RAG / Python / Dashboard 配置是否缺失。
-- 每个问题的影响和建议修复动作。
-
-避免输出:
+汇报包含:当前 `phase` 与 `target_chapter`、是否有 blocker、缺失或异常文件路径、RAG / Python / Dashboard 配置是否缺失、每个问题的影响和建议修复动作。
 
-- 不执行真实修复。
-- 不展示或要求用户粘贴 API key。
-- 不把 init 刚结束的项目按已写多章项目检查。
+不执行真实修复,不展示或要求粘贴 API key。

+ 77 - 251
webnovel-writer/skills/webnovel-init/SKILL.md

@@ -9,64 +9,38 @@ argument-hint: "[书名或灵感(可选)]"
 
 ## 目标
 
-- 通过结构化交互收集足够信息,避免"先生成再返工"。
-- 产出可落地项目骨架:`.webnovel/state.json`、`设定集/*`、`大纲/总纲.md`、`.webnovel/idea_bank.json`。
+- 结构化交互收集足够信息,避免"先生成再返工"。
+- 产出可落地骨架:`.webnovel/state.json`、`设定集/*`、`大纲/总纲.md`、`.webnovel/idea_bank.json`、`.story-system/MASTER_SETTING.json`。
 - 保证后续 `/webnovel-plan` 与 `/webnovel-write` 可直接运行。
 
 ## 执行原则
 
 1. 先收集,再生成;未过充分性闸门,不执行 `webnovel.py init`。
-2. 分波次提问,每轮只问"当前缺失且会阻塞下一步"的信息。
-3. 允许调用 `Read/Grep/Bash/Agent/AskUserQuestion/WebSearch/WebFetch` 辅助收集。
-4. 用户已明确的信息不重复问;冲突信息优先让用户裁决。
-5. Deep 模式优先完整性,允许慢一点,但禁止漏关键字段。
-6. 参考书拆解只返回结构化结果给 init 主流程;用户确认前不得写入 `idea_bank.json`、`.story-system`、`设定集`、`大纲`、`正文`、`.webnovel/state.json` 或任何 canon/read model 文件。
+2. 分波次提问,每轮只问"当前缺失且会阻塞下一步"的信息;用户已明确的不重复问,冲突让用户裁决。
+3. 参考书拆解只返回结构化结果;用户确认前不得写入 `idea_bank.json`、`.story-system`、`设定集`、`大纲`、`正文`、`.webnovel/state.json` 或任何 canon/read model 文件。
 
 ## 引用加载策略
 
-路径说明:`references/` 指 skill 私有 `skills/webnovel-init/references/`;`../../references/` 指共享 references。
-
-### md 必读
-
-| Step | Trigger | Reference | 实际路径 |
-|------|---------|-----------|---------|
-| Step 1 | always | 数据流规范 | `${SKILL_ROOT}/references/system-data-flow.md` |
-| Step 1 | always | 题材套路库 | `${SKILL_ROOT}/references/genre-tropes.md` |
-| 卖点/题材采集 | always | 题材配置 | `${SKILL_ROOT}/../../references/genre-profiles.md` |
-
-### md 按需
-
-| Step | Trigger | Reference | 实际路径 |
-|------|---------|-----------|---------|
-| Step 2 | 用户人物扁平 | 角色设计 | `${SKILL_ROOT}/references/worldbuilding/character-design.md` |
-| Step 4 | always | 势力格局 | `${SKILL_ROOT}/references/worldbuilding/faction-systems.md` |
-| Step 4 | 涉及修仙/玄幻/高武/异能 | 力量体系 | `${SKILL_ROOT}/references/worldbuilding/power-systems.md` |
-| Step 4 | always | 世界规则 | `${SKILL_ROOT}/references/worldbuilding/world-rules.md` |
-| Step 5 | always | 创意约束 | `${SKILL_ROOT}/references/creativity/creativity-constraints.md` |
-| Step 5 | always | 卖点生成 | `${SKILL_ROOT}/references/creativity/selling-points.md` |
-| Step 5 | 复合题材 | 题材融合 | `${SKILL_ROOT}/references/creativity/creative-combination.md` |
-| Step 5 | 卡顿 | 灵感候选 | `${SKILL_ROOT}/references/creativity/inspiration-collection.md` |
-| Step 5 | 题材映射命中 | 反套路库 | `${SKILL_ROOT}/references/creativity/anti-trope-*.md` |
-| Step 6 | always | 设定一致性 | `${SKILL_ROOT}/references/worldbuilding/setting-consistency.md` |
-
-### CSV 检索
-
-| Step | Trigger | 检索命令 |
-|------|---------|---------|
-| 角色/书名/势力设定 | 用户开始设定命名 | `python -X utf8 "${SCRIPTS_DIR}/reference_search.py" --skill init --table 命名规则 --query "{命名对象} {题材}" --genre {题材}` |
-
-## 工具策略(按需)
-
-- `Read/Grep`:读取项目上下文与参考文件(`README.md`、`CLAUDE.md`、`templates/genres/*`、`references/*`)。
-- `Bash`:执行 `webnovel.py init`、文件存在性检查、最小验证命令。
-- `Agent`:拆分并行子任务(如题材映射、约束包候选生成、文件验证);Step 1.5 用户选择参考书拆解作为灵感来源时,调用 `webnovel-writer:deconstruction-agent`。
-- `AskUserQuestion`:用于关键分歧裁决、候选方案选择、最终确认。
-- `WebSearch`:用于检索最新市场趋势、平台风向、题材数据(可带域名过滤)。
-- `WebFetch`:用于抓取已确定来源页面内容并做事实核验。
-- 外部检索触发条件:
-  - 用户明确要求参考市场趋势或平台风向;
-  - 创意约束需要"时间敏感依据";
-  - 对题材信息存在明显不确定。
+路径说明:`references/` 指 `skills/webnovel-init/references/`;`../../references/` 指共享 references。详细采集字段见 `references/init-collection-schema.md`(按需区段读,逐项收集,必填项以「充分性闸门」为准)。
+
+| Step | Trigger | Reference |
+|------|---------|-----------|
+| Step 1 | always | `references/system-data-flow.md`、`references/genre-tropes.md` |
+| 题材/卖点采集 | always | `../../references/genre-profiles.md`(只读当前 genre 段) |
+| 角色卡顿 | 人物扁平 | `references/worldbuilding/character-design.md` |
+| 世界观/力量 | 按需 | `references/worldbuilding/faction-systems.md`、`references/worldbuilding/power-systems.md`、`references/worldbuilding/world-rules.md`、`references/worldbuilding/setting-consistency.md` |
+| 创意约束 | Step 6 | `references/creativity/creativity-constraints.md`(区段:采集读 `## 一、创意包 Schema (Idea Package)`、`## 六、硬约束驱动创意 (Hard Constraints)`、`## 八、评分系统 (Scoring System)`,评分展示读 `### 8.1 五维评分`)、`references/creativity/selling-points.md`(区段:`## 9. 核心卖点定位模板` 骨架,按需补 `### 1.3 核心卖点黄金公式`、`## 7. 实战检查清单`);复合题材读 `creative-combination.md`;卡顿读 `inspiration-collection.md`;题材命中读 `anti-trope-*.md` |
+| 命名 | 开始命名 | `python -X utf8 "${SCRIPTS_DIR}/reference_search.py" --skill init --table 命名规则 --query "{命名对象} {题材}" --genre {题材}` |
+
+按需读取上述长细则(创意约束、反套路库、世界观设计指南、卖点模板),不内联其条目。
+
+## 工具策略
+
+- `Read/Grep`:读项目上下文与参考文件。
+- `Bash`:执行 `webnovel.py init`、文件存在性检查、最小验证。
+- `Agent`:拆分并行子任务;Step 1.5 用户选择参考书拆解作灵感来源时调用 `webnovel-writer:deconstruction-agent`。
+- `AskUserQuestion`:关键分歧裁决、候选选择、最终确认。
+- `WebSearch`/`WebFetch`:仅在用户要求市场趋势/平台风向、创意约束需时间敏感依据、或题材信息明显不确定时使用,先 search 后 fetch 核验。
 
 ## 交互流程(Deep)
 
@@ -84,23 +58,16 @@ export SCRIPTS_DIR="${CLAUDE_PLUGIN_ROOT}/scripts"
 ```
 
 必须做:
-- 确认当前目录可写。
-- 解析脚本目录并确认入口存在(仅支持插件目录):
-  - 固定路径:`${CLAUDE_PLUGIN_ROOT}/scripts`
-  - 入口脚本:`${SCRIPTS_DIR}/webnovel.py`
-- 初始化前不要用 `where` 把 `WORKSPACE_ROOT` 解析成书项目根;新项目尚不存在时,`where` 可能命中旧指针或旧项目。
+- 确认当前目录可写;确认入口脚本 `${SCRIPTS_DIR}/webnovel.py` 存在(仅支持插件目录)。
+- 初始化前不要用 `where` 把 `WORKSPACE_ROOT` 解析成书项目根;新项目尚不存在时 `where` 可能命中旧指针或旧项目。
 - 只打印工作区与脚本目录,确认生成目标将在工作区下的书名安全化子目录中。
-- 加载最小参考:
-  - `references/system-data-flow.md`(用于校对 init 产物与 plan/write 输入链路)
-  - `references/genre-tropes.md`
-  - `templates/genres/`(仅在用户选定题材后按需读取)
+- 加载最小参考:`references/system-data-flow.md`、`references/genre-tropes.md`;`templates/genres/` 仅在选定题材后按需读取。
 
-输出:
-- 进入 Deep 采集前的"已知信息清单"和"待收集清单"。
+输出:进入 Deep 采集前的"已知信息清单"和"待收集清单"。
 
 ### Step 1.5:灵感来源询问(可选)
 
-进入故事核采集前,必须先用 `AskUserQuestion` 或直接提问的方式确认用户是否提供灵感来源。不要默认拆书,也不要把参考作品当作必填项。
+进入故事核采集前,必须先用 `AskUserQuestion` 或直接提问确认用户是否提供灵感来源。不要默认拆书,也不要把参考作品当作必填项。
 
 建议询问:
 
@@ -108,113 +75,53 @@ export SCRIPTS_DIR="${CLAUDE_PLUGIN_ROOT}/scripts"
 你这本书的灵感来源想从哪里开始?可以直接说原创想法,也可以提供参考作品做拆书提炼。若要拆书,请给参考书名+平台,并尽量提供章节摘录或文本路径;没有参考也可以直接跳过。
 ```
 
-可接受的灵感来源:
-- 用户自由描述的原创想法;
-- 参考作品拆书:书名、平台、章节摘录、完整文本路径;
-- 市场趋势或平台风向;
-- 题材模板、反套路库、已有脑洞片段。
+可接受来源:原创想法、参考作品拆书(书名/平台/章节摘录/文本路径)、市场趋势、题材模板/反套路库/已有脑洞片段。
 
 当用户选择参考作品拆书且提供文本路径或章节摘录时,必须使用 `Agent` 工具调用 `webnovel-writer:deconstruction-agent`,不得由 init 主流程口头替代拆解结果。
 
 ```text
-Agent(
-  subagent_type: "webnovel-writer:deconstruction-agent",
-  prompt: "reference_title={reference_title}; reference_source={reference_source}; reference_text_path={reference_text_path}; reference_text_excerpt={reference_text_excerpt}; analysis_mode={quick|deep|auto}; init_goal={当前初始化故事方向或空}; target_genre={题材或空}。只返回 init_reference_research JSON 对象,不写任何文件,不创建目录,不写 .story-system、.webnovel、设定集、大纲、正文、idea_bank.json、state.json 或任何 canon/read model 文件。"
-)
+Use the Agent tool to run `webnovel-writer:deconstruction-agent`.
+
+Prompt: reference_title={reference_title}; reference_source={reference_source}; reference_text_path={reference_text_path}; reference_text_excerpt={reference_text_excerpt}; analysis_mode={quick|deep|auto}; init_goal={当前初始化故事方向或空}; target_genre={题材或空}。只返回 init_reference_research JSON 对象,不写任何文件,不创建目录,不写 .story-system、.webnovel、设定集、大纲、正文、idea_bank.json、state.json 或任何 canon/read model 文件。
 ```
 
 处理规则:
-- 如果用户只有书名/平台,没有文本或摘录,先询问是否能提供摘录/路径;若不能提供,则把参考书仅作为"方向线索",不得编造该书黄金三章、角色、设定或剧情事实。
-- 接收返回的 `init_reference_research` JSON 后,只使用其中的 `reader_promise`、`opening_hook_patterns`、`cool_point_loops`、`protagonist_patterns`、`antagonist_pressure_patterns`、`pacing_notes`、`borrowable_structures`、`differentiation_requirements`、`init_candidates`、`quality`。
-- 先检查 `quality`:`quality.passed=false`、`confidence < 0.85` 或 `warnings` 非空时,不得把候选折叠进创意约束包只能把风险和需补充材料展示给用户确认。
+- 只有书名/平台、无文本或摘录时,先问能否提供摘录/路径;不能提供则把参考书仅作"方向线索",不得编造其黄金三章、角色、设定或剧情事实。
+- 接收返回的 `init_reference_research` JSON 后,只使用 `reader_promise`、`opening_hook_patterns`、`cool_point_loops`、`protagonist_patterns`、`antagonist_pressure_patterns`、`pacing_notes`、`borrowable_structures`、`differentiation_requirements`、`init_candidates`、`quality`。
+- 先检查 `quality`:`quality.passed=false`、`confidence < 0.85` 或 `warnings` 非空时,不得把候选折叠进创意约束包只能把风险和需补充材料展示给用户确认。
 - `do_not_copy` 和 `canon_contamination_warnings` 必须进入已知信息清单,作为后续创意生成红线。
-- Step 2-6 只能使用用户确认过、并已变形为本书差异化表达的模式。
-- 禁止把参考书角色、设定、组织、地点、金手指、剧情事实原样写入生成项目文件。
+- Step 2-6 只能使用用户确认过、并已变形为本书差异化表达的模式;禁止把参考书角色、设定、组织、地点、金手指、剧情事实原样写入生成项目文件。
 
 ### Step 2:故事核与商业定位
 
-收集项(必收):
-- 书名(可先给工作名)
-- 题材(支持 A+B 复合题材)
-- 目标规模(总字数或总章数)
-- 一句话故事
-- 核心冲突
-- 目标读者/平台
-
-canonical 题材集合(写入 `project_info.genre`):
-- 都市 | 玄幻 | 仙侠 | 奇幻 | 科幻
-- 历史 | 悬疑 | 游戏 | 古言 | 现言
-- 幻言 | 年代 | 种田 | 快穿 | 衍生
-
-可自由输入细分 preset / 套路 / 形式,初始化脚本会映射到 canonical 并按 taxonomy 加载模板:
-- 示例:修仙、系统流、高武、西幻、无限流、末世
-- 示例:都市异能、都市日常、都市脑洞、现实题材、电竞、直播文
-- 示例:规则怪谈、悬疑脑洞、悬疑灵异、克苏鲁、知乎短篇
-- 示例:古言、宫斗宅斗、青春甜宠、豪门总裁、职场婚恋、民国言情、幻想言情、种田、年代
-
-交互方式:
-- 优先让用户自由描述,再二次结构化确认。
-- 若用户卡住,给 2-4 个候选方向供选。
+必收:书名、题材(支持 A+B 复合)、目标规模(总字数或总章数)、一句话故事、核心冲突、目标读者/平台。
 
-### Step 3:角色骨架与关系冲突
+canonical 题材集合(写入 `project_info.genre`):都市、玄幻、仙侠、奇幻、科幻、历史、悬疑、游戏、古言、现言、幻言、年代、种田、快穿、衍生。
+
+可自由输入细分 preset / 套路 / 形式,初始化脚本会映射到 canonical 并按 taxonomy 加载模板(示例:修仙、系统流、规则怪谈、宫斗宅斗、电竞、末世)。优先让用户自由描述再二次结构化确认;卡住时给 2-4 个候选方向。
 
-收集项(必收):
-- 主角姓名
-- 主角欲望(想要什么)
-- 主角缺陷(会害他付代价的缺陷)
-- 主角结构(单主角/多主角)
-- 感情线配置(无/单女主/多女主)
-- 反派分层(小/中/大)与镜像对抗一句话
+### Step 3:角色骨架与关系冲突
 
-收集项(可选):
-- 主角原型标签(成长型/复仇型/天才流等)
-- 多主角分工
+必收:主角姓名、主角欲望、主角缺陷(会害他付代价)、主角结构(单/多主角)、感情线配置(无/单女主/多女主)、反派分层(小/中/大)与镜像对抗一句话。可选:主角原型标签、多主角分工。
 
 ### Step 4:金手指与兑现机制
 
-收集项(必收):
-- 金手指类型(可为"无金手指")
-- 名称/系统名(无则留空)
-- 风格(硬核/诙谐/黑暗/克制等)
-- 可见度(谁知道)
-- 不可逆代价(必须有代价或明确"无+理由")
-- 成长节奏(慢热/中速/快节奏)
-
-收集项(条件必收):
-- 若为系统流:系统性格、升级节奏
-- 若为重生:重生时间点、记忆完整度
-- 若为传承/器灵:辅助边界与出手限制
+必收:金手指类型(可为"无金手指")、名称/系统名(无则留空)、风格、可见度、不可逆代价(必须有代价或明确"无+理由")、成长节奏。
+条件必收:系统流给系统性格+升级节奏;重生给重生时间点+记忆完整度;传承/器灵给辅助边界+出手限制。
 
 ### Step 5:世界观与力量规则
 
-收集项(必收):
-- 世界规模(单城/多域/大陆/多界)
-- 力量体系类型
-- 势力格局
-- 社会阶层与资源分配
-
-收集项(题材相关):
-- 货币体系与兑换规则
-- 宗门/组织层级
-- 境界链与小境界
+必收:世界规模(单城/多域/大陆/多界)、力量体系类型、势力格局、社会阶层与资源分配。
+题材相关:货币体系与兑换规则、宗门/组织层级、境界链与小境界。
 
 ### Step 6:创意约束包(差异化核心)
 
 流程:
 1. 汇总 Step 1.5 已确认的灵感来源:原创想法、参考拆书结果、市场趋势、题材模板或反套路库。
 2. 基于题材映射加载反套路库(最多 2 个主相关库)。
-3. 生成 2-3 套创意包,每套包含:
-   - 一句话卖点
-   - 反套路规则 1 条
-   - 硬约束 2-3 条
-   - 主角缺陷驱动一句话
-   - 反派镜像一句话
-   - 开篇钩子
-4. 三问筛选:
-   - 为什么这题材必须这么写?
-   - 换成常规主角会不会塌?
-   - 卖点能否一句话讲清且不撞模板?
-5. 展示五维评分(详见 `references/creativity/creativity-constraints.md` 的 `8.1 五维评分`),辅助用户决策。
+3. 生成 2-3 套创意包,每套含:一句话卖点、反套路规则 1 条、硬约束 2-3 条、主角缺陷驱动一句话、反派镜像一句话、开篇钩子。
+4. 三问筛选:为什么这题材必须这么写?换常规主角会不会塌?卖点能否一句话讲清且不撞模板?
+5. 展示五维评分(详见 `references/creativity/creativity-constraints.md` 的 `8.1 五维评分`)辅助决策。
 6. 用户选择最终方案,或拒绝并给出原因。
 
 备注:
@@ -223,20 +130,9 @@ canonical 题材集合(写入 `project_info.genre`):
 
 ### Step 7:一致性复述与最终确认
 
-必须输出"初始化摘要草案"并让用户确认:
-- 故事核(题材/一句话故事/核心冲突)
-- 主角核(欲望/缺陷)
-- 金手指核(能力与代价)
-- 世界核(规模/力量/势力)
-- 创意约束核(反套路 + 硬约束)
-
-确认规则:
-- 用户未明确确认,不执行生成。
-- 若用户仅改局部,回到对应 Step 最小重采集。
+必须输出"初始化摘要草案"并让用户确认:故事核(题材/一句话故事/核心冲突)、主角核(欲望/缺陷)、金手指核(能力与代价)、世界核(规模/力量/势力)、创意约束核(反套路+硬约束)。
 
-## 内部数据模型(初始化收集对象)
-
-采集对象的完整字段结构见 `references/init-collection-schema.md`,按其中字段逐项收集;必填项以下方「充分性闸门」为准。
+确认规则:用户未明确确认,不执行生成;用户仅改局部,回到对应 Step 最小重采集。
 
 ## 充分性闸门(必须通过)
 
@@ -247,24 +143,13 @@ canonical 题材集合(写入 `project_info.genre`):
 3. 主角姓名 + 欲望 + 缺陷完整。
 4. 世界规模 + 力量体系类型完整。
 5. 金手指类型已确定(允许"无金手指")。
-6. 创意约束已确定:
-   - 反套路规则 1 条
-   - 硬约束至少 2 条
-   - 或用户明确拒绝并记录原因。
+6. 创意约束已确定:反套路规则 1 条 + 硬约束至少 2 条,或用户明确拒绝并记录原因。
 
 ## 项目目录安全规则(必须)
 
-- `project_root` 必须由书名安全化生成(去非法字符,空格转 `-`)。
-- 构造公式:`project_root = <当前工作目录>/<书名安全化结果>`,即 `PROJECT_ROOT="${WORKSPACE_ROOT}/${PROJECT_SLUG}"`。
-- 若安全化结果为空或以 `.` 开头,自动前缀 `proj-`。
-- 禁止在插件目录下生成项目文件(`${CLAUDE_PLUGIN_ROOT}`)。
-- 禁止直接把 `WORKSPACE_ROOT` 当作 `PROJECT_ROOT`,除非用户明确指定当前目录本身就是书项目根。
-- 初始化前必须展示并确认:
-  - `WORKSPACE_ROOT`
-  - `PROJECT_SLUG`
-  - `PROJECT_ROOT`
-
-推荐安全化命令(与规则保持一致):
+- `project_root` 必须由书名安全化生成:`PROJECT_ROOT="${WORKSPACE_ROOT}/${PROJECT_SLUG}"`;安全化结果为空或以 `.` 开头时自动前缀 `proj-`。
+- 禁止在插件目录(`${CLAUDE_PLUGIN_ROOT}`)下生成项目文件;禁止直接把 `WORKSPACE_ROOT` 当作 `PROJECT_ROOT`,除非用户明确指定当前目录就是书项目根。
+- 初始化前必须展示并确认 `WORKSPACE_ROOT`、`PROJECT_SLUG`、`PROJECT_ROOT`。
 
 ```bash
 PROJECT_SLUG="$(python -X utf8 -c "import re,sys; title=sys.argv[1].strip(); slug=re.sub(r'[\\\\/:*?\"<>|]+','',title); slug=re.sub(r'\\s+','-',slug).strip('-'); print(('proj-' + slug) if (not slug or slug.startswith('.')) else slug)" "{title}")"
@@ -278,79 +163,38 @@ echo "PROJECT_ROOT=${PROJECT_ROOT}"
 
 ### 1) 运行初始化脚本
 
+参数全部来自上面的采集对象(书名/题材/主角/金手指/世界观/反派/创意约束等),逐字段映射为 `webnovel.py init` 的 `--*` 选项;完整字段清单见 `references/init-collection-schema.md`,可用 `python "${SCRIPTS_DIR}/webnovel.py" init --help` 核对选项名。
+
 ```bash
 python "${SCRIPTS_DIR}/webnovel.py" init \
-  "${PROJECT_ROOT}" \
-  "{title}" \
-  "{genre}" \
+  "${PROJECT_ROOT}" "{title}" "{genre}" \
   --protagonist-name "{protagonist_name}" \
-  --target-words {target_words} \
-  --target-chapters {target_chapters} \
-  --golden-finger-name "{gf_name}" \
-  --golden-finger-type "{gf_type}" \
-  --golden-finger-style "{gf_style}" \
-  --core-selling-points "{core_points}" \
-  --protagonist-structure "{protagonist_structure}" \
-  --heroine-config "{heroine_config}" \
-  --heroine-names "{heroine_names}" \
-  --heroine-role "{heroine_role}" \
-  --co-protagonists "{co_protagonists}" \
-  --co-protagonist-roles "{co_protagonist_roles}" \
-  --antagonist-tiers "{antagonist_tiers}" \
-  --world-scale "{world_scale}" \
-  --factions "{factions}" \
-  --power-system-type "{power_system_type}" \
-  --social-class "{social_class}" \
-  --resource-distribution "{resource_distribution}" \
-  --gf-visibility "{gf_visibility}" \
-  --gf-irreversible-cost "{gf_irreversible_cost}" \
-  --currency-system "{currency_system}" \
-  --currency-exchange "{currency_exchange}" \
-  --sect-hierarchy "{sect_hierarchy}" \
-  --cultivation-chain "{cultivation_chain}" \
-  --cultivation-subtiers "{cultivation_subtiers}" \
-  --protagonist-desire "{protagonist_desire}" \
-  --protagonist-flaw "{protagonist_flaw}" \
-  --protagonist-archetype "{protagonist_archetype}" \
-  --antagonist-level "{antagonist_level}" \
-  --target-reader "{target_reader}" \
-  --platform "{platform}"
+  --target-words {target_words} --target-chapters {target_chapters} \
+  --protagonist-desire "{protagonist_desire}" --protagonist-flaw "{protagonist_flaw}" \
+  --golden-finger-type "{gf_type}" --gf-irreversible-cost "{gf_irreversible_cost}" \
+  --world-scale "{world_scale}" --power-system-type "{power_system_type}" \
+  --core-selling-points "{core_points}"
+  # 其余字段(结构/感情线/反派/势力/货币/境界/原型/读者/平台等)按采集对象继续追加对应 --* 选项
 ```
 
 ### 2) 写入 `idea_bank.json`
 
-写入 `.webnovel/idea_bank.json`:
+写入 `.webnovel/idea_bank.json`,内容必须与最终选定方案一致:
 
 ```json
 {
-  "selected_idea": {
-    "title": "",
-    "one_liner": "",
-    "anti_trope": "",
-    "hard_constraints": []
-  },
-  "constraints_inherited": {
-    "anti_trope": "",
-    "hard_constraints": [],
-    "protagonist_flaw": "",
-    "antagonist_mirror": "",
-    "opening_hook": ""
-  }
+  "selected_idea": {"title": "", "one_liner": "", "anti_trope": "", "hard_constraints": []},
+  "constraints_inherited": {"anti_trope": "", "hard_constraints": [], "protagonist_flaw": "", "antagonist_mirror": "", "opening_hook": ""}
 }
 ```
 
 ### 3) Patch 总纲
 
-必须补齐:
-- 故事一句话
-- 核心主线 / 核心暗线
-- 创意约束(反套路、硬约束、主角缺陷、反派镜像)
-- 反派分层
-- 关键爽点里程碑(2-3 条)
+`大纲/总纲.md` 必须补齐:故事一句话、核心主线/暗线、创意约束(反套路、硬约束、主角缺陷、反派镜像)、反派分层、关键爽点里程碑(2-3 条)。
 
 ### 4) 生成写前合同树(Story System 初始化)
 
-init 完成后立即生成 MASTER_SETTING,让后续 plan 有调性/禁忌参照
+init 完成后立即生成 MASTER_SETTING,让后续 plan 有调性/禁忌参照。此处不传 `--chapter`(只生成 `MASTER_SETTING.json` 和 `anti_patterns.json`),也不传 `--emit-runtime-contracts`(还没有卷/章级数据);plan 拆到具体章节时再生成 volume/chapter/review 合同。
 
 ```bash
 GENRE="$(python -X utf8 -c "import json,os; root=os.environ['PROJECT_ROOT']; s=json.load(open(root + '/.webnovel/state.json',encoding='utf-8')); pi=s.get('project_info',{}); print(pi.get('genre') or s.get('project',{}).get('genre',''))")"
@@ -359,15 +203,8 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" \
   story-system "${GENRE}" --genre "${GENRE}" --persist --format json
 ```
 
-说明:
-- 此时不传 `--chapter`,只生成 `MASTER_SETTING.json` 和 `anti_patterns.json`
-- 不传 `--emit-runtime-contracts`(还没有卷/章级数据)
-- plan 阶段拆到具体章节时再生成 volume/chapter/review 合同
-
 ## 验证与交付
 
-执行检查:
-
 ```bash
 test -f "${PROJECT_ROOT}/.webnovel/state.json"
 find "${PROJECT_ROOT}/设定集" -maxdepth 1 -type f -name "*.md"
@@ -378,25 +215,14 @@ test "$(basename "${PROJECT_ROOT}")" = "${PROJECT_SLUG}"
 ```
 
 成功标准:
-- `state.json` 存在且关键字段不为空(title/genre/target_words/target_chapters)。
-- 设定集核心文件存在:`世界观.md`、`力量体系.md`、`主角卡.md`。
-- 单主角项目不生成 `主角组.md`;`heroine_config=无女主` 不生成 `女主卡.md`。
-- 默认不生成 `金手指设计.md`、`复合题材-融合逻辑.md`、`爽点规划.md` 或空的 `角色库/物品库/其他设定` 目录;这些信息以主角卡、世界观、卷纲为事实源。
-- `总纲.md` 已填核心主线与约束字段。
-- `idea_bank.json` 已写入且与最终选定方案一致。
+- `state.json` 存在且 title/genre/target_words/target_chapters 不为空。
+- 设定集核心文件存在:`世界观.md`、`力量体系.md`、`主角卡.md`;单主角不生成 `主角组.md`,`heroine_config=无女主` 不生成 `女主卡.md`。
+- 默认不生成 `金手指设计.md`、`复合题材-融合逻辑.md`、`爽点规划.md` 或空目录;这些以主角卡、世界观、卷纲为事实源。
+- `总纲.md` 已填核心主线与约束字段;`idea_bank.json` 已写入且与最终选定方案一致。
 - `.story-system/MASTER_SETTING.json` 存在且 `route.primary_genre` 非空。
 
 ## 失败处理(最小回滚)
 
-触发条件:
-- 关键文件缺失;
-- 总纲关键字段缺失;
-- 约束启用但 `idea_bank.json` 缺失或内容不一致。
-
-恢复流程:
-1. 仅补缺失字段,不全量重问。
-2. 仅重跑最小步骤:
-   - 文件缺失 -> 重跑 `webnovel.py init`;
-   - 总纲缺字段 -> 只 patch 总纲;
-   - idea_bank 不一致 -> 只重写该文件。
-3. 重新验证,全部通过后结束。
+触发:关键文件缺失;总纲关键字段缺失;约束启用但 `idea_bank.json` 缺失或不一致。
+
+恢复:只补缺失字段,不全量重问;只重跑最小步骤(文件缺失→重跑 `webnovel.py init`;总纲缺字段→只 patch 总纲;idea_bank 不一致→只重写该文件);重新验证,全部通过后结束。

+ 14 - 40
webnovel-writer/skills/webnovel-learn/SKILL.md

@@ -1,6 +1,6 @@
 ---
 name: webnovel-learn
-description: 从当前会话提取成功模式并写入 project_memory.json
+description: 从当前会话提取成功写作模式并写入 project_memory.json
 allowed-tools: Read Bash
 argument-hint: "[要记住的写作经验]"
 ---
@@ -10,7 +10,7 @@ argument-hint: "[要记住的写作经验]"
 ## Project Root Guard(必须先确认)
 
 - 必须在项目根目录执行(需存在 `.webnovel/state.json`)
-- 使用统一入口解析项目根,避免写错目录:
+- 用统一入口解析项目根,避免写错目录:
 
 ```bash
 export WORKSPACE_ROOT="${CLAUDE_PROJECT_DIR:-$PWD}"
@@ -19,31 +19,14 @@ export PROJECT_ROOT="$(python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-roo
 ```
 
 ## 目标
-- 提取可复用的写作模式(钩子/节奏/对话/微兑现等)
-- 追加到 `.webnovel/project_memory.json`
 
-## 输入
-```bash
-/webnovel-learn "本章的危机钩设计很有效,悬念拉满"
-```
-
-## 输出
-```json
-{
-  "status": "success",
-  "learned": {
-    "pattern_type": "hook",
-    "description": "危机钩设计:悬念拉满",
-    "source_chapter": 100,
-    "learned_at": "2026-02-02T12:00:00Z"
-  }
-}
-```
+提取可复用的写作模式(钩子/节奏/对话/微兑现等),追加到 `.webnovel/project_memory.json`。
 
 ## 执行流程
-1. 读取 `"$PROJECT_ROOT/.webnovel/state.json"`,获取当前章节号(progress.current_chapter)
-2. 解析用户输入(即 `/webnovel-learn` 后附带的经验文本;若为空则取本次对话中用户认可的写法),归类 pattern_type(hook/pacing/dialogue/payoff/emotion/format/other)
-3. 必须调用脚本写入,不得手写或拼接 JSON:
+
+1. 读取 `"$PROJECT_ROOT/.webnovel/state.json"` 的 `progress.current_chapter` 作为当前章节号;缺失则用 `source_chapter: null`,不阻断。
+2. 解析用户输入(`/webnovel-learn` 后的经验文本;为空则取本次对话中用户认可的写法),归类 `pattern_type`(hook/pacing/dialogue/payoff/emotion/format/other,无法归类用 `other`)。
+3. 调用 `project-memory add-pattern` 写入,不得手写或拼接 JSON:
 
 ```bash
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" project-memory add-pattern \
@@ -53,30 +36,21 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" pro
   --importance "{high|medium|low}"
 ```
 
-脚本会自动读取/初始化 `.webnovel/project_memory.json`,并用 JSON 序列化写回,自动转义英文双引号、换行等字符。
-
 ## 约束
-- 不删除旧记录,仅追加
-- 避免完全重复的 description(可去重)
-- 禁止使用 `Write` 或手工编辑 `.webnovel/project_memory.json`
-
-## 去重规则
 
-- 追加前扫描已有 `patterns` 数组
-- 若存在 `pattern_type` + `description` 完全相同的记录,跳过并告知用户
-- 部分相似不去重,由用户判断
+- 不删除旧记录,仅追加。
+- 追加前扫描已有 `patterns`;`pattern_type` + `description` 完全相同则跳过并告知用户,部分相似不去重。
+- 禁止使用 `Write` 或手工编辑 `.webnovel/project_memory.json`。
 
 ## 成功标准
 
-- `project_memory.json` 存在且格式合法
-- 新 pattern 已追加到 `patterns` 数组
-- 输出包含 `status: success` 和完整 `learned` 对象
+- `project_memory.json` 存在且格式合法,新 pattern 已追加到 `patterns` 数组。
+- 输出包含 `status: success` 和完整 `learned` 对象。
 
 ## 失败恢复
 
 | 故障 | 恢复方式 |
 |------|---------|
-| `project_memory.json` 不存在 | 自动初始化 `{"patterns": []}` 后继续 |
+| `project_memory.json` 不存在 | 脚本自动初始化 `{"patterns": []}` 后继续 |
 | JSON 解析失败 | 不写入脏数据,告知用户文件损坏并建议手动修复 |
-| `state.json` 缺失导致无法获取章节号 | 使用 `source_chapter: null`,不阻断 |
-| 用户输入无法归类 | 使用 `pattern_type: "other"`,不阻断 |
+| `state.json` 缺失无法取章节号 | 用 `source_chapter: null`,不阻断 |

+ 92 - 253
webnovel-writer/skills/webnovel-plan/SKILL.md

@@ -7,44 +7,23 @@ argument-hint: "[卷号,如 1]"
 
 # Outline Planning
 
-## 目标
-
-- 基于总纲细化卷纲、时间线与章纲,不重做全局故事。
-- 先补齐设定基线,再产出可直接进入写作的章纲。
-- 卷纲完成后,把新增设定增量写回现有设定集。
-- 将详细大纲升级为"结构化详细大纲",为下游写作提供中层情节结构。
+主 agent 职责:基于总纲增量细化卷纲/时间线/章纲,把新增设定写回设定集,并刷新 Story System 写作合同。不重做全局故事,不重写整份总纲或设定集。
 
 ## 执行原则
 
 1. 只做增量补齐,不重写整份总纲或设定集。
 2. 先锁定卷级节奏,再批量拆章。
-3. 时间线是硬约束,所有章纲必须带时间字段。
+3. 时间线是硬约束,所有章纲必须带时间字段。
 4. 若发现总纲与设定冲突,先阻断,再等用户裁决。
-5. 结构化节点服务于写作执行,不追求语法学上的严格 SVO 抽取。
-
-## 常见误区
-
-- ❌ 先拆章再想卷级目标
-- ❌ 时间线字段缺失但仍继续拆章
-- ❌ 把结构化节点写成空泛摘要句
-- ❌ 一次性读完全部 reference 再开始规划
-- ❌ 发现设定冲突后继续产出章纲而不阻断
-
-## 优先级链
+5. 优先级链:用户明确要求 > 总纲核心冲突与卷末高潮 > 时间线硬约束 > skill 默认流程 > reference 建议。
 
-1. 用户明确要求(最高)
-2. 总纲核心冲突与卷末高潮(不可偏离)
-3. 时间线硬约束(单调递增、倒计时正确)
-4. skill 默认流程
-5. reference 建议(最低)
+## 阻断条件
 
-## 决策树入口
-
-- 若项目根不合法或总纲缺失 → **阻断**
-- 若总纲缺少卷名/章节范围/核心冲突/卷末高潮 → **阻断**,请求用户补全
-- 若 Step 2 发现设定冲突 → **标记 BLOCKER**,等待用户裁决
-- 若批量拆章时时间回跳且未标注闪回 → **阻断**当前批次
-- 若 Step 9 验证失败 → 只重做失败批次,不覆盖整卷
+- 项目根不合法或总纲缺失。
+- 总纲缺少卷名 / 章节范围 / 核心冲突 / 卷末高潮 → 阻断并请求用户补全。
+- Step 2 / Step 8 发现设定冲突 → 标记 `BLOCKER`,等待用户裁决。
+- 批量拆章时时间回跳且未标注闪回 → 阻断当前批次。
+- Step 9 验证失败 → 只重做失败批次,不覆盖整卷。
 
 ## 环境准备
 
@@ -57,287 +36,140 @@ export PROJECT_ROOT="$(python "${SCRIPTS_DIR}/webnovel.py" --project-root "${WOR
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" placeholder-scan --format text
 ```
 
-若本次规划会直接落到具体章节,还必须先刷新 Story System runtime 合同:
+规划开始 / 结束都运行 `placeholder-scan`;plan 阶段发现占位先警告并补齐相关文件,进入写章前不得保留当前章相关实体的 `[待...]` / `暂名` / `{占位}`。
 
-```bash
-# genre 从 state.json 的初始化配置快照读取;写前主链真源是 .story-system 合同树。
-# 必须先从详细大纲解析真实 CHAPTER_GOAL,禁止传 {章纲目标} / 第N章章纲目标 这类占位文本。
-GENRE="$(python -X utf8 -c "import json,sys; s=json.load(open('${PROJECT_ROOT}/.webnovel/state.json',encoding='utf-8')); pi=s.get('project_info',{}); print(pi.get('genre') or s.get('project',{}).get('genre',''))")"
+## 读取策略(按阶段触发,不预读全部 reference)
 
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" \
-  story-system "${CHAPTER_GOAL}" --genre "${GENRE}" --chapter {chapter_num} --persist --emit-runtime-contracts --format both
-```
+每个 reference 只在对应 Step 触发时读取,且优先区段读:先用 `Grep` 匹配 `^#{2,4} ` 定位标题锚点行号,再用 `Read` 的 offset/limit 取目标段。
 
-生成后必须把 `.story-system/MASTER_SETTING.json`、`.story-system/volumes/`、
-`.story-system/chapters/`、`.story-system/reviews/` 视为后续写作主链输入。
-规划开始/结束都运行 `placeholder-scan`;plan 阶段发现占位先警告并补齐相关文件,进入写章前不得保留当前章相关实体的 `[待...]` / `暂名` / `{占位}`。
-每卷规划完成后,只向 `大纲/总纲.md` 渐进追加下一卷概要与本卷新增/承接伏笔,不在 init 阶段预填 V2-V20 空表。
-规划完成后写回必须来自显式结构化文件 `大纲/第{volume_id}卷-总纲写回.json`,禁止从卷纲自由文本推断伏笔或开放环。
+| 触发 | 读取方式 | 文件 |
+|------|---------|------|
+| Step 4 | 全文 | `${SKILL_ROOT}/../../templates/output/大纲-卷节拍表.md` |
+| Step 5 | 全文 | `${SKILL_ROOT}/../../templates/output/大纲-卷时间线.md` |
+| Step 6 always | 区段 | `${SKILL_ROOT}/../../references/genre-profiles.md`(仅当前 genre 的 `### 2.x` 段) |
+| Step 6 always | 全文 | `${SKILL_ROOT}/../../references/shared/strand-weave-pattern.md` |
+| 章纲拆分 always | 区段 | `${SKILL_ROOT}/../../references/outlining/plot-signal-vs-spoiler.md` |
+| Step 6 需要爽点 | 区段 | `${SKILL_ROOT}/../../references/shared/cool-points-guide.md` |
+| Step 6/7 需要冲突 | 区段 | `${SKILL_ROOT}/references/outlining/conflict-design.md` |
+| Step 6/7 特定节奏 | 区段 | `${SKILL_ROOT}/references/outlining/genre-volume-pacing.md` |
+| Step 7 追读力分析 | 区段 | `${SKILL_ROOT}/../../references/reading-power-taxonomy.md` |
+| Step 7 章纲细化 + 节点规范 | 区段 | `${SKILL_ROOT}/references/outlining/chapter-planning.md` |
 
-## 引用加载策略
+CSV 创作参考用检索读,不 `cat` 整表:
 
-### md 必读
-
-| Step | Trigger | Reference |
-|------|---------|-----------|
-| Step 4 | always | `templates/output/大纲-卷节拍表.md` |
-| Step 5 | always | `templates/output/大纲-卷时间线.md` |
-| Step 6 | always | `../../references/genre-profiles.md` |
-| Step 6 | always | `../../references/shared/strand-weave-pattern.md` |
-| 章纲拆分 | always | `../../references/outlining/plot-signal-vs-spoiler.md` |
-
-### md 按需
-
-| Step | Trigger | Reference |
-|------|---------|-----------|
-| Step 6 | 需要爽点设计 | `../../references/shared/cool-points-guide.md` |
-| Step 6/7 | 需要冲突设计 | `references/outlining/conflict-design.md` |
-| Step 7 | 需要追读力分析 | `../../references/reading-power-taxonomy.md` |
-| Step 7 | 需要章纲细化 | `references/outlining/chapter-planning.md` |
-| Step 6/7 | 特定题材节奏 | `references/outlining/genre-volume-pacing.md` |
-
-### CSV 检索
-
-| Step | Trigger | 检索命令 |
-|------|---------|---------|
-| 卷级规划 | always | `python -X utf8 "${SCRIPTS_DIR}/reference_search.py" --skill plan --table 场景写法 --query "卷级结构 叙事功能"` |
-| 章纲拆分 | 新增角色出现 | `... --skill plan --table 命名规则 --query "角色命名" --genre {题材}` |
+```bash
+python -X utf8 "${SCRIPTS_DIR}/reference_search.py" --skill plan --table 爽点与节奏 --query "{卷级核心冲突}" --genre "${GENRE}"
+python -X utf8 "${SCRIPTS_DIR}/reference_search.py" --skill plan --table 桥段套路 --query "{卷级核心冲突}" --genre "${GENRE}"
+python -X utf8 "${SCRIPTS_DIR}/reference_search.py" --skill plan --table 命名规则 --query "角色命名" --genre "${GENRE}"
+```
 
 ## 执行流程
 
 ### Step 1:加载项目数据并确认前置条件
 
-**必须加载**:
-
 ```bash
 # 项目配置/投影状态(兼容读取,不作为写后事实真源)
 cat "$PROJECT_ROOT/.webnovel/state.json"
 
-# 总纲(全局蓝图)
+# 总纲(全局蓝图);确认卷名/章节范围/核心冲突/卷末高潮,不足则阻断
 cat "$PROJECT_ROOT/大纲/总纲.md"
 
-# 题材(来自 init 配置快照,后续 CSV 检索和裁决匹配依赖此值)
+# 题材(来自 init 配置快照,后续 CSV 检索和裁决匹配依赖此值);写后主链真源仍是 .story-system/
 GENRE="$(python -X utf8 -c "import json; s=json.load(open('${PROJECT_ROOT}/.webnovel/state.json',encoding='utf-8')); pi=s.get('project_info',{}); print(pi.get('genre') or s.get('project',{}).get('genre',''))")"
 ```
 
-**已有卷的剧情状态**(跨卷规划时必须加载):
+按需读取设定集:`设定集/世界观.md`、`设定集/力量体系.md`、`设定集/主角卡.md`、`设定集/反派设计.md`、`.webnovel/idea_bank.json`。
 
-若已有已完成卷(`.webnovel/summaries/` 下有文件),加载以下数据感知已写内容
+**跨卷状态读取**(已有已完成卷,即 `.webnovel/summaries/` 下有文件时必须执行):
 
 ```bash
-# 最近 5 章摘要(了解剧情走向)
+# 最近 5 章摘要
 for ch in $(seq $((START_CH - 5)) $((START_CH - 1))); do
   cat "$PROJECT_ROOT/.webnovel/summaries/ch$(printf '%04d' $ch).md" 2>/dev/null
 done
 
-# 核心角色当前状态
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" \
-  knowledge query-entity-state --entity "{protagonist_id}" --at-chapter {上一卷最后章}
-
-# 核心关系当前状态
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" \
-  knowledge query-relationships --entity "{protagonist_id}" --at-chapter {上一卷最后章}
-
-# 活跃伏笔(跨卷未回收的伏笔)
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" \
-  memory-contract get-open-loops
+# 核心角色当前状态 / 核心关系当前状态 / 活跃伏笔(跨卷未回收)
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" knowledge query-entity-state --entity "{protagonist_id}" --at-chapter {上一卷最后章}
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" knowledge query-relationships --entity "{protagonist_id}" --at-chapter {上一卷最后章}
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" memory-contract get-open-loops
 ```
 
-**CSV 创作参考**(卷级规划时按需检索):
-
-```bash
-python -X utf8 "${SCRIPTS_DIR}/reference_search.py" --skill plan --table 爽点与节奏 --query "{卷级核心冲突}" --genre "${GENRE}"
-python -X utf8 "${SCRIPTS_DIR}/reference_search.py" --skill plan --table 桥段套路 --query "{卷级核心冲突}" --genre "${GENRE}"
-```
-
-**按需读取**(设定集):
-- `设定集/世界观.md`
-- `设定集/力量体系.md`
-- `设定集/主角卡.md`
-- `设定集/反派设计.md`
-- `.webnovel/idea_bank.json`
-
-阻断条件:
-- 总纲缺少卷名、章节范围、核心冲突或卷末高潮
-
 ### Step 2:补齐设定基线
 
-目标:让设定集从骨架模板进入"可规划、可写作"的状态。
-
-必须补齐:
-- `设定集/世界观.md`:世界边界、社会结构、关键地点用途
-- `设定集/力量体系.md`:境界链、限制、代价与冷却
-- `设定集/主角卡.md`:欲望、缺陷、初始资源与限制
-- `设定集/反派设计.md`:小/中/大反派层级与镜像关系
+让设定集从骨架进入"可规划、可写作"。增量补齐,不清空、不重写整文件;发现冲突先列出并阻断。
 
-硬规则:
-- 只增量补齐,不清空、不重写整文件
-- 发现冲突时先列出冲突并阻断
+- `设定集/世界观.md`:世界边界、社会结构、关键地点用途。
+- `设定集/力量体系.md`:境界链、限制、代价与冷却。
+- `设定集/主角卡.md`:欲望、缺陷、初始资源与限制。
+- `设定集/反派设计.md`:小/中/大反派层级与镜像关系。
 
 ### Step 3:选择目标卷并确认范围
 
-必须确认:
-- 卷名
-- 章节范围
-- 核心冲突
-- 是否存在特殊要求,例如视角、情感线、题材偏移
+确认卷名、章节范围、核心冲突,以及是否有特殊要求(视角、情感线、题材偏移)。
 
 ### Step 4:生成卷节拍表
 
-执行前加载模板:
+加载模板 `${SKILL_ROOT}/../../templates/output/大纲-卷节拍表.md`。
 
-```bash
-cat "${SKILL_ROOT}/../../templates/output/大纲-卷节拍表.md"
-```
-
-硬要求:
-- 必须填写中段反转;若确实没有,写"无(理由:...)"
-- 危机链至少 3 次递增
-- 卷末新钩子必须能落到最后一章的章末未闭合问题
+硬要求:必须填写中段反转,确无则写"无(理由:...)";危机链至少 3 次递增;卷末新钩子必须能落到最后一章的章末未闭合问题。
 
 输出文件:`大纲/第{volume_id}卷-节拍表.md`
 
 ### Step 5:生成卷时间线表
 
-执行前加载模板:
+加载模板 `${SKILL_ROOT}/../../templates/output/大纲-卷时间线.md`。
 
-```bash
-cat "${SKILL_ROOT}/../../templates/output/大纲-卷时间线.md"
-```
-
-硬要求:
-- 必须明确时间体系
-- 必须明确本卷时间跨度
-- 有倒计时事件时必须列出并标记 D-N
+硬要求:必须明确时间体系与本卷时间跨度;有倒计时事件时列出并标记 D-N。
 
 输出文件:`大纲/第{volume_id}卷-时间线.md`
 
 ### Step 6:生成卷纲骨架
 
-必须加载:
+必读 `${SKILL_ROOT}/../../references/genre-profiles.md` 与 `${SKILL_ROOT}/../../references/shared/strand-weave-pattern.md`;按需读取爽点 / 冲突 / 节奏 reference(见读取策略表)。
 
-```bash
-cat "${SKILL_ROOT}/../../references/genre-profiles.md"
-cat "${SKILL_ROOT}/../../references/shared/strand-weave-pattern.md"
-```
+卷纲必须明确:卷摘要、关键人物与反派层级、Strand 分布、爽点密度规划、伏笔规划、约束触发规划。
 
-按需加载
+跨卷一致性检查(非首卷必须执行):
 
-```bash
-cat "${SKILL_ROOT}/../../references/shared/cool-points-guide.md"
-cat "${SKILL_ROOT}/references/outlining/conflict-design.md"
-cat "${SKILL_ROOT}/references/outlining/genre-volume-pacing.md"
-cat "$PROJECT_ROOT/.webnovel/idea_bank.json"
-```
+- 上一卷未回收的伏笔必须出现在新卷伏笔规划中(继续推进或标记回收)。
+- 角色关系变化必须延续,不能当上一卷没发生过。
+- 主角能力 / 境界必须承接,不回退也不跳级(除非有剧情解释)。
+
+### Step 7:批量生成章纲
 
-卷纲必须明确:
-- 卷摘要
-- 关键人物与反派层级
-- Strand 分布
-- 爽点密度规划
-- 伏笔规划
-- 约束触发规划
+批次规则:默认 `10章/批`;复杂题材或多线并进降到 `8章/批`;简单升级流放宽到 `12章/批`;不建议单批超过 `12章`。
 
-跨卷一致性检查(非首卷时必须执行):
-- 上一卷未回收的伏笔必须出现在新卷的伏笔规划中(继续推进或标记回收)
-- 角色关系变化必须延续(不能当上一卷没发生过)
-- 主角能力/境界必须承接(不能回退也不能跳级,除非有剧情解释)
+按需读取 `${SKILL_ROOT}/../../references/reading-power-taxonomy.md` 与 `${SKILL_ROOT}/references/outlining/chapter-planning.md`。
 
-### Step 7:批量生成章纲
+每章必须包含:目标、阻力、代价、时间锚点、章内时间跨度、与上章时间差、倒计时状态、爽点、Strand、反派层级、视角/主角、关键实体、本章变化、章末未闭合问题、钩子,以及结构化节点 `CBN`、`CPNs`、`CEN`、`必须覆盖节点`、`本章禁区`。
 
-批次规则:
-- 默认按 `10章/批`
-- 复杂题材或多线并进时可降到 `8章/批`
-- 简单升级流可放宽到 `12章/批`
-- 不建议单批超过 `12章`
+#### 结构化节点
 
-按需加载:
+节点格式统一为 `主体 | 动作/变化 | 对象/结果`(写作执行骨架,不追求严格语法 SVO)。完整格式说明、字段细则与示例见 `${SKILL_ROOT}/references/outlining/chapter-planning.md` 的「结构化节点规范」,按需区段读,不在本文件内联。
 
-```bash
-cat "${SKILL_ROOT}/../../references/reading-power-taxonomy.md"
-cat "${SKILL_ROOT}/references/outlining/chapter-planning.md"
-```
+核心约束:
 
-每章必须包含:
-- 目标
-- 阻力
-- 代价
-- 时间锚点
-- 章内时间跨度
-- 与上章时间差
-- 倒计时状态
-- 爽点
-- Strand
-- 反派层级
-- 视角/主角
-- 关键实体
-- 本章变化
-- 章末未闭合问题
-- 钩子
-- `章节起点(CBN)`
-- `推进节点(CPNs)`
-- `章节终点(CEN)`
-- `必须覆盖节点`
-- `本章禁区`
-
-#### 结构化节点规范
-
-节点格式统一为 `主体 | 动作/变化 | 对象/结果`(写作执行骨架,不追求严格语法 SVO;格式说明与示例见 `references/outlining/chapter-planning.md` 的「结构化节点规范」)。
-
-结构规则:
-- 每章固定 `1 个 CBN`
-- 每章 `2-4 个 CPN`
-- 每章固定 `1 个 CEN`
-- 相邻章节 `CEN -> 下一章 CBN` 必须逻辑承接(首章和末章除外)
-- `CPNs` 必须按时间顺序排列
-
-必须覆盖规则:
-- 每章必须覆盖节点最多 `4` 个
-- 建议为:`CBN + CEN + 1~2 个核心 CPN`
-- 可选节点只作为写作建议,不得作为 fail 主依据
-
-本章禁区规则:
-- 不超过 `5` 条
-- 只写本章绝对不能发生的硬禁区
-- 不写风格类建议,不写空泛表述
-
-向后兼容:
-- 若旧项目章纲缺失 `CBN/CPNs/CEN/必须覆盖节点/本章禁区` 字段,下游流程正常执行,仅跳过结构化检查
+- 每章固定 1 个 `CBN`、`2-4 个 CPN`、固定 1 个 `CEN`;`CPNs` 按时间顺序排列。
+- 相邻章节 `CEN -> 下一章 CBN` 必须逻辑承接(首章和末章除外)。
+- `必须覆盖节点`最多 4 个,建议 `CBN + CEN + 1~2 个核心 CPN`;可选节点只作建议,不作 fail 主依据。
+- `本章禁区`不超过 5 条,只写本章绝对不能发生的硬禁区,不写风格类建议。
+- 向后兼容:旧项目章纲缺失上述字段时,下游流程正常执行,仅跳过结构化检查。
 
 输出文件:`大纲/第{volume_id}卷-详细大纲.md`
 
 ### Step 8:把新增设定写回现有设定集
 
-输入来源:
-- 卷节拍表
-- 卷时间线表
-- 卷详细大纲
-- 现有设定集文件
+输入:卷节拍表、卷时间线表、卷详细大纲、现有设定集文件。
 
-写回规则:
-- 只增量补充相关段落
-- 新角色写入角色卡或角色组
-- 新势力、地点、规则写入世界观或力量体系
-- 新反派层级写入反派设计
+写回规则:只增量补充相关段落;新角色写入角色卡或角色组;新势力 / 地点 / 规则写入世界观或力量体系;新反派层级写入反派设计。
 
-硬规则:
-- 若发现与总纲或既有设定冲突,标记 `BLOCKER` 并停止后续更新
+硬规则:若发现与总纲或既有设定冲突,标记 `BLOCKER` 并停止后续更新。
 
 ### Step 9:验证、保存并更新状态
 
-必须通过以下检查:
-- 节拍表存在且非空
-- 时间线表存在且非空
-- 详细大纲存在且非空
-- 每章时间字段齐全
-- 时间线单调递增
-- 倒计时推进正确
-- 新设定已回写到现有设定集
-- `BLOCKER=0`
-- 有节点时,相邻章节 `CEN -> CBN` 无明显逻辑冲突
-- 有节点时,每章必须覆盖节点不超过 `4` 个
+必须通过:节拍表 / 时间线表 / 详细大纲均存在且非空;每章时间字段齐全;时间线单调递增;倒计时推进正确;新设定已回写;`BLOCKER=0`;有节点时相邻章节 `CEN -> CBN` 无明显逻辑冲突且每章`必须覆盖节点`不超过 4 个。
 
-验证全部通过后,生成显式结构化写回文件:
+验证全部通过后,生成显式结构化写回文件 `大纲/第{volume_id}卷-总纲写回.json`(只写规划中显式列出的伏笔 / 开放环,禁止从卷纲自由文本推断):
 
 ```json
 {
@@ -356,7 +188,7 @@ cat "${SKILL_ROOT}/references/outlining/chapter-planning.md"
 }
 ```
 
-只允许写入规划过程中显式列出的结构化伏笔/开放环;不要把自由文本里的暗示整理进去。随后执行最小总纲写回:
+执行最小总纲写回(只更新 `大纲/总纲.md` 的 V+1 卷名 / 核心冲突 / 卷末高潮与伏笔表,不生成下一卷详细大纲 / 节拍表 / 时间线 / 章纲)
 
 ```bash
 python "${SCRIPTS_DIR}/webnovel.py" --project-root "$PROJECT_ROOT" master-outline-sync \
@@ -365,8 +197,6 @@ python "${SCRIPTS_DIR}/webnovel.py" --project-root "$PROJECT_ROOT" master-outlin
   --format text
 ```
 
-该步骤只允许更新 `大纲/总纲.md` 的 V+1 卷名 / 核心冲突 / 卷末高潮与伏笔表,不得生成下一卷详细大纲、节拍表、时间线或章纲。
-
 更新状态:
 
 ```bash
@@ -375,17 +205,26 @@ python "${SCRIPTS_DIR}/webnovel.py" --project-root "$PROJECT_ROOT" update-state
   --chapters-range "{start}-{end}"
 ```
 
+### Step 10:刷新 Story System 写作合同(本次规划已落到具体章节时必须执行)
+
+genre 从 `state.json` 初始化配置快照读取;写前主链真源是 `.story-system/` 合同树。必须先从详细大纲解析真实 `CHAPTER_GOAL`,禁止传 `{章纲目标}` / `第N章章纲目标` 这类占位文本。
+
+```bash
+GENRE="$(python -X utf8 -c "import json; s=json.load(open('${PROJECT_ROOT}/.webnovel/state.json',encoding='utf-8')); pi=s.get('project_info',{}); print(pi.get('genre') or s.get('project',{}).get('genre',''))")"
+
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" story-system "${CHAPTER_GOAL}" \
+  --genre "${GENRE}" --chapter {chapter_num} --persist --emit-runtime-contracts --format both
+```
+
+生成后必须把 `.story-system/MASTER_SETTING.json`、`.story-system/volumes/`、`.story-system/chapters/`、`.story-system/reviews/` 视为后续写作主链输入。进入写章前不得保留当前章相关实体的 `[待...]` / `暂名` / `{占位}`。
+
 ## 硬失败条件
 
-- 节拍表不存在或为空
-- 中段反转缺失且未给出理由
-- 时间线表不存在或为空
-- 详细大纲不存在或为空
-- 任一章节缺少时间字段
-- 时间回跳且未标注闪回
-- 倒计时算术冲突
-- 与总纲核心冲突或卷末高潮明显冲突
-- 存在 `BLOCKER` 未裁决
+- 节拍表 / 时间线表 / 详细大纲不存在或为空。
+- 中段反转缺失且未给出理由。
+- 任一章节缺少时间字段;时间回跳且未标注闪回;倒计时算术冲突。
+- 与总纲核心冲突或卷末高潮明显冲突。
+- 存在 `BLOCKER` 未裁决。
 
 ## 恢复规则
 

+ 37 - 33
webnovel-writer/skills/webnovel-query/SKILL.md

@@ -23,66 +23,70 @@ export PROJECT_ROOT="$(python "${SCRIPTS_DIR}/webnovel.py" --project-root "${WOR
 - `PROJECT_ROOT` 必须包含 `.webnovel/state.json`
 - **禁止**在 `${CLAUDE_PLUGIN_ROOT}/` 下读取或写入项目文件
 
-## 查询类型识别
-
-| 关键词 | 查询类型 | 数据源 |
-|--------|---------|--------|
-| 角色/主角/配角 | 标准查询 | 主角卡.md, 角色库/ |
-| 境界/筑基/金丹 | 标准查询 | 力量体系.md |
-| 宗门/势力/地点 | 标准查询 | 世界观.md |
-| 伏笔/紧急伏笔 | 伏笔分析 | state.json + foreshadowing.md |
-| 金手指/系统 | 金手指状态 | state.json |
-| 节奏/Strand | 节奏分析 | state.json + strand-weave-pattern.md |
-| 标签/实体格式 | 格式查询 | tag-specification.md |
-| 某角色在第N章时/历史状态/时间点状态 | 时序查询 | knowledge query-entity-state / query-relationships |
+## 查询分类 → 最窄工具
+
+先识别查询类型,再用下表最窄工具。不默认全量加载,只在综合 / 跨多类型查询时用 `memory-contract load-context`。
+
+| 查询类型 | 关键词 | 最窄工具 |
+|---------|--------|---------|
+| 角色历史状态 | 某角色在第N章时 / 时间点状态 / 境界变化 | `knowledge query-entity-state` |
+| 实体关系 | 关系 / 敌友 / 师徒 / 阵营归属 | `knowledge query-relationships` |
+| 世界规则 | 力量规则 / 设定铁律 / 境界体系约束 | `memory-contract query-rules` |
+| 伏笔 / open loop | 伏笔 / 紧急伏笔 / 未闭合悬念 | `memory-contract get-open-loops` |
+| 综合 / 复杂 | 跨多类型、需要时间线 + 长期记忆联合 | `memory-contract load-context` |
+| 静态设定 | 角色卡 / 力量体系 / 世界观 / 势力 / 标签格式 | `Grep` + `Read` 设定集 |
 
 ## 引用加载策略
 
-先识别查询类型,再按需加载。路径说明:`references/` 指 skill 私有 `skills/webnovel-query/references/`;`../../references/` 指共享 references。
+按查询类型按需加载,先识别再加载。路径说明:`references/` 指 skill 私有 `skills/webnovel-query/references/`;`../../references/` 指共享 references。
 
 | 查询类型 | Reference | 实际路径 |
 |---------|-----------|---------|
-| 所有查询 | 数据流规范 | `${SKILL_ROOT}/references/system-data-flow.md` |
+| 数据流 / 优先级 | 数据流规范 | `${SKILL_ROOT}/references/system-data-flow.md` |
 | 伏笔分析 | 伏笔分析 | `${SKILL_ROOT}/references/advanced/foreshadowing.md` |
 | 节奏分析 | Strand 模式 | `${SKILL_ROOT}/../../references/shared/strand-weave-pattern.md` |
 | 格式查询 | 标签规范 | `${SKILL_ROOT}/references/tag-specification.md` |
 
-不得同时加载两个以上 L2 文件,除非用户请求明确跨多类型。
+不得同时加载两个以上 reference,除非用户请求明确跨多类型。
 
 ## 查询流程
 
-1. **识别查询类型**:根据用户关键词匹配上表
-2. **加载参考**:只加载该类型需要的 reference
-3. **加载主链上下文**:
-
-```bash
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" memory-contract load-context --chapter {chapter_num}
-```
-
-4. **按优先级查询数据源**(写前真源 → 写后真源 → 投影层):
+1. **识别查询类型**:按「查询分类 → 最窄工具」表匹配关键词。
+2. **按优先级定位写前真源**(写前真源 → 写后真源 → 投影层):
    1. `.story-system/MASTER_SETTING.json` - 全书主设定(题材、调性、核心禁忌)
    2. `.story-system/volumes/*.json` - 卷级合同(本卷目标、节奏策略)
    3. `.story-system/chapters/*.json` - 章级合同(本章焦点、动态上下文)
    4. latest accepted `.story-system/commits/chapter_XXX.commit.json` - 写后事实(已发布章节的定稿状态)
-   5. `memory-contract load-context` - 记忆编排结果(长期记忆、伏笔、时间线)
-   6. `.webnovel/state.json` / `index.db` - 投影层(仅 fallback/read-model,类比网文后台的"角色卡"、"章节列表")
-   
+   5. `memory-contract` 系列查询 - 记忆编排结果(长期记忆、伏笔、时间线)
+   6. `.webnovel/state.json` / `index.db` - 投影层(仅 fallback / read-model,类比网文后台的"角色卡"、"章节列表")
+
    **优先级说明**:
    - 写前真源(1-3):作者开写前必须遵守的"大纲、设定、禁区"
    - 写后真源(4):已发布章节的"定稿状态",不可篡改
    - 投影层(5-6):从写后真源自动生成的"查询视图",方便快速检索
-5. **确认上下文充足**:查询类型已识别 + 主链合同 / latest commit 已加载
-6. **执行查询**:按类型检索对应数据源。若为时序查询,使用以下命令:
+
+3. **调用最窄工具检索**:按类型只调用所需命令,不默认全量 `load-context`。
 
 ```bash
-# 查询某实体在指定章节时的状态
+# 角色历史状态:某实体在指定章节时的状态
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" knowledge query-entity-state --entity "{entity_id}" --at-chapter {N}
 
-# 查询某实体在指定章节时的所有关系
+# 实体关系:某实体在指定章节时的所有关系
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" knowledge query-relationships --entity "{entity_id}" --at-chapter {N}
+
+# 世界规则
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" memory-contract query-rules --chapter {chapter_num}
+
+# 伏笔 / open loop
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" memory-contract get-open-loops
+
+# 仅综合 / 复杂查询:需要时间线 + 长期记忆联合时才用
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" memory-contract load-context --chapter {chapter_num}
 ```
 
-7. **格式化输出**:按下方模板输出
+   静态设定(角色卡 / 力量体系 / 世界观 / 标签格式)直接用 `Grep` 定位行号再 `Read` 取片段,不经 memory-contract。
+
+4. **格式化输出**:按下方模板输出。
 
 ## 输出格式
 
@@ -91,7 +95,7 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" kno
 
 ## 概要
 - **匹配类型**: {type}
-- **数据源**: state.json + 设定集 + 大纲
+- **数据源**: {实际命中的真源 / 投影层}
 - **匹配数量**: X 条
 
 ## 详细信息

+ 41 - 101
webnovel-writer/skills/webnovel-review/SKILL.md

@@ -9,124 +9,71 @@ argument-hint: "[章号或范围,如 5 或 1-5]"
 
 ## 目标
 
-- 解析真实书项目根目录,按统一流程完成章节审查。
-- 调用统一 `reviewer` 生成结构化问题列表与审查报告。
-- 把审查指标写入 `index.db`,并把审查记录写入 `.webnovel/state.json` 兼容投影,主链事实仍以 review contract 与 accepted `CHAPTER_COMMIT` 为准。
-- 审查时优先依据 `.story-system/reviews/chapter_{NNN}.review.json` 与 latest accepted `CHAPTER_COMMIT` 判断主链事实。
-- 若存在关键问题,明确交给用户决定是否立即返工。
+- 解析真实书项目根,调度统一 `reviewer` 完成结构化审查并落库。
+- 主链事实以 `.story-system/reviews/chapter_{NNN}.review.json` 与 latest accepted `CHAPTER_COMMIT` 为准;`.webnovel/state.json` 仅为兼容投影。
+- 有 `blocking=true` 问题时交用户裁决。
 
-## 常见误区
+## 红线
 
-- ❌ 没看 reviewer 原始 JSON 就直接口头总结
-- ❌ 有 blocking issue 仍将流程视为通过
-- ❌ 把 report 文件生成等同于已落库(`review-pipeline --save-metrics` 未跑)
-- ❌ 主流程伪造 `overall_score` 或审查结论
-- ❌ 按需参考一次性全部读完
-
-## 优先级链
-
-1. 用户明确要求(最高)
-2. `blocking=true` 硬门槛
-3. 项目私有约束(设定集、已有剧情)
-4. skill 默认流程
-5. reference 建议(最低)
-
-## 决策树入口
-
-- 若项目根不合法或缺少 `.webnovel/state.json` → **阻断**
-- 若正文文件不存在 → **阻断**
-- 若 reviewer 返回 `blocking=true` issue → 进入 Step 6 用户裁决
-- 若所有 issue 均为非 blocking → 正常落库,流程结束
+- 必须通过 `Agent` 工具调用 `reviewer`,禁止主流程伪造结论或口头总结代替 subagent 输出。
+- reviewer 只返回严格 JSON;主流程负责把返回值写入 `${PROJECT_ROOT}/.webnovel/tmp/review_results.json`。
+- 报告与 metrics 只由 `review-pipeline --save-metrics` 产出;主流程不伪造 `overall_score`。
+- 项目根不合法 / 缺 `.webnovel/state.json` / 缺待审正文 → 阻断。
 
 ## 执行流程
 
-### Step 1:解析项目根目录并建立环境变量
+### Step 1:解析项目根
 
 ```bash
 export WORKSPACE_ROOT="${CLAUDE_PROJECT_DIR:-$PWD}"
-export SKILL_ROOT="${CLAUDE_PLUGIN_ROOT}/skills/webnovel-review"
 export SCRIPTS_DIR="${CLAUDE_PLUGIN_ROOT}/scripts"
 export PROJECT_ROOT="$(python "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" where)"
 ```
 
-若目标章缺少 runtime 合同,先补齐:
+`PROJECT_ROOT` 必须包含 `.webnovel/state.json`,否则阻断。
+
+### Step 2:目标章缺合同时刷新 runtime 合同
+
+目标章缺 runtime 合同时,先用详细大纲的真实本章目标刷新(`CHAPTER_GOAL` 禁止 `{章纲目标}` / `第N章章纲目标` 占位文本):
 
 ```bash
-GENRE="$(python -X utf8 -c "import json,sys; s=json.load(open('${PROJECT_ROOT}/.webnovel/state.json',encoding='utf-8')); pi=s.get('project_info',{}); print(pi.get('genre') or s.get('project',{}).get('genre',''))")"
+GENRE="$(python -X utf8 -c "import json; s=json.load(open('${PROJECT_ROOT}/.webnovel/state.json',encoding='utf-8')); pi=s.get('project_info',{}); print(pi.get('genre') or s.get('project',{}).get('genre',''))")"
 
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" \
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" \
   story-system "${CHAPTER_GOAL}" --genre "${GENRE}" --chapter {chapter_num} --persist --emit-runtime-contracts --format both
 ```
 
-要求:
-- `PROJECT_ROOT` 必须包含 `.webnovel/state.json`
-- 任一关键目录不存在时立即阻断
-- `CHAPTER_GOAL` 必须来自详细大纲真实目标;若 `chapter_brief.meta.query` 仍是 `{章纲目标}` / `第N章章纲目标`,按系统问题记录。
-
-### Step 2:按需加载参考资料
-
-#### md 必读
+### Step 3:按需加载参考
 
 | Trigger | Reference |
 |---------|-----------|
 | always | `../../references/shared/core-constraints.md` |
 | always | `../../references/review-schema.md` |
-
-#### md 按需
-
-| Trigger | Reference |
-|---------|-----------|
-| 审查涉及爽点或钩子分析 | `../../references/shared/cool-points-guide.md` |
+| 审查涉及爽点或钩子 | `../../references/shared/cool-points-guide.md` |
 | 审查涉及多线交织 | `../../references/shared/strand-weave-pattern.md` |
-| blocking issue 需用户决策 (Step 6) | `../../references/review/blocking-override-guidelines.md` |
+| blocking issue 需用户裁决 (Step 8) | `../../references/review/blocking-override-guidelines.md` |
 
-### Step 3:加载项目投影状态与待审正文
+### Step 4:加载投影状态与待审正文
 
 ```bash
 cat "${PROJECT_ROOT}/.webnovel/state.json"
 ```
 
-要求:
-- 明确当前章节号与对应正文文件
-- 若缺少正文或兼容状态文件,立即阻断
+确认当前章节号与对应正文文件;缺正文或缺兼容状态文件立即阻断。
 
-### Step 4:调用统一审查 Agent
+### Step 5:调用统一审查 Agent
 
-必须通过 `Agent` 工具调用 `reviewer`,禁止主流程伪造结论或口头总结代替 subagent 输出
+必须通过 `Agent` 工具调用 `reviewer`。审查方法与维度细则由 reviewer 自带,本 Skill 不展开
 
 ```text
-Agent(
-  subagent_type: "webnovel-writer:reviewer",
-  prompt: "chapter={chapter_num}; chapter_file={chapter_file}; project_root=${PROJECT_ROOT}; scripts_dir=${SCRIPTS_DIR}。严格输出 reviewer schema JSON,并保存到 ${PROJECT_ROOT}/.webnovel/tmp/review_results.json。"
-)
-```
-
-输入:
-- `chapter`
-- `chapter_file`
-- `project_root`
-- `scripts_dir`
-
-输出约束:
-- 只输出 JSON
-- 每个 issue 必须有 `evidence`
-- 不输出 `overall_score`
+Use the Agent tool to run `webnovel-writer:reviewer`.
 
-中间产物约定:
-- reviewer 原始结果:`${PROJECT_ROOT}/.webnovel/tmp/review_results.json`
-- 落库指标:`${PROJECT_ROOT}/.webnovel/tmp/review_metrics.json`
-
-### Step 5:生成审查报告并落库
-
-报告保存到:`审查报告/第{chapter_num}章审查报告.md`
+Prompt: chapter={chapter_num}; chapter_file={chapter_file}; project_root=${PROJECT_ROOT}; scripts_dir=${SCRIPTS_DIR}。严格输出 reviewer schema JSON,不评分,不口头总结。
+```
 
-报告结构:
-- 总览(问题数 / 阻断数)
-- 阻断问题
-- 其他问题
-- 修复方向
+reviewer 返回后,主流程把严格 JSON 写入 `${PROJECT_ROOT}/.webnovel/tmp/review_results.json`(reviewer 不持 Write,是这份 artifact 的非写入方)。
 
-标准文件流:
+### Step 6:生成报告并落库
 
 ```bash
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" review-pipeline \
@@ -137,34 +84,27 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" rev
   --save-metrics
 ```
 
-要求:
-- `review-pipeline --save-metrics` 必须同时完成报告生成、metrics 文件输出、`review_metrics` 表写入
-- 阻断判断以 reviewer 原始结果中的 `blocking=true` 为准
+`review-pipeline --save-metrics` 同时完成报告生成、`review_metrics.json` 输出、`review_metrics` 表写入。阻断判断以 review_results 中的 `blocking=true` 为准。
 
-### Step 6:写入兼容审查记录并处理阻断
-
-先写入兼容审查记录(read-model/projection,不是写后事实真源):
+### Step 7:写入兼容审查记录
 
 ```bash
 python "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" update-state -- --add-review "{chapter_num}-{chapter_num}" "审查报告/第{chapter_num}章审查报告.md"
 ```
 
-如存在任意 `blocking=true` 问题,必须使用 `AskUserQuestion` 询问用户:
-- 立即修复
-- 仅保存报告,稍后处理
+兼容投影 / read model,不是写后事实真源。
+
+### Step 8:处理阻断
 
-若用户选择立即修复:
-- 输出返工清单
-- 在用户明确授权下做最小修改
+存在任意 `blocking=true` 问题时,用 `AskUserQuestion` 让用户裁决:
 
-若用户选择稍后处理:
-- 保留报告与指标记录,结束流程
+- 立即修复:输出返工清单,仅在用户明确授权下做最小修改。
+- 仅保存报告,稍后处理:保留报告与指标记录,结束流程
 
 ## 成功标准
 
-1. 已解析真实书项目根目录。
-2. 已通过 `reviewer` 输出结构化问题 JSON。
-3. 审查报告已生成。
-4. `review_metrics` 已写入 `index.db`。
-5. 审查记录已写入 `.webnovel/state.json` 兼容投影。
-6. 如存在阻断问题,用户已明确选择处理策略。
+1. 已解析真实书项目根。
+2. 已通过 `reviewer` 输出结构化问题 JSON,落盘到 `.webnovel/tmp/review_results.json`。
+3. 审查报告已生成,`review_metrics` 已写入 `index.db`,`review_metrics.json` 已输出。
+4. 审查记录已写入 `.webnovel/state.json` 兼容投影。
+5. 存在阻断问题时,用户已明确选择处理策略。

+ 60 - 24
webnovel-writer/skills/webnovel-write/SKILL.md

@@ -17,7 +17,7 @@ argument-hint: "[章号] [--fast|--minimal]"
 |------|------|
 | 默认 | Step 1→2→3→4→5→6 |
 | `--fast` | Step 1→2→3(轻量)→4→5→6 |
-| `--minimal` | Step 1→2→4(仅排版)→5→6 |
+| `--minimal` | Step 1→2→3(写 no-review artifact)→4(仅排版)→5→6 |
 
 ## 硬规则
 
@@ -61,7 +61,7 @@ genre 从 `.webnovel/state.json` 的初始化配置快照读取,用于刷新
 ```bash
 GENRE="$(python -X utf8 -c "import json,sys; s=json.load(open('${PROJECT_ROOT}/.webnovel/state.json',encoding='utf-8')); pi=s.get('project_info',{}); print(pi.get('genre') or s.get('project',{}).get('genre',''))")"
 
-python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" \
+python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" \
   story-system "${CHAPTER_GOAL}" --genre "${GENRE}" --chapter {chapter_num} --persist --emit-runtime-contracts --format both
 
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" \
@@ -83,12 +83,16 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" \
 
 必须使用 `Agent` 工具调用 `context-agent`,不得由主流程自行整理任务书。
 
-```text
-Agent(
-  subagent_type: "webnovel-writer:context-agent",
-  prompt: "chapter={chapter_num}; project_root=${PROJECT_ROOT}; scripts_dir=${SCRIPTS_DIR}; storage_path=${PROJECT_ROOT}/.webnovel; state_file=${PROJECT_ROOT}/.webnovel/state.json(projection/read-model,仅兼容读取)。先 research,再按 本章硬性约束→CBN/CPNs/CEN→本章禁区→风格指引→dynamic_context补充参考 的顺序输出五段写作任务书。"
-)
-```
+Use the Agent tool to run `webnovel-writer:context-agent`.
+
+Task:
+- chapter={chapter_num}
+- project_root=${PROJECT_ROOT}
+- scripts_dir=${SCRIPTS_DIR}
+- storage_path=${PROJECT_ROOT}/.webnovel
+- state_file=${PROJECT_ROOT}/.webnovel/state.json(projection/read-model,仅兼容读取)
+- 先 research,再按 本章硬性约束 → CBN/CPNs/CEN → 本章禁区 → 风格指引 → dynamic_context 补充参考 的顺序输出五段写作任务书。
+- 上下文不足时返回 blocker。
 
 产物:一份写作任务书,能独立支撑 Step 2 起草。
 
@@ -100,12 +104,17 @@ Agent(
 
 必须使用 `Agent` 工具调用 `reviewer`,不得由主流程伪造审查 JSON。
 
-```text
-Agent(
-  subagent_type: "webnovel-writer:reviewer",
-  prompt: "chapter={chapter_num}; chapter_file=${CHAPTER_FILE}; project_root=${PROJECT_ROOT}; scripts_dir=${SCRIPTS_DIR}。严格输出 reviewer schema JSON,并保存到 ${PROJECT_ROOT}/.webnovel/tmp/review_results.json。"
-)
-```
+Use the Agent tool to run `webnovel-writer:reviewer`.
+
+Task:
+- chapter={chapter_num}
+- chapter_file=${CHAPTER_FILE}
+- project_root=${PROJECT_ROOT}
+- scripts_dir=${SCRIPTS_DIR}
+- 只返回严格的 reviewer schema JSON,不写任何文件。
+- 不评分、不口头总结。
+
+reviewer 只返回 JSON;主流程负责用 `Write` 把返回的 JSON 写入 `${PROJECT_ROOT}/.webnovel/tmp/review_results.json`(reviewer 不持 Write,是这份 artifact 的非写入方),随后运行 review-pipeline。
 
 ```bash
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" review-pipeline \
@@ -116,11 +125,17 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" rev
   --save-metrics
 ```
 
-审查只跑一轮,reviewer 只调用一次。`blocking=true` 的问题在不改剧情、不破设定的前提下定点修复后直接进 Step 4,不重新调用 reviewer;确实无法修复的 blocking 问题用 `AskUserQuestion` 让用户裁决(接受当前版本 / 手动修复 / 放弃)。非 blocking issue 交给 Step 4 处理。`--fast` 只检查 setting/timeline/continuity。`--minimal` 跳过。
+审查只跑一轮,reviewer 只调用一次。`blocking=true` 的问题在不改剧情、不破设定的前提下定点修复后直接进 Step 4,不重新调用 reviewer;确实无法修复的 blocking 问题用 `AskUserQuestion` 让用户裁决(接受当前版本 / 手动修复 / 放弃)。非 blocking issue 交给 Step 4 处理。`--fast` 只检查 setting/timeline/continuity。
+
+`--minimal` 不调用 reviewer 与 `review-pipeline`,但必须**覆盖写入**本章新的 no-review `review_results.json`(禁止复用旧 artifact),使 Step 5 提交链有有效 `--review-result`(成功标准“审查已落库”对 `--minimal` 的豁免仍成立):
+
+```bash
+python -X utf8 -c "import json,os; from pathlib import Path; root=Path(os.environ['PROJECT_ROOT']); ch=int('{chapter_num}'); p=root/'.webnovel'/'tmp'/'review_results.json'; p.parent.mkdir(parents=True,exist_ok=True); p.write_text(json.dumps({'chapter':ch,'issues':[],'issues_count':0,'blocking_count':0,'has_blocking':False,'summary':'minimal mode: reviewer skipped by user-selected --minimal flow','review_skipped':True,'review_mode':'minimal'},ensure_ascii=False,indent=2),encoding='utf-8')"
+```
 
 ### Step 4:润色
 
-加载 `references/polish-guide.md`、`references/writing/typesetting.md`、`references/style-adapter.md`。
+`references/polish-guide.md` 区段读:先 `Grep` 匹配 `^#{1,3} ` 定位锚点行号,再 `Read` 的 offset/limit 取段——主路径取 `## 2. 执行顺序(必须按序)`;Anti-AI 终检单独区段取 `## 2A. Anti-AI 检测细则` 与 `## Phase 1 增补:Anti-AI 规范(7层,原版)`(词库段),不全文读。`references/writing/typesetting.md`、`references/style-adapter.md` 短文件,全文读
 
 顺序:修复非 blocking issue → 风格适配 → 排版 → Anti-AI 终检。
 
@@ -132,21 +147,42 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" rev
 
 必须使用 `Agent` 工具调用 `data-agent`,产出 fulfillment_result / disambiguation_result / extraction_result 三份 JSON,并复用 Step 3 的 review_results。
 
-```text
-Agent(
-  subagent_type: "webnovel-writer:data-agent",
-  prompt: "chapter={chapter_num}; chapter_file=${CHAPTER_FILE}; project_root=${PROJECT_ROOT}; scripts_dir=${SCRIPTS_DIR}。从正文提取事实,生成 .webnovel/tmp/ 下的 fulfillment_result.json、disambiguation_result.json、extraction_result.json;fulfillment_result.json 必须顶层包含 planned_nodes/covered_nodes/missed_nodes/extra_nodes;disambiguation_result.json 必须顶层包含 pending;extraction_result.json 必须严格按你的第7节格式输出顶层字段 accepted_events/state_deltas/entity_deltas/entities_appeared/scenes/summary_text,禁止包在 chapter/fulfillment/disambiguation/extraction 等外层对象里;accepted_events 子项必须包含 event_id/chapter/event_type/subject/payload;不直接写 state/index/summaries/memory。"
-)
-```
+Use the Agent tool to run `webnovel-writer:data-agent`.
 
-Data Agent 只提取事实+生成 artifacts,不直接写 state/index/summaries/memory。
+Task:
+- chapter={chapter_num}
+- chapter_file=${CHAPTER_FILE}
+- project_root=${PROJECT_ROOT}
+- scripts_dir=${SCRIPTS_DIR}
+- output_dir=${PROJECT_ROOT}/.webnovel/tmp
+- 按你自己的 schema(见 data-agent 输出格式段)生成 fulfillment_result.json、disambiguation_result.json、extraction_result.json 三份 artifact。
+- 你是这三份 artifact 的唯一写入者;不直接写 state/index/summaries/memory/vectors/projection。
 
-#### 5.2 CHAPTER_COMMIT
+artifact 字段 schema 由 data-agent 自身定义、runtime validator 校验;主流程只检查文件存在与 schema,不重写、不补写、不口头替代。
+
+#### 5.2 提交前校验与 CHAPTER_COMMIT
+
+先跑 precommit gate:
 
 ```bash
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" \
   write-gate --chapter {chapter_num} --stage precommit --format json
+```
+
+precommit 通过后,运行提交前只读 `git diff` 变更面校验(写入所有权 sanity check,只读、不 stage、不提交):
+
+```bash
+if git -C "${PROJECT_ROOT}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
+  git -C "${PROJECT_ROOT}" diff --name-status -- .
+  git -C "${PROJECT_ROOT}" diff --check -- .
+fi
+```
+
+变更面不得出现插件目录、其他书项目、其他章节正文或不属于本章流程的手写状态文件;`git diff` 只覆盖 git 可见文件,SQLite / `.webnovel/` 内部语义由 5.3 postcommit 与 runtime 只读查询验证。若项目根不是 git worktree,记录“跳过 git diff 校验”,不得因此跳过 precommit gate。本步只读,禁止在此执行 `git add`/`git commit`。
 
+校验通过后运行 chapter-commit:
+
+```bash
 python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" chapter-commit \
   --chapter {chapter_num} \
   --review-result "${PROJECT_ROOT}/.webnovel/tmp/review_results.json" \

+ 2 - 224
webnovel-writer/skills/webnovel-write/references/writing/combat-scenes.md

@@ -3,227 +3,5 @@ name: combat-scenes
 purpose: 战斗场面写作时按需加载
 ---
 
-<context>
-此文件用于战斗场面写作参考。整合五阶段结构、画面切换、感官描写、心理配合、观众反应等核心技巧。
-</context>
-
-<instructions>
-
-## 一、五阶段结构
-
-| 阶段 | 篇幅 | 内容 | 节奏 |
-|------|------|------|------|
-| 起手 | 15-20% | 对峙试探、环境氛围、心理博弈 | 慢 |
-| 试探 | 20-25% | 初步交手、实力试探、能力展示 | 渐快 |
-| 冲突升级 | 30-40% | 正式交锋、底牌亮出、战局变化 | 快 |
-| 高潮 | 15-20% | 关键转折、决定性一击、情绪最高潮 | 极快 |
-| 收尾 | 10-15% | 胜负分晓、后续影响、气氛收束 | 慢 |
-
-**战斗类型与字数**:
-
-| 类型 | 字数 | 节奏 | 爽点 |
-|------|------|------|------|
-| 秒杀 | 50-100字 | 极快 | 碾压快感 |
-| 碾压 | 200-300字 | 快 | 装逼打脸 |
-| 势均力敌 | 500-800字 | 中速 | 绝地反击 |
-| 苦战 | 800-1500字 | 慢 | 爆发逆转 |
-| 大战 | 2000字+ | 多段 | 多重高潮 |
-
----
-
-## 二、节奏变化控制
-
-**快节奏(激烈交锋)**: 短句 + 动作密集 + 快速切换
-```
-萧炎动了。
-一拳。
-王浩后退三步。
-又一拳。
-胸骨碎裂的声音。
-```
-
-**慢节奏(对峙蓄力)**: 长句 + 细节描写 + 心理活动
-```
-萧炎深吸一口气,感受着体内斗气的流转。他知道,接下来这一击,将决定一切。对面的王浩同样在蓄力,空气中弥漫着令人窒息的压迫感。
-```
-
-**节奏变化曲线**:
-```
-慢(对峙)→ 快(交手)→ 慢(喘息)→ 快(爆发)→ 慢(结束)
-```
-
----
-
-## 三、画面切换技巧
-
-| 景别 | 描写重点 | 适用场景 |
-|------|----------|----------|
-| 远景 | 整体战斗画面、战场全貌、势力对比 | 开场、大规模战斗 |
-| 近景 | 详细动作与表情、微表情、肌肉紧绷 | 关键动作、情绪表达 |
-| 特写 | 关键瞬间放大(拳头接触、武器碰撞) | 决定性时刻 |
-
-**切换示例**:
-```
-【远景】两道身影在山巅对峙,狂风呼啸。
-【近景】萧炎的眼中闪过一丝精光,嘴角微微上扬。
-【特写】他的拳头握紧,指节发白。
-【远景】下一瞬,两道身影同时消失,山巅炸开一个巨坑。
-```
-
----
-
-## 四、感官描写
-
-| 感官 | 描写重点 | 示例 |
-|------|----------|------|
-| 视觉 | 动作轨迹、特效光芒、环境变化 | 剑光如龙、火焰升腾 |
-| 听觉 | 拳风呼啸、武器碰撞、骨骼断裂 | "咔嚓"一声脆响 |
-| 触觉 | 拳头冲击、疼痛传递、气浪冲击 | 骨骼碎裂的震动 |
-| 嗅觉 | 血腥味、焦糊味、汗水味 | 空气中弥漫着血腥 |
-
-**综合示例**:
-```
-拳头砸在王浩胸口的瞬间,萧炎感受到了骨骼碎裂的震动【触觉】。
-"咔嚓"一声脆响【听觉】,王浩的身体像断线的风筝般飞出【视觉】。
-空气中弥漫着淡淡的血腥味【嗅觉】。
-```
-
----
-
-## 五、心理描写配合
-
-**战斗心理变化曲线**:
-```
-自信 → 警惕 → 震惊 → 恐惧 → 绝望
-或
-紧张 → 冷静 → 兴奋 → 狂热 → 满足
-```
-
-**内心独白时机**:
-- 关键决策前
-- 发现对手底牌时
-- 使用底牌前
-- 胜负已分时
-
-**心理描写示例**:
-```
-"不可能!"王浩心中大骇,"他明明只是斗者,怎么可能有这种力量?"
-恐惧如潮水般涌来,他第一次后悔招惹了这个"废物"。
-```
-
----
-
-## 六、观众反应(必须有)
-
-通过路人反应烘托主角牛逼,增强爽感:
-
-```
-全场震惊。
-"这...这怎么可能!"
-"他竟然一招就..."
-众人面面相觑,眼中满是不可置信。
-```
-
-**观众反应类型**:
-- 震惊型:瞳孔骤缩、下巴掉落
-- 恐惧型:后退、冷汗、腿软
-- 崇拜型:眼睛发亮、欢呼
-- 嫉妒型:脸色铁青、咬牙切齿
-
----
-
-## 七、战斗类型模板
-
-**1v1 决斗**: 对峙 → 试探 → 交锋 → 底牌 → 决胜
-
-**群战混战**: 全景 → 聚焦主角 → 穿插配角 → 关键转折 → 收束
-
-**越级挑战**: 压制 → 挣扎 → 转机 → 爆发 → 逆转
-
-**团队配合**: 分工 → 配合 → 危机 → 默契 → 胜利
-
----
-
-## 八、战斗逻辑自洽
-
-**检查清单**:
-- [ ] 战斗过程符合双方实力设定
-- [ ] 战斗结果合理且有说服力
-- [ ] 战斗细节符合世界观规则
-- [ ] 能力使用符合已建立的限制
-- [ ] 体力/状态消耗合理
-
-**常见问题**:
-- ❌ 越级战斗无合理解释
-- ❌ 能力突然超出设定
-- ❌ 无限体力/无消耗
-- ❌ 对手智商下线
-
-</instructions>
-
-<examples>
-
-<example>
-<input>秒杀战(碾压龙套)</input>
-<output>
-林天出剑。
-快如闪电!
-龙套连反应都来不及,就被一剑斩首。
-鲜血喷涌,人头滚落。
-全场震惊。
-
-**字数**: 50字 | **爽点**: 秒杀快感
-</output>
-</example>
-
-<example>
-<input>势均力敌战(完整五阶段)</input>
-<output>
-**起手(对峙)**:
-"就凭你?"叶良辰冷笑。
-林天不语,拔剑。剑光一闪——
-叶良辰侧身躲过:"速度不错,但这点实力还不够!"
-
-**试探交锋**:
-两人剑气纵横,空中不断碰撞。
-每次碰撞,都爆发出刺耳的金铁交鸣声。
-叶良辰脸色渐渐凝重:"这小子怎么越打越强?"
-
-**危机**:
-"哼,不陪你玩了!焚天诀!"
-赤红火焰从天而降,将林天笼罩。
-"糟了!"林天脸色大变,这招他挡不住!
-
-**爆发**:
-就在这时,吞噬系统突然启动。
-【叮!检测到高能攻击,自动触发吞噬!】
-火焰被神秘力量吸入林天体内,反噬叶良辰!
-"什么?!"叶良辰瞳孔骤缩。
-
-**终结**:
-林天一剑斩出。剑光如龙,洞穿叶良辰胸口。
-"你...怎么可能..."叶良辰不甘倒地。
-</output>
-</example>
-
-<example>
-<input>装逼打脸结尾</input>
-<output>
-林天甩了甩剑,血珠滴落。
-"不堪一击。"
-转身离去,留下一地尸体和震惊的众人。
-</output>
-</example>
-
-</examples>
-
-<errors>
-❌ 流水账("你一剑我一剑,打了很久") → ✅ 五阶段节奏
-❌ 无悬念(明显主角更强) → ✅ 先示弱后爆发
-❌ 无画面切换(全程近景或远景) → ✅ 远近特写交替
-❌ 无感官描写(只写动作) → ✅ 视听触嗅结合
-❌ 无心理描写(只写招式) → ✅ 关键时刻穿插心理
-❌ 无观众反应 → ✅ 通过路人烘托震惊
-❌ 同一招式重复使用 → ✅ 类型轮换
-❌ 战斗结束太突然 → ✅ 有结尾处理(装逼/留悬念/干脆利落)
-</errors>
+本文件内容已迁移到 CSV `场景写法`,运行时通过 `reference_search.py --table 场景写法` 检索(战斗相关行如 SP-001/SP-008/SP-009/SP-031/SP-032/SP-067/SP-068)。
+此处不再维护正文(历史见 git)。

+ 2 - 226
webnovel-writer/skills/webnovel-write/references/writing/dialogue-writing.md

@@ -3,229 +3,5 @@ name: dialogue-writing
 purpose: 对话写作时按需加载
 ---
 
-<context>
-此文件用于对话写作参考。整合潜台词五层分析、对话类型库、节奏控制、对话设计原则等核心技巧。
-</context>
-
-<instructions>
-
-## 一、潜台词五层分析
-
-### 1. 情感潜台词
-隐含的情感和情绪
-```
-表面:"你来了。"
-潜台词:期待已久/冷淡疏离/惊讶不安
-```
-
-### 2. 动机潜台词
-隐含的目的和动机
-```
-表面:"最近过得怎么样?"
-潜台词:试探/关心/找话题/暗示
-```
-
-### 3. 关系潜台词
-隐含的人物关系
-```
-表面:"王总,您好。"
-潜台词:尊敬/讽刺/疏远/客套
-```
-
-### 4. 情节潜台词
-隐含的情节发展
-```
-表面:"这件事,我会处理的。"
-潜台词:即将行动/敷衍/威胁/承诺
-```
-
-### 5. 主题潜台词
-隐含的主题和思想
-```
-表面:"人各有命。"
-潜台词:宿命论/无奈/接受/反讽
-```
-
----
-
-## 二、潜台词类型库
-
-| 类型 | 特征 | 示例 | 潜台词 |
-|------|------|------|--------|
-| 试探型 | 旁敲侧击 | "听说你最近和李总走得很近?" | 试探关系/警告/嫉妒 |
-| 隐瞒型 | 含糊其辞 | "那天晚上?我在家休息。" | 隐瞒行踪/撒谎/回避 |
-| 暗示型 | 话里有话 | "有些事,知道太多不是好事。" | 警告/威胁/保护 |
-| 躲避型 | 转移话题 | "这个...我们改天再说吧。" | 回避/拖延/不想面对 |
-
----
-
-## 三、对话设计原则
-
-### 1. 符合人物设定
-- 语言习惯与身份匹配
-- 用词风格与性格一致
-- 说话方式与背景相符
-
-### 2. 推动情节发展
-- 传递关键信息
-- 制造/升级冲突
-- 揭示/隐藏真相
-
-### 3. 展现人物关系
-- 称呼变化暗示关系
-- 语气变化暗示态度
-- 话题选择暗示亲疏
-
-### 4. 蕴含潜台词
-- 表面意思 ≠ 真实意图
-- 留白让读者思考
-- 通过反应揭示潜台词
-
----
-
-## 四、对话语气设计
-
-| 语气 | 表现 | 潜台词 |
-|------|------|--------|
-| 温柔 | 轻声、慢语 | 关心/安抚/引诱 |
-| 冷淡 | 简短、平淡 | 疏离/不满/警告 |
-| 激动 | 急促、重复 | 愤怒/焦虑/兴奋 |
-| 讽刺 | 反话、夸张 | 不满/嘲笑/攻击 |
-
-**沉默设计**:
-- 沉默的意义:思考/拒绝/震惊/隐忍
-- 打破沉默的时机:关键信息/情绪转折
-- 沉默的描写:动作/表情/环境
-
----
-
-## 五、对话节奏控制
-
-### 快节奏(战斗/紧张场景)
-```
-"杀!"
-"接招!"
-"破!"
-剑光闪过,鲜血飞溅。
-"你……怎么可能……"
-"你太弱了。"
-```
-**特点**: 短句、动作快、不拖泥带水
-
-### 慢节奏(日常/铺垫场景)
-```
-"师兄,你说这次秘境开启,会不会很危险?"
-林天沉思片刻:"难说。根据古籍记载,上一次秘境开启是在三百年前,当时进去的一百多人,活着出来的不到十个。"
-"那……我们还要去吗?"
-"去。机缘与危险并存,修仙之路本就如此。"
-```
-**特点**: 长句、信息量大、有思考停顿
-
----
-
-## 六、对话与动作/心理结合
-
-### 对话与动作结合
-```
-❌ "你好。"他说。
-✅ "你好。"他微微点头,目光却没有离开手中的文件。
-```
-
-### 对话与心理结合
-```
-"没什么。"她说。
-但她攥紧的拳头出卖了她的情绪。
-```
-
-### 对话与环境结合
-```
-"我们...分手吧。"
-窗外的雨越下越大,模糊了玻璃上的倒影。
-```
-
----
-
-## 七、不同阶段对话设计
-
-| 阶段 | 对话特点 | 功能 |
-|------|----------|------|
-| 初识 | 客套/试探 | 建立第一印象、埋下关系伏笔 |
-| 熟悉 | 日常互动 | 展示性格、深化关系 |
-| 冲突 | 争吵/对峙 | 暴露矛盾、推动转折 |
-| 和解 | 解释/道歉 | 化解误会、关系升级 |
-
----
-
-## 八、对话黄金比例
-
-- **对话**: 40-50%(网文主流)
-- **叙述**: 30-40%
-- **心理活动**: 10-20%
-
-**过多对话问题**: 注水,读者会骂"能不能别废话?"
-**过少对话问题**: 全是叙述,缺少画面感
-
-</instructions>
-
-<examples>
-
-<example>
-<input>经典对话模式:针锋相对</input>
-<output>
-A: "你以为你是谁?"
-B: "至少不是你这种人。"
-</output>
-</example>
-
-<example>
-<input>经典对话模式:话中有话</input>
-<output>
-A: "王总最近很忙啊。"
-B: "是啊,忙着处理一些...麻烦。"
-</output>
-</example>
-
-<example>
-<input>经典对话模式:欲言又止</input>
-<output>
-A: "其实我..."
-B: "什么?"
-A: "...没什么。"
-</output>
-</example>
-
-<example>
-<input>经典对话模式:答非所问</input>
-<output>
-A: "你爱我吗?"
-B: "今晚的月色真美。"
-</output>
-</example>
-
-<example>
-<input>高效对话优化</input>
-<output>
-优化前(注水):
-"师兄,你好。""你好。""你今天看起来心情不错。""还行吧。""那就好。""嗯。"
-字数: 54字 | 信息量: 0
-
-优化后:
-"师兄,秘境的事你听说了吗?"
-"嗯,明天出发。"
-"我也想去。"
-"你?"林天挑眉,"筑基期都没到,去送死?"
-字数: 44字 | 信息量: 秘境、时间、实力差距、冲突预兆
-</output>
-</example>
-
-</examples>
-
-<errors>
-❌ 信息倾倒("你知道吗,我是三年前从北京来的,当时因为父亲生病...") → ✅ 信息分散埋入
-❌ 千人一面(所有角色说话方式相同) → ✅ 语言个性化
-❌ 无效对话("你好。""你好。""今天天气不错。""是啊。") → ✅ 每句话都有功能
-❌ 过度解释("我这样做是因为..."直接说出动机) → ✅ 用潜台词表达
-❌ "said"病(他说、她说、他又说) → ✅ 用动作/表情替代标签
-❌ 无意义客套("不好意思打扰一下""没关系,请说") → ✅ 直接进入正题
-❌ 自问自答独角戏 → ✅ 压缩为简短内心独白或通过行动表现
-</errors>
+本文件内容已迁移到 CSV `写作技法`,运行时通过 `reference_search.py --table 写作技法` 检索(对话相关行如 WT-001/WT-005/WT-009/WT-018/WT-047/WT-051/WT-074)。
+此处不再维护正文(历史见 git)。

+ 2 - 260
webnovel-writer/skills/webnovel-write/references/writing/emotion-psychology.md

@@ -3,263 +3,5 @@ name: emotion-psychology
 purpose: 情感与心理描写时按需加载
 ---
 
-<context>
-此文件用于情感与心理描写参考。整合心理层次模型、情绪曲线、内心独白技巧、情感渐进式描写等核心技巧。
-</context>
-
-<instructions>
-
-## 一、心理描写三层次
-
-### 意识层面
-角色能意识到的心理活动
-```
-他知道自己不该这样想,但还是忍不住...
-```
-
-### 潜意识层面
-角色隐约感觉但不愿面对的心理
-```
-她告诉自己只是普通朋友,却总是不自觉地看向他的方向。
-```
-
-### 无意识流露
-角色自己没意识到的心理泄露
-```
-"我没事。"她说,但攥紧的拳头出卖了她。
-```
-
----
-
-## 二、心理描写五技巧
-
-### 1. 直接内心独白
-关键场景的心理展现
-```
-"不可能!"他心中大骇,"他怎么可能有这种实力?"
-```
-**使用原则**: 关键情绪节点用,每段不超过3句话
-
-### 2. 间接心理描写
-通过行为、表情、对话展现
-```
-她的手指无意识地绞着衣角,目光躲闪,不敢与他对视。
-```
-
-### 3. 感官触发心理
-视觉、听觉、触觉、嗅觉触发
-```
-熟悉的香水味飘来,他的心猛地一紧——是她。
-```
-
-### 4. 梦境折射心理
-梦境象征和隐喻
-```
-他又梦到了那个场景,她转身离去,他拼命追却怎么也追不上。
-```
-
-### 5. 自由联想
-思维跳跃展现深层心理
-```
-看着窗外的雨,他想起了那天,想起了她的眼泪,想起了自己的懦弱...
-```
-
----
-
-## 三、情绪变化曲线
-
-### 单一情绪递进
-```
-不安 → 担忧 → 恐惧 → 绝望
-好感 → 心动 → 喜欢 → 深爱
-```
-
-### 情绪转折
-```
-愤怒 → 震惊 → 理解 → 释然
-期待 → 失望 → 愤怒 → 冷漠
-```
-
-### 情绪矛盾
-```
-爱与恨交织
-信任与怀疑并存
-渴望与恐惧同在
-```
-
----
-
-## 四、情绪强度分级
-
-| 级别 | 特点 | 字数 | 描写方式 |
-|------|------|------|----------|
-| 1级 | 日常轻微波动 | 10字 | 一笔带过 |
-| 2级 | 明显情绪 | 30字 | 简单描写 |
-| 3级 | 强烈情绪 | 50字 | 外显+内显结合 |
-| 4级 | 情绪爆发 | 100-200字 | 章节高潮 |
-
-**1级示例**: 林天有些无奈地摇了摇头。
-
-**2级示例**: 林天皱眉,心中有些不悦。这叶良辰也太嚣张了。
-
-**3级示例**:
-```
-林天握紧剑柄,指节泛白。
-(忍!一定要忍!现在还不是时候!)
-他深吸一口气,强压下心中的怒火。
-```
-
-**4级示例**:
-```
-"师尊!!!"
-林天嘶吼,声音撕心裂肺。
-他跪在血泊中,双手颤抖着捧起师尊的尸体。
-泪水止不住地流下。
-为什么……
-为什么自己这么弱?!
-为什么眼睁睁看着师尊死去,却什么都做不了?!
-"我发誓……"林天抬起头,眼中满是血丝,"血神教,我必灭你满门!"
-```
-
----
-
-## 五、内心冲突设计
-
-| 类型 | 说明 | 示例 |
-|------|------|------|
-| 欲望vs道德 | 想要的与应该的冲突 | 想复仇但知道不对 |
-| 理智vs情感 | 理性判断与感性冲动 | 知道该放手但舍不得 |
-| 自我vs他人 | 个人利益与他人利益 | 救自己还是救别人 |
-| 过去vs现在 | 过去经历与当下选择 | 曾经的伤害影响现在 |
-
-**冲突表现**: 犹豫不决 → 反复纠结 → 自我说服 → 最终抉择
-
----
-
-## 六、核心情绪描写模板
-
-### 愤怒(最常用)
-**触发**: 被羞辱、亲人被杀、被背叛
-```
-【外显】林天的脸色阴沉得可怕,眼中仿佛要喷出火来。双拳紧握,身体微微颤抖。
-【内显】(我要杀了他!)
-【行动】"去死!"林天暴起出手,一剑刺出!
-```
-
-### 悲伤(刀子章必备)
-**触发**: 亲人死亡、爱人离去
-```
-【外显】林天跪在地上,肩膀剧烈颤抖。泪水模糊了视线。
-【内显】(师尊……对不起……都是我没用……)
-【转折】他抬起头,眼中悲伤逐渐化为仇恨。"我会为你报仇的……"
-```
-**注意**: 悲伤不能持续太久,要快速转为"复仇决心"
-
-### 恐惧(营造压迫感)
-**触发**: 遇到远超自己的强敌
-```
-【外显】林天额头冷汗直冒,后背发凉。这股气息……太恐怖了!
-【内显】(不行,不能硬拼!必须逃!)
-【行动】他毫不犹豫转身就跑。留得青山在,不怕没柴烧!
-```
-
-### 绝望(极致虐点)
-**触发**: 走投无路、众叛亲离
-```
-【外显】林天无力地坐倒在地,眼神空洞。所有人都背叛了他……
-【内显】(为什么……为什么会变成这样……)
-【转机】就在这时,一个声音响起:"少年,你想变强吗?"
-```
-**注意**: 绝望最多1-2章,是为了衬托后续逆袭
-
----
-
-## 七、情感渐进式描写
-
-### 错误示例(情绪跳跃)
-```
-第10章: 林天心情不错,和师兄开玩笑。
-第11章: 林天得知师尊被杀,愤怒到极点,发誓复仇!
-```
-**问题**: 太突然,没有铺垫
-
-### 正确示例(渐进式)
-```
-第10章: 林天察觉师尊最近行踪诡异,有些担心。
-第11章: 师尊出门前交代后事,林天心中不安。
-第12章: 林天得知师尊遇险,焦急赶去。
-第13章: 林天赶到时,师尊已死,悲痛+愤怒爆发!
-```
-
----
-
-## 八、心理描写与情节配合
-
-| 阶段 | 心理描写重点 |
-|------|--------------|
-| 铺垫 | 暗示心理状态、埋下心理伏笔 |
-| 发展 | 心理变化递进、冲突逐步升级 |
-| 高潮 | 心理爆发、情感宣泄、关键抉择 |
-| 收尾 | 心理沉淀、情感升华、成长确认 |
-
-</instructions>
-
-<examples>
-
-<example>
-<input>紧张情绪</input>
-<output>
-他的心跳越来越快,几乎要从胸腔里跳出来。手心全是汗,握着的笔差点滑落。
-"冷静,冷静..."他在心里默念,但颤抖的手指出卖了他。
-</output>
-</example>
-
-<example>
-<input>心动情绪</input>
-<output>
-她的心漏跳了一拍。
-明明只是普通的一句话,却让她的耳朵瞬间发烫。她低下头,假装整理衣角,不敢让他看到自己发红的脸。
-</output>
-</example>
-
-<example>
-<input>愤怒情绪</input>
-<output>
-血液涌上头顶,太阳穴突突直跳。
-他的拳头握得咯咯作响,指甲几乎嵌进肉里。但他忍住了,只是用尽全力挤出一个字:"滚。"
-</output>
-</example>
-
-<example>
-<input>绝望情绪</input>
-<output>
-像是被抽空了所有力气,她靠在墙上,缓缓滑坐在地。
-眼泪无声地流下,她甚至没有力气哭出声。
-原来,心真的会痛。
-</output>
-</example>
-
-<example>
-<input>复杂情绪混合(复仇成功后)</input>
-<output>
-一剑刺穿血神教主的心脏。
-"师尊……我为你报仇了……"
-林天喃喃自语,脸上却没有任何喜悦。
-只有……空虚。
-仇是报了,但师尊已经回不来了。
-他跪倒在地,泪水滑落。
-
-【情绪混合】: 完成心愿的解脱 + 失去至亲的悲伤 + 空虚感
-</output>
-</example>
-
-</examples>
-
-<errors>
-❌ 过度解释("他很生气,因为她欺骗了他,而他最讨厌被欺骗,因为小时候...") → ✅ 留白让读者感受
-❌ 心理与行为矛盾("她心里很开心,但脸上却是愤怒的表情"无解释) → ✅ 需要合理解释或一致
-❌ 千篇一律(所有角色心理活动方式相同) → ✅ 个性化心理描写
-❌ 脱离人设(冷静理智角色突然情绪化无铺垫) → ✅ 需要合理铺垫
-❌ 过度煽情("他哭了三天三夜") → ✅ 适度克制反而更有力量
-❌ 情绪跳跃(上一章开心,下一章愤怒爆发) → ✅ 渐进式情绪发展
-</errors>
+本文件内容已迁移到 CSV `写作技法`,运行时通过 `reference_search.py --table 写作技法` 检索(情感/心理相关行如 WT-002/WT-006/WT-037/WT-050/WT-068)。
+此处不再维护正文(历史见 git)。

+ 2 - 258
webnovel-writer/skills/webnovel-write/references/writing/scene-description.md

@@ -3,261 +3,5 @@ name: scene-description
 purpose: 场景描写时按需加载
 ---
 
-<context>
-此文件用于场景描写参考。整合五感层次、感官交织方法、氛围营造、动静结合等核心技巧。网文场景描写要"适度"——太少则无画面感,太多则拖节奏。
-</context>
-
-<instructions>
-
-## 一、五感层次
-
-### 视觉(最重要)
-- 动作:轨迹、速度、力度
-- 特效:光芒、色彩、形态
-- 环境:变化、破坏、氛围
-- 表情:微表情、眼神、姿态
-
-### 听觉
-- 声音:对话、呼吸、心跳
-- 音效:碰撞、破碎、风声
-- 沉默:无声的压迫感
-- 语调:语气、节奏、情绪
-
-### 触觉
-- 温度:热、冷、温、凉
-- 力度:轻、重、紧、松
-- 质感:滑、糙、软、硬
-- 动态:静止、移动、颤动
-
-### 嗅觉
-- 体香:标记身份、传递吸引力
-- 气息:呼吸、体温携带的气味
-- 环境:场景气味、氛围营造
-- 变化:从淡到浓暗示情感发展
-
-### 味觉
-- 直接:食物、饮品
-- 隐喻:苦涩、甜蜜、酸楚
-- 情感:用味觉表达情绪
-
----
-
-## 二、感官交织方法
-
-### 并列式
-多感官同时呈现,增强丰富度
-```
-他的手指划过她的脸颊【触觉】,
-带着淡淡的烟草味【嗅觉】,
-她听到自己心跳加速的声音【听觉】。
-```
-
-### 递进式
-感官强度逐步升级,推动情感高潮
-```
-先是若有若无的香气【嗅觉·弱】,
-然后是指尖的轻触【触觉·中】,
-最后是灼热的呼吸喷在耳边【触觉+听觉·强】。
-```
-
-### 对比式
-不同感官形成对比,增强层次感
-```
-外面是刺骨的寒风【触觉·冷】,
-他的怀抱却温暖如春【触觉·暖】。
-```
-
----
-
-## 三、场景类型感官重点
-
-| 场景类型 | 主感官 | 重点 |
-|----------|--------|------|
-| 战斗场景 | 视觉+听觉+触觉 | 动作轨迹、碰撞声、冲击感 |
-| 情感场景 | 触觉+嗅觉+听觉 | 肌肤接触、体香、呼吸心跳 |
-| 环境场景 | 视觉+听觉+嗅觉 | 景色光影、环境音、场景气味 |
-| 美食场景 | 味觉+嗅觉+视觉 | 口感味道、香气热气、色泽摆盘 |
-| 恐怖场景 | 听觉+触觉+视觉 | 诡异声音、阴冷触感、阴暗光影 |
-
----
-
-## 四、感官描写技巧
-
-### 具体化
-```
-❌ 她很香。
-✅ 她身上有淡淡的栀子花香,混着阳光晒过的温暖气息。
-```
-
-### 动态化
-```
-❌ 他的手很温暖。
-✅ 他的指尖划过她的手背,留下一道温热的痕迹。
-```
-
-### 情感化
-```
-❌ 风很冷。
-✅ 寒风像刀子一样割在脸上,和她离去时的眼神一样冰冷。
-```
-
-### 对比化
-```
-她的嘴唇是冰凉的,但吻却灼热得让人窒息。
-```
-
----
-
-## 五、生理反应描写
-
-| 情绪 | 生理反应 |
-|------|----------|
-| 心动 | 心跳漏拍/加速、呼吸急促/屏住、耳朵发热/脸红、手心出汗 |
-| 紧张 | 肌肉紧绷、喉咙发干、瞳孔收缩、手指颤抖 |
-| 恐惧 | 汗毛竖起、脊背发凉、腿脚发软、心跳如鼓 |
-| 愤怒 | 血液上涌、太阳穴跳动、拳头握紧、牙关紧咬 |
-
----
-
-## 六、环境氛围营造
-
-### 光影运用
-```
-阳光 → 温暖、希望
-阴影 → 压抑、危险
-月光 → 浪漫、神秘
-烛光 → 暧昧、温馨
-```
-
-### 天气运用
-```
-晴天 → 明朗、积极
-雨天 → 忧郁、转折
-雪天 → 纯净、冷酷
-雾天 → 迷茫、神秘
-```
-
-### 声音运用
-```
-寂静 → 压迫、紧张
-喧嚣 → 混乱、热闹
-自然声 → 平静、治愈
-机械声 → 冷漠、现代
-```
-
----
-
-## 七、场景描写的"度"
-
-| 场景类型 | 字数建议 | 示例关键词 |
-|----------|----------|------------|
-| 首次进入新地图 | 50-150字 | 核心特征3-5句 |
-| 营造特殊氛围 | 30-80字 | 恐怖/紧张/温馨 |
-| 战斗场景 | 几乎不描写 | 除非环境影响战斗 |
-| 过渡场景 | 10-30字 | 简单交代 |
-
-**场景类型速查表**:
-
-| 类型 | 描写重点 | 关键词 |
-|------|----------|--------|
-| 宗门/学院 | 建筑、人员、氛围 | 山峰、宫殿、弟子、灵气 |
-| 秘境/遗迹 | 神秘、危险 | 石门、符文、禁制、机关 |
-| 战场 | 混乱、血腥 | 尸体、鲜血、喊杀声 |
-| 集市 | 热闹、生活气息 | 人潮、叫卖、店铺 |
-| 密室/山洞 | 压抑、幽闭 | 昏暗、潮湿、阴冷 |
-| 皇宫/豪宅 | 奢华、权力 | 金碧辉煌、守卫、珍宝 |
-
----
-
-## 八、动静结合
-
-### 静态描写(枯燥)
-```
-山峰很高,树很绿,天很蓝。
-```
-
-### 动态描写(生动)
-```
-山峰直插云霄,山风呼啸而过,卷起漫天落叶。树林深处传来兽吼,几只灵鸟惊飞而起,翅膀扇动间,羽毛飘落如雪。
-```
-
-**技巧**: 加入动词(呼啸、卷起、传来、飞起、扇动、飘落)
-
----
-
-## 九、人物与场景互动
-
-### 错误示例(分离)
-```
-【场景】房间很暗,只有一根蜡烛。
-【人物】林天走了进来。
-```
-
-### 正确示例(融合)
-```
-林天推开门,房间内昏暗一片,唯有桌上一根蜡烛摇曳着微弱的光。
-他眯起眼睛,借着烛光扫视四周——破旧的桌椅、落满灰尘的书架、角落里的蜘蛛网……
-"好久没人住了。"他皱眉。
-```
-
-</instructions>
-
-<examples>
-
-<example>
-<input>仙境/圣地模板</input>
-<output>
-【视觉】云雾缭绕,仙鹤飞舞,宫殿金碧辉煌
-【听觉】仙乐缥缈,泉水叮咚
-【嗅觉】灵气浓郁,沁人心脾
-【触觉】微风拂面,温暖舒适
-【总结】"真是仙境……"
-</output>
-</example>
-
-<example>
-<input>魔域/死地模板</input>
-<output>
-【视觉】天空血红,大地焦黑,白骨遍地
-【听觉】鬼哭狼嚎,风声凄厉
-【嗅觉】血腥味、腐臭味弥漫
-【触觉】阴风刺骨,寒意入髓
-【总结】"这里绝不能久留!"
-</output>
-</example>
-
-<example>
-<input>首次进入新地图</input>
-<output>
-林天踏入天剑宗山门,眼前豁然开朗。
-
-巍峨的山峰直插云霄,山腰处云雾缭绕,隐约可见飞檐斗角的宫殿群。空中不时有修士御剑飞过,留下一道道流光。山脚下,数百名外门弟子正在演武场练剑,剑气纵横,杀意凛然。
-
-"不愧是六大圣地之一……"林天喃喃自语。
-
-【字数】约100字 | 【核心特征】山峰、云雾、宫殿、御剑、剑气
-</output>
-</example>
-
-<example>
-<input>恐怖氛围营造</input>
-<output>
-秘境深处,阴风阵阵。
-
-四周漆黑一片,只有手中的火把勉强照亮三米范围。石壁上爬满了青苔,散发着腐烂的气息。远处传来若有若无的呜咽声,像是女子在哭泣,又像是风声……
-
-林天握紧剑柄,额头冷汗直冒。
-</output>
-</example>
-
-</examples>
-
-<errors>
-❌ 过度描写(流水账列举所有细节) → ✅ 只写核心特征,其余让读者脑补
-❌ 堆砌感官(五感全用且篇幅相当) → ✅ 突出1-2个主感官,其他点缀
-❌ 感官错位("看到"香气,除非通感修辞) → ✅ 感官使用准确
-❌ 过度渲染(连续三段描写同一感官) → ✅ 感官变化,避免读者疲劳
-❌ 脱离情节(大段感官描写与情节无关) → ✅ 感官描写服务于情节氛围
-❌ 场景与人物分离 → ✅ 人物与场景互动融合
-❌ 静态堆砌("很大、很高、很美") → ✅ 动词让场景活起来
-</errors>
+本文件内容已迁移到 CSV `写作技法`,运行时通过 `reference_search.py --table 写作技法` 检索(场景/环境/感官相关行如 WT-003/WT-016/WT-039/WT-046/WT-049)。
+此处不再维护正文(历史见 git)。