fix: 校准创作链合同与生产入口

This commit is contained in:
zizi 2026-09-01 17:11:40 +08:00
parent aea6feceb0
commit ca7e2944a2
12 changed files with 194 additions and 105 deletions

View File

@ -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` 八字段抽取仍待建,文档已明确保留。

View File

@ -11,6 +11,7 @@
| 模块概要 | 暂无 | 某模块一页纸:干什么、入口在哪 | 第一次接触某模块、给别人指路时 | 模块对外形状变了 |
| 实现回顾 | 实现回顾/目录.md | 功能实际怎么落地、和设计有何出入 | 后续改同一功能、交接、对照设计时 | 功能合并后留回顾 |
| 缺陷回顾 | 暂无 | bug 根因、修法、如何防再发 | 同类故障、改相关代码前 | 非平凡 bug 修完留根因时 |
| 2026-09-01 创作链路合同校准提交前四维审查报告 | 2026-09-01-创作链路合同校准提交前四维审查报告.md | 本次提交前对创作链 SoT/Skill/实现校准差异的四维审查结论与验证证据 | 提交前确认本轮变更是否可交付时 | 只对应本次合同校准提交,不替代长期 SoT |
| acceptance | acceptance/ | | | |
| design | design/ | | | |
| plans | plans/ | | | |

View File

@ -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 <P> --model <M> "
f"--instruction \"改:{avoid}。禁止细纲原句抄进正文;门符号只在舰队医疗舱语境。\""
)

View File

@ -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)

View File

@ -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/<run_id>-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/<run_id>-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<W>-ch<C>` / `writer-gen-work<W>-ch<C>`,复用=继续原写作智能体)。
- 授权继续:管线停在 `AUTHORIZATION_REQUIRED` 时留痕 `artifacts/<run_id>-authorization-request.json`;人授权后以 `--continue-from <前序 run_id>` 发起新运行,编排方按缺口补证重组上下文后继续。
@ -63,7 +58,7 @@ Dashboard 与人工生产入口统一调用 `scripts/produce_next_chapter.py`。
## 红线
不调用工具,不读写文件,不持久化会话;细纲硬骨架不得删除、反转或提前回收;可以设计新设定但不得冒充已确认事实,不得自行给候选生成 hash、版本、验收或审查结论。
探索阶段只调用任务包声明的只读工具,不直接访问数据库或文件;生成阶段零工具、单条成稿,不读写文件。两个阶段都不得改规划或正式内容;细纲硬骨架不得删除、反转或提前回收;可以设计新设定但不得冒充已确认事实,不得自行给候选生成 hash、版本、验收或审查结论。
## 复利合同

View File

@ -14,11 +14,11 @@
→ 停止并展示候选,等待用户明确选择改 / 丢弃 / 采纳
用法:.venv/bin/python muse/content/work/skills/generate/写下一章/scripts/produce_next_chapter.py [目标章号]
--provider <P> --model <M> [--thinking T] [--instruction "本轮人指令原文"]
--provider <P> --model <M> [--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,

View File

@ -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 路径、授权密钥、真实实验臂或未来信息塞给模型。

View File

@ -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` 仅作显式离线回放 |

View File

@ -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 只管代码与文档,不承载正式内容。

View File

@ -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 章,门锚通用化待建。
---

View File

@ -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 味须先 诊断机器味,本槽不得绕过。 |

View File

@ -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(
{