diff --git a/docs/2026-09-01-创作链路合同校准提交前四维审查报告.md b/docs/2026-09-01-创作链路合同校准提交前四维审查报告.md
new file mode 100644
index 0000000..fb84f67
--- /dev/null
+++ b/docs/2026-09-01-创作链路合同校准提交前四维审查报告.md
@@ -0,0 +1,44 @@
+# 2026-09-01 创作链路合同校准提交前四维审查报告
+
+verdict: PASS
+
+## 审查范围
+
+- `muse/sot/创作周期与Skill导读.md`
+- `muse/sot/domains/05-创作流程领域.md`
+- `muse/lifecycle/flow/chains/README.md`
+- `muse/content/work/skills/generate/写下一章/SKILL.md`
+- `muse/lifecycle/context/skills/准备任务上下文/SKILL.md`
+- `muse/技能目录.md`
+- 实现补充核对:`muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py`
+- 回显补充核对:`muse/authority/studio/read/server.py`
+- 验证补充核对:`tests/skills/写下一章/test_gate_anchor_projection.py`、`muse/authority/studio/read/test_server_display.py`
+
+排除项:`docs/实现回顾/技能身份中文化迁移实现回顾.md`、`muse/platform/llm/src/muse_llm.py`
+
+## 逻辑完整性
+
+- PASS:阶段 0 现在明确区分“前期多案候选”与“正式 `setting` Shadow”,退出条件不再自相矛盾;SoT 与场景 Skill 已对齐。[muse/sot/domains/05-创作流程领域.md:19](../muse/sot/domains/05-创作流程领域.md#L19) [muse/sot/创作周期与Skill导读.md:121](../muse/sot/创作周期与Skill导读.md#L121)
+- PASS:节点 W 现在明确是“探索 → 回放 → 无工具生成”,不再把“来源优先级固定”误写成“全量预拼后直接喂给生成阶段”。[muse/sot/domains/05-创作流程领域.md:129](../muse/sot/domains/05-创作流程领域.md#L129) [muse/sot/domains/05-创作流程领域.md:173](../muse/sot/domains/05-创作流程领域.md#L173) [muse/content/work/skills/generate/写下一章/SKILL.md:13](../muse/content/work/skills/generate/写下一章/SKILL.md#L13)
+- PASS:生产入口已把作品维度参数化到 CLI、运行编号、授权快照和门锚读取,不再只在入口层硬编码作品 12。[muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py:16](../muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py#L16) [muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py:259](../muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py#L259) [muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py:304](../muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py#L304)
+
+## 一致性
+
+- PASS:`写下一章` 的目录描述、Skill 合同、链路登记和导读都统一为“两阶段写手”,不再同时出现“不得自行检索”和“探索阶段允许白名单只读工具”这组冲突表述。[muse/技能目录.md:63](../muse/技能目录.md#L63) [muse/content/work/skills/generate/写下一章/SKILL.md:1](../muse/content/work/skills/generate/写下一章/SKILL.md#L1) [muse/lifecycle/flow/chains/README.md:19](../muse/lifecycle/flow/chains/README.md#L19)
+- PASS:raw 留痕口径统一为“PostgreSQL raw 表是正式留痕,仓外 raw vault 只是临时暂存或可选备份”,与数据权威域一致。[muse/lifecycle/context/skills/准备任务上下文/SKILL.md:63](../muse/lifecycle/context/skills/准备任务上下文/SKILL.md#L63) [muse/sot/domains/08-数据权威与可视化领域.md:70](../muse/sot/domains/08-数据权威与可视化领域.md#L70)
+- PASS:只读看板的“改:”命令现在跟随当前运行/候选的作品与章节,不再把用户带回作品 12 第 3 章的旧入口口径。[muse/authority/studio/read/server.py:2489](../muse/authority/studio/read/server.py#L2489) [muse/authority/studio/read/test_server_display.py:94](../muse/authority/studio/read/test_server_display.py#L94)
+
+## 合理性
+
+- PASS:`DEFAULT_WORK_ID=12` 保留了既有样本兼容性,但新增 `--work-id` 和 `resolve_gate_requirements()`,避免破坏现有运行习惯,同时把作品切换收敛到显式参数。[muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py:93](../muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py#L93) [muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py:259](../muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py#L259)
+- PASS:门锚仍按作品+章节显式登记,未把“样本常量”直接伪装成“所有作品通用规则”;文档把这一点保留为真实待建项,避免假完成。[muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py:113](../muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py#L113) [muse/sot/domains/05-创作流程领域.md:271](../muse/sot/domains/05-创作流程领域.md#L271) [muse/sot/创作周期与Skill导读.md:662](../muse/sot/创作周期与Skill导读.md#L662)
+
+## 可行性
+
+- PASS:机械验证已覆盖改动落点。`skill_harness --strict` 通过;相关单测 40 条通过;架构测试 `test_author_instructions.py` 5 条、`test_markdown_links.py` 2 条、`test_skills_index.py` 3 条通过;`skills_index.py --check` 通过;`git diff --check` 通过。
+- PASS:新增测试直接覆盖了作品级门锚解析与看板重产命令回填,说明本次修复不是只改文档。[tests/skills/写下一章/test_gate_anchor_projection.py:38](../tests/skills/写下一章/test_gate_anchor_projection.py#L38) [muse/authority/studio/read/test_server_display.py:94](../muse/authority/studio/read/test_server_display.py#L94)
+
+## 残余待建
+
+- 门锚还没有从细纲或正式规划中自动派生,仍需按作品+章节登记;未登记即失败关闭。这是保留的真实边界,不构成阻断本次提交。
+- 章后抽取异步执行、方法 Skill 自动只读检查、结构化 `style` 八字段抽取仍待建,文档已明确保留。
diff --git a/docs/目录.md b/docs/目录.md
index a92f640..89eb9f2 100644
--- a/docs/目录.md
+++ b/docs/目录.md
@@ -11,6 +11,7 @@
| 模块概要 | 暂无 | 某模块一页纸:干什么、入口在哪 | 第一次接触某模块、给别人指路时 | 模块对外形状变了 |
| 实现回顾 | 实现回顾/目录.md | 功能实际怎么落地、和设计有何出入 | 后续改同一功能、交接、对照设计时 | 功能合并后留回顾 |
| 缺陷回顾 | 暂无 | bug 根因、修法、如何防再发 | 同类故障、改相关代码前 | 非平凡 bug 修完留根因时 |
+| 2026-09-01 创作链路合同校准提交前四维审查报告 | 2026-09-01-创作链路合同校准提交前四维审查报告.md | 本次提交前对创作链 SoT/Skill/实现校准差异的四维审查结论与验证证据 | 提交前确认本轮变更是否可交付时 | 只对应本次合同校准提交,不替代长期 SoT |
| acceptance | acceptance/ | | | |
| design | design/ | | | |
| plans | plans/ | | | |
diff --git a/muse/authority/studio/read/server.py b/muse/authority/studio/read/server.py
index 2c6724c..d79bc16 100644
--- a/muse/authority/studio/read/server.py
+++ b/muse/authority/studio/read/server.py
@@ -2486,6 +2486,18 @@ def _run_input_summary_panel(run_id: str) -> str:
def _run_decision_menu_panel(run_id: str, candidates: list, terminal_state, pipeline: dict | None) -> str:
"""决策菜单:有候选按候选态;无候选(失败关闭未落行)只给「改」重产命令。"""
+ def _rewrite_targets():
+ if candidates:
+ latest = candidates[-1]
+ work_id = latest.get("work_id") or latest.get("workId")
+ target_chapter = latest.get("target_chapter") or latest.get("targetChapter")
+ if work_id is not None and target_chapter is not None:
+ return int(work_id), int(target_chapter)
+ matched = re.search(r"run-prod-work(\d+)-ch(\d+)-", run_id)
+ if matched:
+ return int(matched.group(1)), int(matched.group(2))
+ return 12, 3
+
pipe = pipeline or {}
fail = pipe.get("failureCode") or ""
gaps = []
@@ -2501,8 +2513,10 @@ def _run_decision_menu_panel(run_id: str, candidates: list, terminal_state, pipe
gap_hints.append(str(g["query"])[:80])
# 预填改指令:绕开不可证缺口(阶段 0.1 选 A 时用)
avoid = ";".join(f"避开「{h}」" for h in gap_hints[:2]) if gap_hints else "按语义/诊断理由修改"
+ rewrite_work_id, rewrite_target = _rewrite_targets()
rewrite_cmd = (
- f".venv/bin/python muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py 3 "
+ f".venv/bin/python muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py {rewrite_target} "
+ f"--work-id {rewrite_work_id} "
f"--provider
--model "
f"--instruction \"改:{avoid}。禁止细纲原句抄进正文;门符号只在舰队医疗舱语境。\""
)
diff --git a/muse/authority/studio/read/test_server_display.py b/muse/authority/studio/read/test_server_display.py
index af432b6..74c1fc8 100644
--- a/muse/authority/studio/read/test_server_display.py
+++ b/muse/authority/studio/read/test_server_display.py
@@ -91,6 +91,16 @@ class DashboardDisplayTest(unittest.TestCase):
self.assertIn("写手", text)
self.assertIn("第2章《称量》", text)
+ def test决策菜单重产命令跟随当前运行作品和章节(self):
+ html = server._run_decision_menu_panel(
+ "run-prod-work27-ch8-deadbeef",
+ [{"id": 91, "state": "passed", "work_id": 27, "target_chapter": 8}],
+ "completed",
+ None,
+ )
+ self.assertIn("--work-id 27", html)
+ self.assertIn("produce_next_chapter.py 8", html)
+
def test原始_JSON_错误信息可读(self):
text = server._content_text('{"failureType":"InvalidTextRepresentation"}')
self.assertIn('"failureType": "InvalidTextRepresentation"', text)
diff --git a/muse/content/work/skills/generate/写下一章/SKILL.md b/muse/content/work/skills/generate/写下一章/SKILL.md
index 80c46f2..58e0b7b 100644
--- a/muse/content/work/skills/generate/写下一章/SKILL.md
+++ b/muse/content/work/skills/generate/写下一章/SKILL.md
@@ -1,6 +1,6 @@
---
name: 写下一章
-description: 按已确认细纲、冻结事实和动态篇幅合同生成下一整章候选。作品需要续写下一章且输入合同完整时使用;不自行检索、不改规划、不把候选直接写入 Canonical。
+description: 通过“白名单探索→确定性回放→无工具生成”两阶段写手,按已确认细纲、冻结事实和动态篇幅合同生成下一整章候选。作品需要续写下一章且输入合同完整时使用;不改规划、不把候选直接写入 Canonical。
disable-model-invocation: true
---
@@ -13,19 +13,14 @@ disable-model-invocation: true
- **章结构=字段合同**:`candidateBody` 只承载正文,不包含 frontmatter;Canonical 写入层依据 `meta/schemas/chapter.yaml` 与 `scene.yaml` 组装章结构。
- **行为约束=字段值**:文风(style 实例逐条)、人物言行(character.行事逻辑/说话方式/知情范围)、能力边界(power_system.代价限制)、地点规则(location.规则特例)——全部从上下文包字段读,不硬编。
-## 输入(`WriterCreativeInput v2`)
+## 输入(冻结合同与两阶段投影)
-可信 adapter 先校验完整的 `WriterContext v1`,再只通过 stdin 传入创作所需投影 `WriterCreativeInput v2`:
+可信 adapter 先校验完整的 `WriterContext v1`。生产入口不会把完整上下文直接交给生成阶段,而是分两步投影:
-- `fineOutline`:本章细纲硬骨架。
-- `narrativeState`:目标章前的叙事状态。
-- `factConstraints`:由卡索引和原文证据归纳出的事实约束。
-- `proseExcerpts`:用于连续性与叙事声音的历史正文摘录。
-- `patternReferences`:可参考的写作范式——每条给名字(name)、一句话摘要(summary)与写法要点(writingPoints);只供借鉴写法,不是事实约束。
-- `lengthContract`:给写手看的生成篇幅合同;生产续写为 4000–7000 汉字,目标 7000。
-- `styleConstraints`:文风约束;生产组装时还包含 `预防机器味` 生成的有限人感前置约束(规则/声音账指纹只留在完整 WriterContext,不投影给 writer)。
+1. 探索阶段只收到作品、目标章、候选版本、本轮人指令和只读工具白名单;它按需读取细纲、前文、叙事状态、实体事实、文风与已绑定范式,输出 `writer-exploration-manifest-v1`。
+2. 确定性回放按 manifest 重放只读工具,无模型参与;生成阶段收到 `two-phase-generation-v1`:`explorationMaterials`、`lengthContract`、`styleConstraints`、`humanInstruction`,以及仅供参考的探索笔记。正文事实只能来自 `explorationMaterials`。
-Writer 不接收 `runId`、权限信息、manifest、hash、候选版本、验收状态、实验臂、oracle 或目标章之后的内容,也不得自行检索;投影里没有的设定不应被当作已确认事实。
+生成阶段不接收 `runId`、权限信息、manifest、hash、候选版本、验收状态、实验臂、oracle 或目标章之后的内容,也不再调用工具。探索阶段没有读到的设定不得被当作已确认事实。
## 功能约束
@@ -40,7 +35,7 @@ Writer 不接收 `runId`、权限信息、manifest、hash、候选版本、验
Dashboard 与人工生产入口统一调用 `scripts/produce_next_chapter.py`。该入口只负责编排本 Skill 已登记的冻结、检索、writer、detector、CAS、候选落库和人闸步骤;不提供自动 accept。运行 artifacts 仍由只读看板按 run_id 读取。
-- 写作节点的唯一执行形态是两阶段写手(写手合同不变,产出为 `candidateBody` 信封):`produce_next_chapter.py <章> --provider P --model M [--thinking T] [--instruction ...]`。探索阶段写作智能体带只读工具自主取材并产出探索清单;确定性回放按清单重放只读工具(无模型参与)得到写手实际依赖资料;生成阶段写作智能体无工具、单条成稿,生成输入只含回放资料与篇幅/文风合同,不含预组装上下文(`scripts/two_phase_writer.py`)。探索与生成各记独立派发运行,探索摘要落 `artifacts/-exploration-summary.json`;2026-08-23 候选 164 以该形态双门通过并经人采纳入正典。
+- 写作节点的唯一执行形态是两阶段写手(写手合同不变,产出为 `candidateBody` 信封):`produce_next_chapter.py <章> --provider P --model M [--work-id W] [--thinking T] [--instruction ...]`。探索阶段写作智能体带只读工具自主取材并产出探索清单;确定性回放按清单重放只读工具(无模型参与)得到写手实际依赖资料;生成阶段写作智能体无工具、单条成稿,生成输入只含回放资料与篇幅/文风合同,不含预组装上下文(`scripts/two_phase_writer.py`)。探索与生成各记独立派发运行,探索摘要落 `artifacts/-exploration-summary.json`;2026-08-23 候选 164 以该形态双门通过并经人采纳入正典。
- `provider`/`model` 必须显式传入(模型策略归角色合同与调用方,不从环境变量推断);直调链已按 2026-08-23 对照裁决退出创作生成,对照实验走独立入口 `scripts/run_contrast_writer.py`(产 `run_type=eval` 候选,四层机械不可接受)。
- 派发链的模型证据记在派发运行下,生产候选账本以显式 `writer_raw_ref` 关联,不双套记账;一章一组稳定会话(`writer-explore-work-ch` / `writer-gen-work-ch`,复用=继续原写作智能体)。
- 授权继续:管线停在 `AUTHORIZATION_REQUIRED` 时留痕 `artifacts/-authorization-request.json`;人授权后以 `--continue-from <前序 run_id>` 发起新运行,编排方按缺口补证重组上下文后继续。
@@ -63,7 +58,7 @@ Dashboard 与人工生产入口统一调用 `scripts/produce_next_chapter.py`。
## 红线
-不调用工具,不读写文件,不持久化会话;细纲硬骨架不得删除、反转或提前回收;可以设计新设定但不得冒充已确认事实,不得自行给候选生成 hash、版本、验收或审查结论。
+探索阶段只调用任务包声明的只读工具,不直接访问数据库或文件;生成阶段零工具、单条成稿,不读写文件。两个阶段都不得改规划或正式内容;细纲硬骨架不得删除、反转或提前回收;可以设计新设定但不得冒充已确认事实,不得自行给候选生成 hash、版本、验收或审查结论。
## 复利合同
diff --git a/muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py b/muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py
index e744b10..e2ab8ea 100644
--- a/muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py
+++ b/muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py
@@ -14,11 +14,11 @@
→ 停止并展示候选,等待用户明确选择改 / 丢弃 / 采纳
用法:.venv/bin/python muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py [目标章号]
- --provider --model [--thinking T] [--instruction "本轮人指令原文"]
+ --provider --model [--work-id W] [--thinking T] [--instruction "本轮人指令原文"]
写手唯一执行形态是两阶段框架派发(2026-08-23 对照裁决:直调链退出创作生成);
--provider/--model 必须显式给出,不从环境变量推断。
缺省写下一章(库内最大章序 +1)。前置:目标章已建立、存在 confirmed 细纲,且门锚合同
-GATE_ANCHORS 已登记该章。
+已按目标作品与目标章登记。
--instruction:写入本轮输入第 7 项(人指令),经 styleConstraints 注入 writer;
与 05 §2.3 同序拼装合同对齐。正式采纳只由用户明确决定后调用 决定正文候选去留。
"""
@@ -90,7 +90,7 @@ from run_registry import finish_run, start_run # noqa: E402
from check_writer_acceptance import AcceptanceError, check_writer_acceptance # noqa: E402
from acceptance_state import LiveStateError, build_live_acceptance_state # noqa: E402
-WORK_ID = 12
+DEFAULT_WORK_ID = 12
GATE_A_CONFIG = json.loads((REPO_ROOT / "muse" / "lifecycle" / "quality" / "skills" / "replay" / "回放评估正文质量" / "configs" /
"writer-gate-a-deep-space-v1-cn-skills.json").read_text(encoding="utf-8"))
@@ -110,57 +110,60 @@ SYSTEM_PROMPT_SHA256 = "sha256:" + hashlib.sha256(SYSTEM_PROMPT.encode("utf-8"))
ARTIFACTS = REPO_ROOT / "docs" / "write-chapter" / "artifacts"
-# 门锚合同按章登记:锚点是章级创作判断,any-hit 子串匹配。新章必须先登记再跑。
+# 门锚合同按作品+章登记:锚点是章级创作判断,any-hit 子串匹配。新章必须先登记再跑。
# 机械门(check_writer_candidate)只认这些子串,不认语义等价;必须投影给写手,
# 否则写手只见细纲「完成第一次升级」却因禁抄长句而避开「升级」二字 → HARD_EVENT_MISSING。
-GATE_ANCHORS = {
- 2: {
- "requiredEvents": [
- {"requirementId": "event-1-isolation",
- "anchors": ["隔离", "收押", "禁闭", "关押", "封锁"]},
- {"requirementId": "event-2-interrogation",
- "anchors": ["审讯", "审问", "询问", "盘问", "讯问"]},
- {"requirementId": "event-3-conceal",
- "anchors": ["隐瞒", "没有告诉", "没说", "没有说", "咽了回去", "沉默", "闭上嘴"]},
- {"requirementId": "event-4-hunger",
- "anchors": ["饥饿", "渴望", "吞噬", "进食", "吃", "贪"]},
- ],
- "requiredCharacters": ["林深", "何岚"],
- "foreshadowingActions": [
- {"requirementId": "foreshadow-upgrade",
- "anchors": ["异种核心", "融合", "升级"]},
- ],
- "chapterEndHook": {
- "requirementId": "hook-ch2",
- "anchors": ["调令", "实战", "出击", "部署", "任务", "出征", "离不开", "不愿离开"],
- "maxDistanceFromEnd": 900,
+GATE_ANCHORS_BY_WORK = {
+ 12: {
+ 2: {
+ "requiredEvents": [
+ {"requirementId": "event-1-isolation",
+ "anchors": ["隔离", "收押", "禁闭", "关押", "封锁"]},
+ {"requirementId": "event-2-interrogation",
+ "anchors": ["审讯", "审问", "询问", "盘问", "讯问"]},
+ {"requirementId": "event-3-conceal",
+ "anchors": ["隐瞒", "没有告诉", "没说", "没有说", "咽了回去", "沉默", "闭上嘴"]},
+ {"requirementId": "event-4-hunger",
+ "anchors": ["饥饿", "渴望", "吞噬", "进食", "吃", "贪"]},
+ ],
+ "requiredCharacters": ["林深", "何岚"],
+ "foreshadowingActions": [
+ {"requirementId": "foreshadow-upgrade",
+ "anchors": ["异种核心", "融合", "升级"]},
+ ],
+ "chapterEndHook": {
+ "requirementId": "hook-ch2",
+ "anchors": ["调令", "实战", "出击", "部署", "任务", "出征", "离不开", "不愿离开"],
+ "maxDistanceFromEnd": 900,
+ },
},
- },
- 3: {
- # 接第2章结尾硬钩子(茧撕开舱门出击、要吃掉更强核心)。锚点 any-hit 子串匹配;
- # requiredCharacters 只硬约束主角(避免过度约束触发 costly 重抽),其余靠事件锚点。
- "requiredEvents": [
- {"requirementId": "event-1-sortie",
- "anchors": ["出击", "实战", "战斗", "交火", "搏杀", "拦截", "扑向", "战场"]},
- {"requirementId": "event-2-devour",
- "anchors": ["吞噬", "吞食", "吃掉", "进食", "撕碎", "吸收", "吞下", "吞"]},
- {"requirementId": "event-3-upgrade",
- "anchors": ["升级", "蜕变", "进化", "变强", "增强", "新的力量", "蜕变"]},
- {"requirementId": "event-4-pollution",
- "anchors": ["黑纹", "污染", "扩散", "蔓延", "加深", "恶化"]},
- ],
- "requiredCharacters": ["林深"],
- "foreshadowingActions": [
- {"requirementId": "foreshadow-voice-merge",
- "anchors": ["分不清", "像他自己", "脑内的声音", "低语", "渴望", "哪个念头", "另一个"]},
- ],
- "chapterEndHook": {
- "requirementId": "hook-ch3",
- "anchors": ["深渊", "更深", "回应", "召唤", "更大", "下一", "不止", "饥饿", "注视", "凝视"],
- "maxDistanceFromEnd": 900,
+ 3: {
+ # 接第2章结尾硬钩子(茧撕开舱门出击、要吃掉更强核心)。锚点 any-hit 子串匹配;
+ # requiredCharacters 只硬约束主角(避免过度约束触发 costly 重抽),其余靠事件锚点。
+ "requiredEvents": [
+ {"requirementId": "event-1-sortie",
+ "anchors": ["出击", "实战", "战斗", "交火", "搏杀", "拦截", "扑向", "战场"]},
+ {"requirementId": "event-2-devour",
+ "anchors": ["吞噬", "吞食", "吃掉", "进食", "撕碎", "吸收", "吞下", "吞"]},
+ {"requirementId": "event-3-upgrade",
+ "anchors": ["升级", "蜕变", "进化", "变强", "增强", "新的力量", "蜕变"]},
+ {"requirementId": "event-4-pollution",
+ "anchors": ["黑纹", "污染", "扩散", "蔓延", "加深", "恶化"]},
+ ],
+ "requiredCharacters": ["林深"],
+ "foreshadowingActions": [
+ {"requirementId": "foreshadow-voice-merge",
+ "anchors": ["分不清", "像他自己", "脑内的声音", "低语", "渴望", "哪个念头", "另一个"]},
+ ],
+ "chapterEndHook": {
+ "requirementId": "hook-ch3",
+ "anchors": ["深渊", "更深", "回应", "召唤", "更大", "下一", "不止", "饥饿", "注视", "凝视"],
+ "maxDistanceFromEnd": 900,
+ },
},
},
}
+GATE_ANCHORS = GATE_ANCHORS_BY_WORK[DEFAULT_WORK_ID]
def format_mechanical_gate_constraints(requirements: Mapping[str, Any]) -> list[str]:
@@ -253,13 +256,22 @@ class ProductionSemanticRunner:
"modelReceiptSha256": sha256_json(receipt_dict)}
-def resolve_target_chapter(requested: int | None) -> int:
+def resolve_gate_requirements(work_id: int, target_chapter: int) -> Mapping[str, Any]:
+ requirements = (GATE_ANCHORS_BY_WORK.get(work_id) or {}).get(target_chapter)
+ if requirements is None:
+ raise SystemExit(
+ f"作品 {work_id} 第{target_chapter}章门锚合同未登记,先补齐该作品该章锚点再跑。"
+ )
+ return requirements
+
+
+def resolve_target_chapter(requested: int | None, *, work_id: int) -> int:
with connect(readonly=True) as conn:
if requested is not None:
return requested
row = conn.execute(
"SELECT COALESCE(MAX(order_no),0)+1 FROM muse_content_chapter "
- "WHERE work_id=%s AND deleted=false", (WORK_ID,)).fetchone()
+ "WHERE work_id=%s AND deleted=false", (work_id,)).fetchone()
return int(row[0])
@@ -294,21 +306,22 @@ def main():
dispatch_model = _take("--model")
dispatch_thinking = _take("--thinking")
continue_from = _take("--continue-from")
+ work_id_raw = _take("--work-id")
if not dispatch_provider or not dispatch_model:
raise SystemExit("生产写手必须显式给出 --provider 与 --model(两阶段框架派发)")
+ work_id = int(work_id_raw) if work_id_raw is not None else DEFAULT_WORK_ID
args = [arg for arg in argv if not arg.startswith("--")]
- target = resolve_target_chapter(int(args[0]) if args else None)
+ target = resolve_target_chapter(int(args[0]) if args else None, work_id=work_id)
as_of = target - 1
- run_id = f"run-prod-work12-ch{target}-{uuid.uuid4().hex[:8]}"
+ run_id = f"run-prod-work{work_id}-ch{target}-{uuid.uuid4().hex[:8]}"
generated_at = datetime.now(timezone.utc).isoformat()
ARTIFACTS.mkdir(exist_ok=True)
- if target not in GATE_ANCHORS:
- raise SystemExit(f"第{target}章门锚合同未登记(GATE_ANCHORS),先登记锚点再跑。")
+ gate_requirements = resolve_gate_requirements(work_id, target)
# 1) 已确认细纲(read-context 统一消费点)+ 前章全文基线(asOf 起连续四章;不足四章从第1章起)
with connect(readonly=True) as conn:
try:
- fine_outline = load_confirmed_fine_outline(conn, work_id=WORK_ID, target_chapter=target)
+ fine_outline = load_confirmed_fine_outline(conn, work_id=work_id, target_chapter=target)
except RetrievalError as exc:
raise SystemExit(f"{exc}(目标章必须先建立并确认细纲)")
first = max(1, as_of - 3)
@@ -316,15 +329,15 @@ def main():
"SELECT c.order_no, b.id, b.revision, b.content_text FROM muse_content_chapter c "
"JOIN muse_content_block b ON b.chapter_id=c.id AND b.deleted=false "
"WHERE c.work_id=%s AND c.deleted=false AND c.order_no BETWEEN %s AND %s "
- "ORDER BY c.order_no", (WORK_ID, first, as_of)).fetchall()
- style_constraints = load_confirmed_style(conn, work_id=WORK_ID)
- pattern_references = load_confirmed_pattern_bindings(conn, work_id=WORK_ID)
+ "ORDER BY c.order_no", (work_id, first, as_of)).fetchall()
+ style_constraints = load_confirmed_style(conn, work_id=work_id)
+ pattern_references = load_confirmed_pattern_bindings(conn, work_id=work_id)
# 第 7 项人指令:与文风并列注入 styleConstraints,看板/raw 可回看
if human_instruction:
style_constraints = list(style_constraints) + [f"本轮人指令:{human_instruction}"]
print(f"本轮人指令已注入({len(human_instruction)} 字)")
# 机械门锚点必须对写手可见(根因修复:#120 写了升级语义但未命中子串)
- gate_lines = format_mechanical_gate_constraints(GATE_ANCHORS[target])
+ gate_lines = format_mechanical_gate_constraints(gate_requirements)
style_constraints = list(style_constraints) + gate_lines
print(f"机械门锚点已注入写手约束({len(gate_lines)} 条)")
recent_chapters = [{
@@ -345,7 +358,7 @@ def main():
# 1.5) 人感前置预防:规则/声音账先形成合同,再冻结进 WriterContext。
try:
humanization_contract = build_prevention_contract(
- f"work:{WORK_ID}", load_database=True
+ f"work:{work_id}", load_database=True
)
humanization_contract["writer_constraints"] = render_writer_constraints(humanization_contract)
prevention_receipt = persist_prevention(humanization_contract)
@@ -358,7 +371,7 @@ def main():
# 2) 检索计划 + 执行(生产仓储;新书无卡诚实返空)
token_budget = {"maxContextChars": 200000}
plan = build_retrieval_plan(
- run_id=run_id, work_id=WORK_ID, target_chapter=target, as_of=as_of,
+ run_id=run_id, work_id=work_id, target_chapter=target, as_of=as_of,
fine_outline=fine_outline, card_index_version="knowledge-index-v1",
prose_index_version="content-block-v1", token_budget=token_budget)
retrieval = retrieve_writer_sources(
@@ -382,12 +395,12 @@ def main():
"characterPositions": {},
"immediateSituation": "按细纲 chapterGoal 展开(上一章结尾状态见基线正文)。",
}
- authorization_snapshot = {"snapshotId": "auth-work12-production-v1",
+ authorization_snapshot = {"snapshotId": f"auth-work{work_id}-production-v1",
"allowedPurpose": "production_generation",
"verifiedAt": generated_at, "sourceVersion": "v1"}
assembled = assemble_context(
run_id=run_id, attempt=1, mode="production", purpose="production",
- quality_policy_version="writer-production-v1", work_id=WORK_ID,
+ quality_policy_version="writer-production-v1", work_id=work_id,
target_chapter=target, as_of=as_of, source_version="outline@v1",
authorization_snapshot=authorization_snapshot, source_status="active",
retrieval_plan=plan, retrieval_result=retrieval, fine_outline=fine_outline,
@@ -411,7 +424,7 @@ def main():
detector_profile = build_semantic_detector_profile()
semantic_runner = ProductionSemanticRunner(detector_profile, run_id=run_id)
state_store = PostgresCasStateStore(
- work_id=WORK_ID, target_chapter=target, creator="continuation")
+ work_id=work_id, target_chapter=target, creator="continuation")
receipts_by_version: dict[int, Any] = {}
candidates_by_version: dict[int, dict] = {}
contexts_by_attempt: dict[int, dict] = {}
@@ -422,7 +435,7 @@ def main():
"""人感技能 3:每个候选先做只读诊断并自动落质量账;不在这里改正文。"""
deai_artifact = run_diagnosis(
- candidate_body, work_ref=f"work:{WORK_ID}",
+ candidate_body, work_ref=f"work:{work_id}",
chapter_ref=f"chapter:{target}", mode="Audit",
)
_dump(ARTIFACTS / f"{run_id}-ai-flavor-diagnosis-v{candidate_version}.json", deai_artifact)
@@ -488,7 +501,7 @@ def main():
try:
next_ctx = reassemble_writer_context_for_gaps(
- context, gaps, attempt, work_id=WORK_ID)
+ context, gaps, attempt, work_id=work_id)
except EvidenceReassembleError as exc:
raise PipelineError(
"PRODUCTION_EVIDENCE_REASSEMBLE_FAILED",
@@ -508,7 +521,7 @@ def main():
"SELECT COALESCE(MAX(CASE WHEN candidate_version ~ '^[0-9]+$' "
"THEN candidate_version::integer END),0) FROM example_candidate "
"WHERE tenant_id=0 AND work_id=%s AND target_chapter=%s AND deleted=false",
- (WORK_ID, target)).fetchone()
+ (work_id, target)).fetchone()
initial_candidate_version = int(max_version_row[0]) + 1
if continue_from:
@@ -517,7 +530,7 @@ def main():
request = json.loads(request_path.read_text(encoding="utf-8"))
except (OSError, ValueError) as exc:
raise SystemExit(f"授权请求读取失败: {request_path}: {exc}")
- if request.get("workId") != WORK_ID or request.get("targetChapter") != target:
+ if request.get("workId") != work_id or request.get("targetChapter") != target:
raise SystemExit("授权请求与本次作品/章不匹配,拒绝继续")
gaps = request.get("evidenceGaps") or []
if not gaps:
@@ -526,7 +539,7 @@ def main():
print(f"[授权继续] 前序运行 {continue_from}:命中缺口 {len(gaps)} 项,"
f"本次以补证后上下文(attempt={writer_context['attempt']})继续。")
- start_run(run_id=run_id, work_id=WORK_ID, target_chapter=target,
+ start_run(run_id=run_id, work_id=work_id, target_chapter=target,
trigger_detail={"stage": "writer-production-pipeline",
"contextSha256": writer_context["contextSnapshot"]["contextSha256"],
**({"continueFrom": continue_from} if continue_from else {}),
@@ -535,7 +548,7 @@ def main():
try:
pipeline_result = run_writer_pipeline(
context=writer_context,
- requirements=GATE_ANCHORS[target],
+ requirements=gate_requirements,
writer=production_writer,
evidence_provider=production_evidence_provider,
semantic_detector=production_semantic_detector,
@@ -571,7 +584,7 @@ def main():
_dump(ARTIFACTS / f"{run_id}-authorization-request.json", {
"schemaVersion": "authorization-request-v1",
"runId": run_id,
- "workId": WORK_ID,
+ "workId": work_id,
"targetChapter": target,
"humanInstruction": human_instruction,
"evidenceGaps": gaps,
diff --git a/muse/lifecycle/context/skills/准备任务上下文/SKILL.md b/muse/lifecycle/context/skills/准备任务上下文/SKILL.md
index de4bb5d..14c5de4 100644
--- a/muse/lifecycle/context/skills/准备任务上下文/SKILL.md
+++ b/muse/lifecycle/context/skills/准备任务上下文/SKILL.md
@@ -60,12 +60,12 @@ writer 的 `factConstraints=[]` 在冻结读取确实没有可确认事实时合
## 回显与存储
- 安全回显只包含来源 ID/版本、章号、字符数、裁剪原因、状态和 hash,不包含原文、完整 prompt/response 或供应商原始响应。
-- 需要保留的 raw 输入、prompt、response、候选和 evaluator-only oracle 只能进入仓库外受控 raw vault,受显式授权、租约、保留期和清理回执约束。
+- 需要保留的 raw 输入、prompt、response、候选和 evaluator-only oracle 以 PostgreSQL raw 表为正式留痕,走单独表与访问控制。仓外受控 raw vault 只可作为运行期临时暂存或可选备份,须有租约、保留期和清理回执,不得替代库内权威。
- 不生成或读取旧文件式上下文;PostgreSQL Canonical 数据与冻结回执才是运行事实。
## 红线
-- 模型不得自行访问数据库、搜索卡、回读文件或扩张检索计划。
+- 上下文组装器不把数据库、文件系统或检索计划控制权交给模型。获得任务包授权的探索智能体可以调用白名单只读工具按需检索;工具层负责限定表与版本范围、阻断未来信息并把读取写入事件账本。智能体不得绕过工具直接访问数据库或文件,也不得自行扩张白名单。
- 人感合同存在但 work_ref/规则指纹不合法时,组装器失败关闭;缺失时保留规则为空的诚实合同,不得静默退回通用「真人文风」。
- 不得把卡摘要当正文替代品,不得因为有卡减少历史原文检索。
- 不得把完整 WriterContext、raw 路径、授权密钥、真实实验臂或未来信息塞给模型。
diff --git a/muse/lifecycle/flow/chains/README.md b/muse/lifecycle/flow/chains/README.md
index cb5ae24..791e714 100644
--- a/muse/lifecycle/flow/chains/README.md
+++ b/muse/lifecycle/flow/chains/README.md
@@ -16,12 +16,12 @@ agent 的提示词按**变化轴**拆三段,不做"一个 agent 一个大 prom
| scenario | 功能 skill | 槽位/agent | purpose | 保护节点序列(简化) | 状态 |
|---|---|---|---|---|---|
-| continuation 续写 | `写下一章` | 写作→writer | generation | 准备任务上下文→无工具 writer→机械门→语义 detector→Shadow→用户三决策→accept_preflight→正式写入→异步抽取 | 生产链已闭环:机械门+真实语义 detector+持久 CAS 链+accept_preflight+正式写入(write_canonical 单事务,含已批准事实增量与投影登记);语义缺口补证重组装已接(production_evidence_reassemble);章后抽取的异步执行未建(抽取投影只登记 pending) |
+| continuation 续写 | `写下一章` | 写作→writer | generation | 准备任务上下文→writer 白名单探索→确定性回放→无工具生成→AI 味诊断→机械门→语义 detector→Shadow→用户三决策→accept_preflight→正式写入→异步抽取 | 作品 12 第 2/3 章样本链已闭环:两阶段写手+机械门+真实语义 detector+持久 CAS 链+accept_preflight+正式写入(write_canonical 单事务,含已批准事实增量与投影登记);生产入口已支持 `--work-id` 显式指定作品,语义缺口补证重组装已接(production_evidence_reassemble);门锚仍按作品+章节登记,章后抽取异步执行未建(抽取投影只登记 pending) |
| rewrite 改写 | `重写选段` | 写作→writer | generation | 同上+expectedRevision 核对 | 已建 |
| expansion 扩写 | `扩写场景` | 写作→writer | generation | 同 continuation | 已建 |
| polish 润色 | `润色正文` | 写作→writer | generation | 同 continuation(只动表达层) | 已建 |
-| setting_init 作品设定初始化 | `完善故事基础设定` | 规划→planner | planning | 用户冻结根设定与对话→隔离候选→离线机械门→用户选择→交给 planning | 手工前置流程首建;候选不落库、不进 Canonical |
-| planning 规划 | `制定作品规划` | 规划→planner | planning | 准备任务上下文(planning 视图)→槽位→用户确认(决定正文候选去留) | 已建 |
+| setting_init 作品设定初始化 | `完善故事基础设定` | 规划→planner | planning | 用户冻结根设定与对话→隔离候选→离线机械门→用户选择→选定交接→planning 写 setting Shadow | 手工前置流程首建;比较候选不落库、不进生成上下文;选中不等于 confirmed |
+| planning 规划 | `制定作品规划` | 规划→planner | planning | 准备任务上下文(planning 视图)→槽位→persist_planning 写 Shadow→用户确认翻 confirmed | 已建 |
| fine_outline 细纲规划 | `规划下一章` | 规划→planner | planning | 准备任务上下文(fine_outline 冻结视图)→槽位→核对内容一致性→评估内容质量 | 评测版首建(2026-07-19) |
| full_parse 拆书 | `拆解书稿` | 分析→extractor | extraction | 导入书稿 分章→逐章槽位→访问数据库 落 draft→管理员确认(G3 门) | 已建;B2 PG 版首验 |
| ai_flavor_capture AI 味案例回填/反馈 | `记录机器味案例` | 检测→detector | detection | 作品/创作反馈→检测完成自动落库→hash 与位置门→Shadow 案例卡 | 已建;默认写入 muse-example,`--offline` 仅作显式离线回放 |
diff --git a/muse/sot/domains/05-创作流程领域.md b/muse/sot/domains/05-创作流程领域.md
index 1f193a4..e48171b 100644
--- a/muse/sot/domains/05-创作流程领域.md
+++ b/muse/sot/domains/05-创作流程领域.md
@@ -14,21 +14,23 @@
本领域拥有这五阶段的**顺序、进入/退出条件和门禁**,不拥有各阶段产物的字段合同与存储表结构——字段权威在 [`muse/content/meta/schemas/`](../../content/meta/schemas/README.md),存储与确认链分别在 [01-作品领域](01-作品领域.md)、[02-实体领域](02-实体领域.md)、[03-范式领域](03-范式领域.md),本节只引用、不重复定义。
-两条贯穿五阶段的硬合同(均为既有合同,本节只引用):
+贯穿五阶段的硬合同(均为既有合同,本节只引用):
-- **横切总闸**:一切规划候选先落 Shadow,经用户确认翻 Confirmed 后才进入生成上下文(Shadow/Canonical 双轨,见 [架构-02](../../../../design-docs/架构-02-核心数据结构与双轨模型.md))。
+- **横切总闸**:一切正式规划候选先落 Shadow,经用户确认翻 Confirmed 后才进入生成上下文(Shadow/Canonical 双轨,见 [架构-02](../../../../design-docs/架构-02-核心数据结构与双轨模型.md))。
+- **阶段 0 探索例外**:`完善故事基础设定` 产出的多案比较稿属于正式规划之前的仓内探索材料,只留在用户指定的设计文档与候选目录,不进 PostgreSQL,也不得进入生成上下文。用户选定方向并通过 `assert_selection_handoff.py` 后,由 `制定作品规划` 把选定内容重写为 `setting` Shadow;该行经用户确认成为 Confirmed,才算阶段 0 正式退出。候选被选中不等于规划已确认。
- **上层约束下层**:大纲派生卷纲、卷纲派生细纲;下层规划不得与已确认的上层规划相悖,冲突时以下层退回为准。
### 阶段定义
-除特别注明外,各阶段规划产出都经 `persist_planning` 落 `example_planning_section`(默认 `state='shadow'`),用户确认后翻 `confirmed`。
+阶段 0 的多案比较稿按上面的探索例外处理;选定方向的正式 `setting` 与阶段 1–4 的规划产出都经 `persist_planning` 落 `example_planning_section`(默认 `state='shadow'`),用户确认后翻 `confirmed`。
**阶段 0 · 定盘**
- 进入条件:作品已创建(作品行存在,见 [01-作品领域](01-作品领域.md))。
-- 产出:`work_core`(题材定位 / 核心卖点 / 主题立意 / 基调 / 禁区 / 核心悬念 / 谜底与真相 / 结局方向 / 目标读者,字段权威见 `muse/content/meta/schemas/work_core.yaml`)+ `novel_work` 的篇幅目标(目标章数 × 单章字数区间 × 分卷数,见 `muse/content/meta/schemas/novel_work.yaml`)。落 `section_type='setting'`。
+- 探索产出:冻结根设定后生成可比较候选;候选只供用户选方向,不是 Shadow,也不是正式内容。统合多案仍只得到一份待选候选。
+- 正式产出:通过选定交接门后,`制定作品规划` 按 `work_core`(题材定位 / 核心卖点 / 主题立意 / 基调 / 禁区 / 核心悬念 / 谜底与真相 / 结局方向 / 目标读者,字段权威见 `muse/content/meta/schemas/work_core.yaml`)和 `novel_work` 的篇幅目标(目标章数 × 单章字数区间 × 分卷数,见 `muse/content/meta/schemas/novel_work.yaml`)生成 `section_type='setting'` Shadow。
- 机械门禁:**篇幅目标三要素齐全**——当前为文档纪律,待升级为落库校验。
-- 退出条件:`work_core` 九字段与篇幅目标三要素齐备且用户确认(`confirmed`)。
+- 退出条件:用户选定记录通过交接门,且 `work_core` 九字段与篇幅目标三要素齐备的 `setting` 已由用户确认(`confirmed`)。
**阶段 1 · 大纲 / 卷纲**
@@ -124,7 +126,7 @@
人决策才发生内容变化:要不要生成/改一版规划草稿 → 要不要 confirmed
【节点 W · 生成正文】
- 自动:prevent 合同装配(约束投影,不改正文);机械门;语义门;diagnose 报告;
+ 自动:prevent 合同装配(约束投影,不改正文);写手探索与确定性回放;diagnose 报告;机械门;语义门;
方法只读检查报告
人决策才发生内容变化:要不要让写手产/改候选草稿 → 要不要 revise → 要不要 accept 进正式正文
@@ -168,7 +170,7 @@
### 2.3 交互回合合同(节点 W)
-每一轮输入拼装**顺序固定**;第 2 轮及以后只在第 7 项写入你的上一轮原话,1–6 与第 1 轮同序。
+每一轮的**来源优先级与交互位置固定**;这不是把 1–6 全量拼成一包直接塞给生成阶段。探索阶段按任务包白名单读取所需材料并产出清单,确定性回放按清单重取材料,生成阶段只接收回放材料、篇幅合同、文风约束与本轮人指令。第 2 轮及以后只替换第 7 项为你的上一轮原话,1–6 的来源范围和优先级不变。
| 序 | 输入项 | 说明 |
|---|---|---|
@@ -177,7 +179,7 @@
| 3 | 前章正式正文基线 | 连续 as_of 起若干章 |
| 4 | 文风 + 已绑定范式 | 有则注入 |
| 5 | prevent 约束投影 | 不改正文 |
-| 6 | 方法合同(只读) | 装载 + 检查报告;待建 |
+| 6 | 方法合同(只读) | 允许探索阶段按需装载;自动检查报告待建 |
| 7 | **本轮人指令** | 第 1 轮可为空(「写第 N 章」);之后为「改:…」原文 |
| 输出块 | 说明 |
@@ -200,7 +202,7 @@
| 节点 | 必用 | 可选(自动报告) | 人授权才用 |
|---|---|---|---|
| P 规划 | `准备任务上下文`、`制定作品规划` / `规划下一章` | 方法只读检查;`核对内容一致性` | 规划草稿生成与 `confirmed` |
-| W 正文 | `预防机器味`→`准备任务上下文`→`写下一章`→机械门→语义→`诊断机器味`→Shadow | 方法只读检查;`评估内容质量`(生产待建) | 「写/改」产候选;`修正正文机器味` / `润色正文` / `重写选段`;`决定正文候选去留` |
+| W 正文 | `预防机器味`→`准备任务上下文`→写手探索→确定性回放→无工具生成→`诊断机器味`→机械门→语义→Shadow | 方法只读检查;`评估内容质量`(生产待建) | 「写/改」产候选;`修正正文机器味` / `润色正文` / `重写选段`;`决定正文候选去留` |
| A 章后 | (accept 触发)下游草稿:`抽取章节知识`、案例/规则候选登记 | lesson `proposed`;只读汇总 | `确认知识草稿`;规则激活;声音账定版;下一章开写 |
参照方法 Skill(`打磨小说语言` 等)不进 `meta/chains`;挂在 P/W「可选装载」,出卡升公共范式仍走复利批次 3 人闸。
@@ -266,6 +268,8 @@ DRAFT/CHECKING/PASSED -> DISCARDED(用户明确决策)
写手执行的唯一生产形态是两阶段写手(`produce_next_chapter.py <章> --provider P --model M`):探索阶段派发写作智能体(只读工具集)自主取材(细纲、前文、文风、范式、实体)、产出探索清单;确定性回放按清单重放只读工具(无模型参与),得到写手实际依赖的材料;生成阶段无工具派发、单次成稿,生成输入只含回放材料与篇幅/文风合同,不含全量预组装。探索与生成使用独立会话、各记独立派发运行,写手事实只来自它探索到的材料;探索清单与摘要落运行工件,探索轨迹与依赖清单进事件账本,是创作台链路透视的数据源。探索与生成分离的原因:单阶段同会话形态下,长篇生成会被多回合探索挤占输出空间导致正文碎片化(2026-08-23 烟测实证)。该形态已于 2026-08-23 生产烟测双门通过并经人采纳进正典;同源对照实验裁决直调链退出创作生成(见 `07-Agent与Skill领域`)。
+当前入口的覆盖范围仍以验证样本为主:`produce_next_chapter.py` 已支持 `--work-id` 显式指定作品,不再把作品号硬编码在执行入口、运行编号和授权快照里;但门锚仍按作品+章节登记,当前只为作品 12 的第 2、3 章备齐样本锚点。未登记门锚的作品或章节会在模型调用前失败关闭。两阶段编排、状态链和作品参数化已经落地,门锚通用化仍待建。
+
## 6. 运行与落库
- 正式内容和候选都在 PostgreSQL 内;Git 只管代码与文档,不承载正式内容。
diff --git a/muse/sot/创作周期与Skill导读.md b/muse/sot/创作周期与Skill导读.md
index 0849921..e7ec3ce 100644
--- a/muse/sot/创作周期与Skill导读.md
+++ b/muse/sot/创作周期与Skill导读.md
@@ -118,7 +118,7 @@ Skill 还有两个实现形态:
0 1 2 3 4
```
-每步产物先落 Shadow,你确认后才变 Confirmed,才允许进入下一步的生成上下文。下层不得跟已确认的上层打架。
+正式规划产物先落 Shadow,你确认后才变 Confirmed,才允许进入下一步的生成上下文。阶段 0 的多案比较稿是唯一例外:它只供选方向,不入库;选定方向必须经 `制定作品规划` 写成 `setting` Shadow 并确认,才算定盘完成。下层不得跟已确认的上层打架。
### 章级:三个节点
@@ -128,7 +128,7 @@ Skill 还有两个实现形态:
节点 P 规划 → 节点 W 正文 → 节点 A 章后
```
-写第 N 章时围着这三节点转。节点 W 喂给写手的顺序写死:
+写第 N 章时围着这三节点转。节点 W 的来源优先级写死,但当前两阶段写手不会把这些内容全量预拼后直接交给生成阶段:探索阶段按白名单取材,确定性回放重取所选材料,生成阶段只看回放材料与篇幅、文风合同。
1. 库事实(作品 / 目标章 / 冻结点)
2. 已确认细纲
@@ -183,13 +183,13 @@ flowchart TD
### 阶段 0 · 定盘(节点 P)
-**要完成**:这本书是什么、写给谁、禁区在哪、篇幅目标(目标章数 × 单章字数 × 分卷数)。九个核心字段齐,你确认后才能往下。
+**要完成**:先从多案中选定方向,再把这本书是什么、写给谁、禁区在哪、篇幅目标(目标章数 × 单章字数 × 分卷数)写成正式 `setting`。九个核心字段齐且正式规划已确认,才能往下。
功能链:`setting_init` → [完善故事基础设定](../lifecycle/flow/skills/book/完善故事基础设定/SKILL.md) → 规划师。
| Skill | 意图 | 规则 | 工具 / 方法 |
|---|---|---|---|
-| [完善故事基础设定](../lifecycle/flow/skills/book/完善故事基础设定/SKILL.md) | 固化根设定,按固定三级章节树出可比较候选 | 候选不落库、不进 Canonical;你选完才交给规划 | `validate_candidates.py`、`assert_selection_handoff.py`;合同在 `references/candidate-contract.md` |
+| [完善故事基础设定](../lifecycle/flow/skills/book/完善故事基础设定/SKILL.md) | 固化根设定,按固定三级章节树出可比较候选 | 候选不落库、不进生成上下文;选中只完成方向交接,不等于规划确认 | `validate_candidates.py`、`assert_selection_handoff.py`;合同在 `references/candidate-contract.md` |
| [合并故事方案](../lifecycle/flow/skills/book/合并故事方案/SKILL.md) | 多案逐章合成一案 | 一级章节一个新代理;根设定优先 | `serial_merge.py` |
| [提炼故事概念](../../.agent/skills/规划/提炼故事概念/SKILL.md) | 把「我想写 X」升级成能撑长篇的疑问 | idea 是陈述,concept 是疑问;四问(新鲜 / 角度 / 冲突 / 可撑长),可撑长必须过 | 无脚本。见 [方法:概念设计](#提炼故事概念-概念设计) |
@@ -291,7 +291,7 @@ flowchart TD
→ Shadow 候选给你看
```
-当前实现仍走确定性装配链(`预防机器味 → 准备任务上下文 → 写下一章 → 机械门 → 核对内容一致性 → 诊断机器味 → Shadow`),写手无工具、收预组装上下文;智能体化切换按整体收敛总 plan 阶段 E 推进,两条描述以角色合同与 05 领域 §5 为准。
+当前生产实现已经切到两阶段写手:`预防机器味 → 冻结任务边界 → 写手带白名单工具探索 → 确定性回放所选材料 → 写手无工具单次成稿 → 诊断机器味 → 机械门 → 核对内容一致性 → Shadow`。探索与生成各用独立会话并分别留痕;生成阶段不接收全量预组装上下文。当前入口已支持 `--work-id` 显式指定作品,不再把作品号硬编码在执行入口、运行编号和授权快照里;但门锚仍按作品+章节登记,当前只备齐作品 12 的第 2、3 章样本锚点,所以还不是任意作品、任意章节的通用入口。
你说「写第 N 章」= 授权本轮产 Shadow。
你说「改:……」= 原话进入下一轮第 7 项,不入库。
@@ -659,6 +659,7 @@ Gate A/B、A/B/C 三臂、参考书标准答案,只属于离线验收。评测
- 文风八字段的书级抽取尚未建;当前注入的是设定行一句话文风。
- 阶段 0–1 的字数、比例、四要素校验仍是文档纪律。
- 设定全书闭环与设定×章消费台账待建。
+- 生产入口已支持 `--work-id` 显式指定作品;门锚仍只登记作品 12 的第 2、3 章,门锚通用化待建。
---
diff --git a/muse/技能目录.md b/muse/技能目录.md
index 96c0e4d..83abf1c 100644
--- a/muse/技能目录.md
+++ b/muse/技能目录.md
@@ -60,7 +60,7 @@
| 技能名 | 技能文件 | 技能说明 |
|---|---|---|
-| 写下一章 | [SKILL.md](content/work/skills/generate/写下一章/SKILL.md) | 按已确认细纲、冻结事实和动态篇幅合同生成下一整章候选。作品需要续写下一章且输入合同完整时使用;不自行检索、不改规划、不把候选直接写入 Canonical。 |
+| 写下一章 | [SKILL.md](content/work/skills/generate/写下一章/SKILL.md) | 通过“白名单探索→确定性回放→无工具生成”两阶段写手,按已确认细纲、冻结事实和动态篇幅合同生成下一整章候选。作品需要续写下一章且输入合同完整时使用;不改规划、不把候选直接写入 Canonical。 |
| 扩写场景 | [SKILL.md](content/work/skills/generate/扩写场景/SKILL.md) | 在既有场景内补充动作链、对话潜台词、感官细节和环境互动。用户认为某个场景过薄并明确要求扩写时使用;不新开场景、不改落点、不夹带新设定。点名改情节/翻案归 重写选段;只修语病/标点/节奏归 润色正文;AI 味须先 诊断机器味,本槽不得绕过。 |
| 润色正文 | [SKILL.md](content/work/skills/generate/润色正文/SKILL.md) | 修正候选或既有正文的错字、语病、标点、节奏和角色声音指纹(非规则级去 AI 味)。用户只要求改善表达层时使用;不得改变情节事实、场景结构或对白语义。场景加厚归 扩写场景;点名改范围/翻案归 重写选段;AI 味检测与修订归 诊断机器味 → 修正正文机器味,本 Skill 不得自称去 AI 味入口。 |
| 重写选段 | [SKILL.md](content/work/skills/generate/重写选段/SKILL.md) | 按用户意见重写指定段落或场景,并校验范围、事实锚和版本。用户明确点名修改范围时使用;范围外原文不动,不顺手改规划或知识卡。场景过薄只加厚归 扩写场景;只修表达层归 润色正文;AI 味须先 诊断机器味,本槽不得绕过。 |
diff --git a/tests/skills/写下一章/test_gate_anchor_projection.py b/tests/skills/写下一章/test_gate_anchor_projection.py
index b768b8e..7815257 100644
--- a/tests/skills/写下一章/test_gate_anchor_projection.py
+++ b/tests/skills/写下一章/test_gate_anchor_projection.py
@@ -35,6 +35,13 @@ class GateConstraintProjectionTests(unittest.TestCase):
self.assertIn("变强", blob)
self.assertIn("机械门验收", blob)
+ def test_resolve_gate_requirements_by_work_and_chapter(self) -> None:
+ requirements = self.entry.resolve_gate_requirements(12, 3)
+ self.assertEqual(requirements["requiredCharacters"], ["林深"])
+ with self.assertRaises(SystemExit) as ctx:
+ self.entry.resolve_gate_requirements(99, 3)
+ self.assertIn("作品 99 第3章门锚合同未登记", str(ctx.exception))
+
def test_missing_event_group_still_lists_characters(self) -> None:
lines = self.entry.format_mechanical_gate_constraints(
{