Преглед изворни кода

docs: add author-friendly final report contracts

lingfengQAQ пре 2 недеља
родитељ
комит
3142ca3df6

+ 66 - 0
webnovel-writer/scripts/data_modules/tests/test_prompt_integrity.py

@@ -27,6 +27,12 @@ SCRIPTS_DIR = PLUGIN_ROOT / "scripts"
 AGENT_FILES = sorted(AGENTS_DIR.glob("*.md"))
 SKILL_FILES = sorted(SKILLS_DIR.glob("*/SKILL.md"))
 ALL_PROMPT_FILES = AGENT_FILES + SKILL_FILES
+AUTHOR_REPORT_SKILLS = (
+    "webnovel-init",
+    "webnovel-plan",
+    "webnovel-write",
+    "webnovel-review",
+)
 
 # webnovel.py 注册的子命令(从 add_parser 提取)
 REGISTERED_CLI_SUBCOMMANDS = {
@@ -281,6 +287,66 @@ def test_webnovel_write_skill_uses_explicit_agent_invocation_templates():
     assert "不得用主流程口头代替 subagent 输出" in text
 
 
+@pytest.mark.parametrize("skill_name", AUTHOR_REPORT_SKILLS)
+def test_main_skills_define_author_friendly_final_report_contract(skill_name: str):
+    """四个主 Skill 必须提供作者友好的总状态 + 三段式最终报告契约。"""
+    text = _read_text(SKILLS_DIR / skill_name / "SKILL.md")
+
+    assert "作者友好最终报告契约" in text
+    assert "总状态:已完成 / 部分完成 / 需要你处理 / 未完成" in text
+    for section in (
+        "一、产生的文件与完成情况",
+        "二、过程中遇到的问题与异常耗时",
+        "三、下一步建议",
+    ):
+        assert section in text, f"{skill_name}: 缺少最终报告段落 {section}"
+    for issue_type in ("已自动处理", "建议确认", "必须处理"):
+        assert issue_type in text, f"{skill_name}: 缺少异常分类 {issue_type}"
+    assert "任务化语言" in text
+    assert "可复制命令" in text
+    assert "/webnovel-doctor" in text
+    assert "不写 token 统计" in text
+
+
+def test_write_skill_final_report_covers_commit_projection_and_backup():
+    """写章最终报告必须覆盖正文、审查、data artifacts、commit、projection、backup。"""
+    text = _read_text(SKILLS_DIR / "webnovel-write" / "SKILL.md")
+    for required in (
+        "正文文件路径",
+        "审查报告路径",
+        ".webnovel/tmp/review_results.json",
+        ".webnovel/tmp/fulfillment_result.json",
+        ".webnovel/tmp/disambiguation_result.json",
+        ".webnovel/tmp/extraction_result.json",
+        ".story-system/commits/chapter_{NNN}.commit.json",
+        "state / index / summary / memory / vector 更新状态",
+        "备份状态",
+        "是否可以继续写下一章",
+    ):
+        assert required in text
+    assert "chapter-commit rejected" in text
+    assert "最终状态不得写“已完成”" in text
+    assert "--fast" in text and "--minimal" in text
+    assert "projection retry" in text
+
+
+def test_review_skill_final_report_covers_metrics_and_blocking_decision():
+    """审查最终报告必须覆盖报告、metrics、blocking 数与用户裁决状态。"""
+    text = _read_text(SKILLS_DIR / "webnovel-review" / "SKILL.md")
+    for required in (
+        "审查报告文件",
+        ".webnovel/tmp/review_results.json",
+        ".webnovel/tmp/review_metrics.json",
+        "review_metrics",
+        "阻断问题数量",
+        "用户裁决状态",
+        "如果无阻断,明确可以继续写作",
+    ):
+        assert required in text
+    assert "有 blocking 问题且用户未选择处理策略" in text
+    assert "最终状态为“需要你处理”" in text
+
+
 def test_story_system_runtime_contract_commands_exist():
     text = (SKILLS_DIR / "webnovel-write" / "SKILL.md").read_text(encoding="utf-8")
     assert "story-system" in text

+ 40 - 0
webnovel-writer/skills/webnovel-init/SKILL.md

@@ -226,3 +226,43 @@ test "$(basename "${PROJECT_ROOT}")" = "${PROJECT_SLUG}"
 触发:关键文件缺失;总纲关键字段缺失;约束启用但 `idea_bank.json` 缺失或不一致。
 
 恢复:只补缺失字段,不全量重问;只重跑最小步骤(文件缺失→重跑 `webnovel.py init`;总纲缺字段→只 patch 总纲;idea_bank 不一致→只重写该文件);重新验证,全部通过后结束。
+
+## 作者友好最终报告契约
+
+最终回复必须面向作者,不输出原始 JSON、traceback 或长命令日志。使用固定三段式,并以一句总状态开头:
+
+```text
+总状态:已完成 / 部分完成 / 需要你处理 / 未完成。
+
+一、产生的文件与完成情况
+- ...
+
+二、过程中遇到的问题与异常耗时
+- 已自动处理:...
+- 建议确认:...
+- 必须处理:...
+
+三、下一步建议
+- ...
+```
+
+必须汇报:
+- 项目目录、`.webnovel/state.json`、`.webnovel/idea_bank.json`。
+- `设定集/世界观.md`、`设定集/力量体系.md`、`设定集/主角卡.md`、`设定集/反派设计.md`。
+- `大纲/总纲.md`、`.story-system/MASTER_SETTING.json`。
+- 是否使用参考作品拆解;用户确认前未写入 canon 的情况。
+- 缺失信息是否影响后续 `/webnovel-plan`。
+
+异常分类:
+- 已自动处理:脚本补齐目录、重跑最小初始化步骤、重新生成缺失的非内容文件等。
+- 建议确认:参考拆解质量略低、候选创意需用户再看一眼。
+- 必须处理:核心设定未确认、项目目录不安全、关键文件仍缺失。
+
+下一步建议必须使用任务化语言 + 可复制命令,例如:
+
+```text
+- 接下来可以规划第一卷:
+  /webnovel-plan 1
+```
+
+不写 token 统计;如需排查故障,只给日志路径或建议运行 `/webnovel-doctor`。

+ 42 - 0
webnovel-writer/skills/webnovel-plan/SKILL.md

@@ -231,3 +231,45 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" sto
 1. 只重做失败批次,不覆盖整卷文件。
 2. 最后一个批次无效时,只删除并重写该批次。
 3. 仅在全部验证通过后更新状态。
+
+## 作者友好最终报告契约
+
+最终回复必须面向作者,不输出原始 JSON、traceback 或长命令日志。使用固定三段式,并以一句总状态开头:
+
+```text
+总状态:已完成 / 部分完成 / 需要你处理 / 未完成。
+
+一、产生的文件与完成情况
+- ...
+
+二、过程中遇到的问题与异常耗时
+- 已自动处理:...
+- 建议确认:...
+- 必须处理:...
+
+三、下一步建议
+- ...
+```
+
+必须汇报:
+- `大纲/第{volume_id}卷-节拍表.md`。
+- `大纲/第{volume_id}卷-时间线.md`。
+- `大纲/第{volume_id}卷-详细大纲.md`。
+- 新增设定写回了哪些设定集文件。
+- `大纲/第{volume_id}卷-总纲写回.json`。
+- `master-outline-sync`、`update-state`、Story System 合同刷新是否完成。
+- 占位符、时间线、节点承接是否通过。
+
+异常分类:
+- 已自动处理:只重做失败批次、补齐非阻断占位、重跑合同刷新。
+- 建议确认:新增角色名、势力名、卷末钩子需要作者看一眼。
+- 必须处理:总纲 / 设定冲突、时间线回跳、`BLOCKER` 未裁决、当前章相关占位残留。
+
+下一步建议必须使用任务化语言 + 可复制命令,例如:
+
+```text
+- 接下来可以写第一章:
+  /webnovel-write 1
+```
+
+不写 token 统计;如需排查故障,只给日志路径或建议运行 `/webnovel-doctor`。

+ 47 - 0
webnovel-writer/skills/webnovel-review/SKILL.md

@@ -108,3 +108,50 @@ python "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" update-stat
 3. 审查报告已生成,`review_metrics` 已写入 `index.db`,`review_metrics.json` 已输出。
 4. 审查记录已写入 `.webnovel/state.json` 兼容投影。
 5. 存在阻断问题时,用户已明确选择处理策略。
+
+## 作者友好最终报告契约
+
+最终回复必须面向作者,不输出原始 JSON、traceback 或长命令日志。使用固定三段式,并以一句总状态开头:
+
+```text
+总状态:已完成 / 部分完成 / 需要你处理 / 未完成。
+
+一、产生的文件与完成情况
+- ...
+
+二、过程中遇到的问题与异常耗时
+- 已自动处理:...
+- 建议确认:...
+- 必须处理:...
+
+三、下一步建议
+- ...
+```
+
+必须汇报:
+- 审查报告文件。
+- `.webnovel/tmp/review_results.json`。
+- `.webnovel/tmp/review_metrics.json`。
+- `review_metrics` 是否落库。
+- 阻断问题数量。
+- 用户裁决状态。
+- 如果无阻断,明确可以继续写作。
+
+状态规则:
+- 有 blocking 问题且用户未选择处理策略时,最终状态为“需要你处理”。
+- 只保存报告、稍后处理时,最终状态为“需要你处理”或“部分完成”。
+- reviewer 跳过、失败或输出不完整时,最终状态不得写“已完成”。
+
+异常分类:
+- 已自动处理:重复生成报告、覆盖本次旧审查中间文件、成功补写 metrics。
+- 建议确认:非阻断但高收益修改建议、命名或设定细节建议看一眼。
+- 必须处理:blocking issue、缺待审正文、reviewer 输出不完整、metrics 落库失败。
+
+下一步建议必须使用任务化语言 + 可复制命令,例如:
+
+```text
+- 审查无阻断,可以继续写下一章:
+  /webnovel-write {next_chapter}
+```
+
+不写 token 统计;如需排查故障,只给日志路径或建议运行 `/webnovel-doctor`。

+ 50 - 0
webnovel-writer/skills/webnovel-write/SKILL.md

@@ -236,3 +236,53 @@ python -X utf8 "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" bac
 ## 失败恢复
 
 审查缺失→重跑 Step 3。摘要/状态/记忆缺失→重跑 Step 5。润色失真→回 Step 4 修复后重跑 Step 5。
+
+## 作者友好最终报告契约
+
+最终回复必须面向作者,不输出原始 JSON、traceback 或长命令日志。使用固定三段式,并以一句总状态开头:
+
+```text
+总状态:已完成 / 部分完成 / 需要你处理 / 未完成。
+
+一、产生的文件与完成情况
+- ...
+
+二、过程中遇到的问题与异常耗时
+- 已自动处理:...
+- 建议确认:...
+- 必须处理:...
+
+三、下一步建议
+- ...
+```
+
+必须汇报:
+- 正文文件路径。
+- 审查报告路径。
+- `.webnovel/tmp/review_results.json`。
+- `.webnovel/tmp/fulfillment_result.json`。
+- `.webnovel/tmp/disambiguation_result.json`。
+- `.webnovel/tmp/extraction_result.json`。
+- `.story-system/commits/chapter_{NNN}.commit.json`。
+- state / index / summary / memory / vector 更新状态。
+- 备份状态。
+- 是否可以继续写下一章。
+
+状态规则:
+- `chapter-commit rejected`、任一 `write-gate` failed、projection failed 时,最终状态不得写“已完成”。
+- `--fast` 和 `--minimal` 的跳过项必须说明;`--minimal` 跳过审查时归入“已自动处理”或“建议确认”,不得假装已完成完整审查。
+- projection retry 发生时必须说明已自动处理和最终结果。
+
+异常分类:
+- 已自动处理:projection retry 成功、RAG 临时降级但不影响结果、旧 no-review artifact 被本章新 artifact 覆盖。
+- 建议确认:新增角色名 / 设定名、低置信歧义但不阻断、非阻断审查建议。
+- 必须处理:blocking issue 未裁决、data artifacts 缺失或 schema 不完整、commit rejected、projection failed。
+
+下一步建议必须使用任务化语言 + 可复制命令,例如:
+
+```text
+- 接下来可以写下一章:
+  /webnovel-write {next_chapter}
+```
+
+不写 token 统计;如需排查故障,只给日志路径或建议运行 `/webnovel-doctor`。