# KMC 创作管线 generation_config 冗余生成配置覆盖度全量审计（GLM 独立版）

**审计员：GLM（独立完成，未读 Hermes 报告；交叉对比为最后一步）**
**日期：2026-08-16**
**对象：** `skills/kais-movie-pipeline/pipeline/` 的 `generation_config` 统一冗余生成配置系统 vs 21 个 registry phase 的全部生成性资产
**方法：** 21 个 phase 模块逐个实读 + grep 证据 + 行号引用；只读，零代码改动
**工作目录：** `/home/kai/workspace/kais-hermes-skills/skills/kais-movie-pipeline`（与 `/data/...` 同仓双检；行号以该检出的当前工作树为准）

---

## 0. 系统基线（复核 prompt 给定事实）

| 事实 | 复核结果 |
|---|---|
| `_DEFAULT_GENERATION_CONFIG` 在 runner.py:2169 | ✅ 确认，6 组 key：`p01_hook / p03_script / p04_character / p07_scene / p10_voice / p11_video`（runner.py:2220-2240） |
| `_resolve_n_candidates()` 扁平>嵌套>default | ✅ runner.py:2192-2230，优先级与文档一致 |
| 调用点 p04:1005-1007 / p07:620-621 / p10:636-637 / p11b:1881-1882 / p01:425-433 / p03:594-596 | ✅ 全部确认（p01/p03 是手工扁平读，非 `_resolve_n_candidates`） |
| `ALLOW_CANDIDATES_SLOTS` 5 个 slot @ phase_output_schema.py:118-124 | ✅ 确认 |
| PHASE_REGISTRY 21 步 | ✅ phases/__init__.py:179-229（p05 已移除、p12 legacy 未注册、p06_blackboard 为 runtime-swap 默认变体 runner.py:2332-2340 + 272-293 默认 True） |

---

## 1. 覆盖度总表（21 phase × 资产 × 冗余状态 × config 接入 × 严重度）

| # | Phase | 生成性资产 | 冗余机制 | config 接入 | 严重度 |
|---|---|---|---|---|---|
| 1 | p01_hook_topic | hook 候选（LLM 文本） | ✅ Pattern A：K-dispatch + 3-way ensemble 选择器 | ✅ `p01_hook.n_candidates`（手工扁平读 p01:433） | INFO（附 P2：bounds [3,5] 与 N=1 契约冲突，见 C-5） |
| 2 | **p02_outline** | story-framework N-best（LLM 文本） | ✅ Pattern A：ThreadPoolExecutor best-of-N（p02:117-141） | ❌ **零接入**（全文件无 generation_config/episode_config；run() 签名无该参数 p02:70-77） | **P0** |
| 3 | p03_script_audit | 剧本（LLM 文本） | 双分支：Form C 5-agent（默认）/ 单 agent N-best（fallback） | ⚠️ 已接线（p03:594-596）**但默认分支不经过它**（见 A-3） | **P1**（Form C 旁路） |
| 4 | p035_dramatic_polish | 润色剧本（LLM 文本） | ❌ 单 delegate + file-recovery + degrade（p035:498） | ❌ | P2（设计性排除：收敛性任务 + 结构不变量守卫 p035:50-53） |
| 5 | p04_character_design | bible（文本）/ turnaround（图像）/ costume_swap（图像） | Pattern B goal-text（turnaround/costume_swap，p04:1069-1081） | ⚠️ turnaround/costume_swap ✅；**bible 的 n 解析后从未使用**（`p04_bible_n` 仅 L1005 定义处 1 次命中——死配置） | INFO（TR/换装）；**P2**（bible 死 knob） |
| 6 | p06（默认=blackboard） | L1 情绪曲线+场次节拍 / L2 五路 shot_design / L3 合成（LLM 文本） | ❌ 域并行非候选冗余（L2_WRITERS 5 路 p06_blackboard:92-99；serial 变体单 delegate 原子约束 CF-36-03 p06_spatio:137-152） | ❌（run() 无 episode_config 参数） | 设计性排除 |
| 7 | p07_scene_generation | scene_image / multiview（图像） | ✅ Pattern B（p07:641-653） | ✅ 2 子 key（p07:620-621） | INFO |
| 8 | p08_scene_selection | 场景子集选择 + geometry-bed（LLM 决策） | ❌ 单 delegate（p08:280） | ❌ | P2（决策类，无独立判分器；另：模块 docstring 自称"PURE ORCHESTRATION — no LLM"p08:5 与实际 delegate 矛盾——陈旧注释 INFO） |
| 9 | p09_shot_breakdown | shot-list（LLM 大文本） | ❌ 单 delegate（p09:2276；3 专家单原子调用） | ❌（episode_config 仅用于 FORM_FACTOR 注入 p09:1965-1970） | P2（32K token 溢出风险见 `p09-glm-large-output-32k-token-overflow/`；P09b 确定性审计闭环兜底） |
| 10 | p09b_shot_audit | —（确定性审计，无 LLM，p09b:4） | — | — | 设计性排除 |
| 11 | p09c_storyboard_board | 白模分镜板（dreamina 5.0Pro i2i 图像，经 KAP 脚本 subprocess p09c:459-466） | ❌ 无候选机制（grep candidates/variant 零命中；KAP `gen_storyboard_sheets.py:41` MAX_RETRY=3 是网络重试非质量冗余） | ❌ | P2（display-only 层：registry 注释明示"DISPLAY-ONLY — no downstream consumer"；qwen-eye advisory QC 已存在 p09c:505-509） |
| 12 | p10_voice | voice_design（音频 ref）/ tts（音频 clone） | ⚠️ Pattern B 已注入（p10:664-674）**但机制断裂**（见 A-12） | ⚠️ 2 子 key 已接（p10:636-637） | **P1**（voice_design 死机制）/ P2（tts 无选择器） |
| 13 | p10c_voice_audit | —（确定性 WAV 校验，无 LLM，p10c:4） | — | — | 设计性排除 |
| 14 | p11a_preview_clips | 预览视频变体（turbo 引擎，纯 engine 无 delegate，p11a:1386-1389） | ✅ 代码级 fan-out（`_build_variants` + per-shot 循环，n 经 `_resolve_n_candidates` p11a:828-831，asset_type="variants"） | ⚠️ **key 缺席 `_DEFAULT_GENERATION_CONFIG`**，三份 generation-config skill 文档零提及 | P2（功能完整 + 5 个测试钉住 override 路径；纯可发现性/文档缺口） |
| 15 | p11b_final_render | iframe（首尾帧图像）/ video_render（视频） | ⚠️ Pattern B 已注入（p11b:1982-1995）**但 P1-2 引擎化后两端全死**（见 A-15） | ⚠️ 2 子 key 已接（p11b:1881-1882） | **P1**（两个 knob 均无实际效果） |
| 16 | p11c_video_qc | —（qwen-eye 两段式判定：观察层+判定层，evaluator，p11c:11-15） | — | — | 设计性排除（判定器本身不是冗余对象） |
| 17 | p12a_timeline_composition | EDL 剪辑决策（LLM）+ 确定性 concat | ❌ 单 delegate（p12a:311） | ❌ | P2（决策类；切点错误无机器判分器可挑 best-of-N） |
| 18 | **p12b_audio_composition** | BGM + SFX/Foley（生成音频；delegate 带 terminal toolset，专家 skill 指路 SA3/ACE，movie-experts/audio_pipeline/SKILL.md:525-532） | ❌ 无候选机制（grep candidates/variant 零命中） | ❌ | **P1**（BGM 是完播率关键资产；验证失败→整 phase 重跑连带 voice/mix）；附注：goal 文本自认 real BGM API "not yet integrated (deferred)"（p12b:131-132），引擎接入本身半集成 |
| 19 | p13_delivery | 交付包元数据（LLM）+ 确定性 ffmpeg fallback | ❌ 单 delegate（p13:771） | ❌ | 设计性排除（交付非创作采样） |
| 20 | p14_quality_audit | —（规则引擎 + 直连 GLM 审计，无 delegate，p14:1220） | — | — | 设计性排除（审计者须独立于被审者） |
| 21 | p15_feedback | —（反思 writer，从不调 delegate_task，p15:208） | — | — | 设计性排除 |
| — | （registry 外）templates/gen_iframes.py | 首尾帧（dreamina 图像，operator 手动 prep 脚本） | ✅ 自有 `--variants`（gen_iframes:1606-1610）+ director 档自动升 2（gen_iframes:1148-1152） | ❌ **与 generation_config 完全脱钩**（grep generation_config=0） | P2/INFO（文档应显式声明脱钩，见 B） |

**缺口总计：P0×1（P02）+ P1×4（P03 Form C 旁路 / p10 voice_design 死机制 / p11 子树全死 / p12b 无冗余）+ P2×6（p04 bible 死 knob / p10 tts 无选择器 / p11a 默认表缺 key / p09c / p01 bounds 契约冲突 / gen_iframes 脱钩）+ 设计性排除×8 + INFO×3。**

---

## A. 逐 phase 资产盘点（仅展开有结论量的条目）

### A-2. P02 outline —— **P0：完全游离于 generation_config 之外**

证据链：
- `p02_outline.py:51`：`_N_CANDIDATES = int(os.environ.get("KAIS_PIPELINE_N_CANDIDATES", "3"))` —— import 时读 env，无任何配置层参与。
- `p02_outline.py:117-141`：`_N_CANDIDATES > 1` 时 ThreadPoolExecutor N-best fan-out + `_select_best_story_framework` 打分选优。
- 全文件 grep `generation_config` / `episode_config`：**零命中**；`run()` 签名（p02:70-77）**没有 `episode_config` 参数** —— runner 的内省注入（runner.py:2364 `_episode_config_kwargs`）即使想注入也无门。
- 对照 P03：`p03_script_audit.py:594-596` 优先读 `generation_config["p03_script"]["n_candidates"]`，回退 `_N_CANDIDATES`。

四点危害：
1. **省钱配置失灵**：operator 在 requirement.json 写 `{"p03_script": {"n_candidates": 1}}` 时 P03 收敛为单发，**P02 仍默认 3 路 GLM 并发**——统一配置层对一个真实的 3× LLM fan-out phase 完全不可见。
2. **env var 语义纠缠**：`KAIS_PIPELINE_N_CANDIDATES` 被 P02（p02:51 直读）与 P03（p03:58 直读 + runner.py:2332-2338 转写进 config）双消费，无法只关一个。
3. **文档与行为不一致**：`generation-config-asset-type-architecture/SKILL.md:107` 明示该 env var 只映射 `p03_script.n_candidates`；`kais-movie-center-dev/SKILL.md:77` 又教 operator 用它"disable N-best"（实际同时关 P02+P03）。
4. **诊断假象**：日志见 P02 跑 3 次 delegate 会被误判为配置未生效。

`story-framework` 不在 `ALLOW_CANDIDATES_SLOTS`（phase_output_schema.py:118-124）**今天不构成问题**：P02 只落最优单条 dict（p02:140-142 + p02:153 `asset_bus_write("story-framework", expert_output)`），从不落 candidates wrapper；Layer4 校验（phase_output_schema.py:354-360）只要求 `story_kernel` 字段。仅当未来想让 P02 落全候选时才需同步加白名单 + 改 schema。

### A-3. P03 script —— **P1：默认 Form C 分支整体旁路配置读取**

这是本次审计的新发现（Hermes 疑点清单与初步报告均未覆盖）：
- `p03_script_audit.py:64-70`：`_USE_FORM_C` **默认 True**（KAIS_FORM_C=0 才关）。
- `p03_script_audit.py:440-461`：`if _USE_FORM_C:` → `run_form_c(...)` → 写 slot → manifest → gate → **`return result`（p03:581）**。
- `p03_script_audit.py:594-596` 的 `generation_config["p03_script"]["n_candidates"]` 读取位于 **Form C return 之后**的单 agent 分支。

结论：**生产默认路径（Form C）下，`p03_script.n_candidates`（含 `_DEFAULT_GENERATION_CONFIG` 里的默认 3 和 env var 转写）从不执行任何代码**。N-best fan-out（p03:613-641）只在 KAIS_FORM_C=0 的 fallback 分支活着。Form C 的对抗式 5-agent 是"迭代精修"范式而非 best-of-N 采样，不可互相替代——但这不改变"配置系统宣称可控、实际默认不可控"的事实。文档 `generation-config-system/SKILL.md:65`（"P03 … Code-level ThreadPoolExecutor, highest audit score wins"）描述的是非默认分支。

### A-5. P04 —— turnaround/costume_swap 真接入；**bible 是死配置**

- 接入：p04:1004-1007 三路 `_resolve_n_candidates`；n>1 时 goal 追加指令（p04:1069-1081）。goal 内含真实 bash 批量生成通道（dreamina_gen_image.py batch），子代理可机械执行——Pattern B 在 P04 **功能成立**。
- **死 knob**：`p04_bible_n`（p04:1005）解析后**全文件再无一处使用**（grep 仅定义 1 次命中）——goal 没有任何 bible 级 n>1 指令。operator 配 `p04_character.bible.n_candidates=3` 会静默无效。文档表格却把 bible 列为 12 资产之一（generation-config-system/SKILL.md:67）。价值上 bible 多版本本就低价值（锚定资产，Hermes D.3 同判），故 P2：建议要么实现要么从文档/默认表移除。

### A-12. P10 voice —— **P1：voice_design 候选机制被去重逻辑杀死；tts 无宿主侧选择器**

P1-3 引擎化（"DO NOT run curl or call the TTS API yourself — the pipeline runs the Qwen3-TTS engine in-process from your plan"，p10:643）之后，Pattern B 的两句注入指令（p10:664-674）与宿主合成循环产生了断裂：

1. **voice_design n>1 机械性死亡**：`_run_synthesis` Phase 1 循环 `for vd in voice_designs: ... if not char or char in ref_map: continue`（p10:370-379）——ref_map 按角色去重，同一角色的第 2..N 个候选 entry **直接 continue 跳过**，只有第一个被 design_voice 合成。子代理就算按指令给每角色写 N 个候选，宿主也只合成 1 个。指令"从中选择音色匹配度最高的一个"更无从谈起（子代理听不到音频）。
2. **tts n>1 半残**：子代理理论上可在 plan 里给每句对白写 N 条 voice_clips entry（不同 emotion 参数），宿主 `clone_voice` 会逐条合成（p10:480-487）；但**没有任何宿主侧选择器**，下游全部 first-wins / 全量混入：p11b `voice_clips_by_shot.setdefault`（p11b:1866-1870）第一条胜出，p12b `_assemble_voice_track` 的 list-based `tts_by_shot` 会把 N 条全部摆上时间线互相重叠。多候选反而制造脏数据。

### A-15. P11b —— **P1：iframe 与 video_render 两个 knob 在 P1-2 引擎化后全部失效**

这是本次审计最重要的新发现。三层证据：

1. **goal 自相矛盾**：`p11_iframe_n>1` / `p11_video_render_n>1` 的注入指令（p11b:1982-1995）要求子代理"生成 N 组首尾帧候选 / N 条视频候选（filenamePrefix 加 _v1/_v2/...）"；但同一 goal 的 execution_hint 明文规定（p11b:2006-2016）："**RENDER IS NOW IN-PROCESS (P1-2 engine-adapter) … Do NOT write or run a render_S*.py wrapper, and do NOT shell out to curl/ffmpeg to render the shot … YOUR JOB IS CREATIVE + METADATA ONLY**"。子代理被同时要求做和禁止做同一件事。
2. **宿主侧单次渲染**：`_run_engine_render`（p11b:2178-2247）每 shot 恰好调用一次 `engine.render(...)`（p11b:2218-2226），输出路径写死 `assets/P11/{shot_id}.mp4`，**没有变体循环、没有 _v{n} 后缀、没有 seed 变化**。N 条视频候选在代码路径上不存在。
3. **first_frame 从不进引擎**：`engine.render` 签名支持 `first_frame`（plugins/kais_aigc/video_engine.py:203-215），但 p11b 调用处**不传该参数**；iframe manifest entry 只喂给 `_h3_enhance_prompt`（决定 FRAME-0 ANCHOR 块，p11b:2214-2216）和元数据回填（p11b:2400-2405）。H3 ref2va 链路用 turnaround+scene 参考图（"H3 不用首尾帧条件…has_frame_pair 始终为 False"，p11b:1958-1961）。
4. **首尾帧的真实生产者是 operator 手动跑的 gen_iframes.py**：pipeline/ 内 grep 其调用为零；它是"P11 prep"外部脚本（kai-frame-workflow-two-phase/SKILL.md:96-99），读不到 generation_config（grep=0）。

结论：`p11_video.iframe.n_candidates` 与 `p11_video.video_render.n_candidates` 是 LTX 首尾帧时代的遗产开关——文档（generation-config-system/SKILL.md:71-72）与 `_DEFAULT_GENERATION_CONFIG`（runner.py:2236-2240）都在宣称它们可用，实际两端（子代理/宿主引擎）都无法兑现。operator 设 >1 得到的是零行为变化。

### A-18. P12b —— P1：BGM/SFX 无冗余控制（附引擎半集成现状）

- 全文件 grep candidates/variant：**零命中**（:928 附近唯一 "variant" 字样是 FORM-01 注释）。
- 单 delegate（p12b:1002，toolsets 含 terminal）；BGM/SFX 的真实生成通道在子代理加载的专家 skill 里（movie-experts/audio_pipeline/SKILL.md:525-532：ACE-Step 1.5 `POST /api/v1/ace/generate` + Stable Audio 3 `POST /api/v1/stableaudio/generate`），不在 goal 文本里。
- **重要限定**：p12b 的 goal 指令自己承认（p12b:131-132）"Real BGM from Suno/Udio/Mubert API is ALLOWED but **not yet integrated (deferred)**…mark audio_stems.bgm.path=null with reason='no real BGM API integrated yet'"——BGM 引擎接入本身处于半集成状态，冗余控制的前提（稳定真引擎）尚未完全成立。审计判定维持 P1（缺口真实、价值真实：BGM 显著影响完播率，bgm_verify 6 项检查在 P14 侧兜底 p14:749-823，验证失败即整 phase 重跑连带 voice/sfx/mix），但修复应排在"真 BGM 引擎接入收口"之后或同批。
- SFX 兜底脚本 `scripts/sfx_from_shotlist.py` 是 numpy 合成 fallback（R11 边界内的应急路径），同样无候选。

### A-11. P09c —— 事实确认但降级为 P2

- 无候选机制：确认（phase 侧 grep 零命中；重试在网络层——KAP `gen_storyboard_sheets.py:41` `MAX_RETRY = 3`，注意 Hermes 引用的 "p09c:57-58" 实为 `_GEN_TIMEOUT` 注释行，MAX_RETRY 在 KAP 仓脚本里）。
- **降级理由**（Hermes 定 P1，本审计定 P2）：
  1. registry 注释明示 **DISPLAY-ONLY — no downstream consumer**（phases/__init__.py p09c 注册块），坏图不污染主线，只影响 operator 看板；
  2. 全程 degrade-tolerant never-raises（p09c:8-11）；
  3. Hermes 标"待验证"的 qwen-eye 审核兜底**实际存在**：`storyboard-qc` advisory QC（p09c:505-509，panels non-blank / pose legible / scene match）；
  4. KAP 脚本带 per-sheet `source_hash` 幂等，重跑只补缺。

### A-14. P11a —— 事实确认但降级为 P2

- 半接入事实确认：`_resolve_n_candidates(episode_config, "p11a_preview", "variants", VARIANTS_PER_SHOT)`（p11a:828-831）；`_DEFAULT_GENERATION_CONFIG` 无该 key（runner.py:2220-2240 全列）；三份 generation-config skill 文档 grep `p11a_preview` 零命中。
- **降级理由**（Hermes 定 P1，本审计定 P2）：这是**纯可发现性缺口，不是功能缺口**——
  1. `_resolve_n_candidates` 在 key 缺席时正确返回 default=1（VARIANTS_PER_SHOT，2026-08-15 有意从 3 降 1，注释 p11a:116-121 写明恢复路径）；
  2. override 路径被 5 个测试文件钉住（test_p11a_render_prompt_join.py:151-171、test_p10b_unit.py:472-474、test_p10b_jsonl_format.py:189、test_p10b_degrade_warning.py:171、test_p10b_dual_engine_e2e.py:204）；
  3. rapid-preview-clips 是 JSONL slot 且 p11a 在 `_MICRO_GATE_PHASES`（phase_output_schema.py:774-786）——Layer4 整体豁免，无需动 ALLOW_CANDIDATES_SLOTS。
- 附带小疵：同一文件内 key 写法不一致——L827 docstring 说 `["p11a_preview"]["n_candidates"]`（扁平），L120 常量注释说 `["p11a_preview"]["variants"]`（嵌套）；两种都能被 resolver 吃下（扁平优先），但文档应统一。

---

## B. 双轨变体机制冲突排查（prompt 疑点 5）

**问题重述**：P09 `prompt_richness` 三级（lean/standard/director）注释宣称"variants 联动（director 默认 2 变体）"（p09:129-135、p09:2227-2232），消费端是 gen_iframes v2 模板；另一轨是 `p11_video.iframe.n_candidates` 的 goal 注入。两套是否同时生效？谁覆盖谁？会不会出现 variants 数不符合任一配置？

**逐链证据**：
1. P09 侧：`_derive_prompt_richness`（p09:165-228）确定性 Python 给每 shot 打 lean/standard/director 标。
2. gen_iframes 侧：`--variants` CLI 参数默认 1（gen_iframes:1606-1610）；`_collect_gen_tasks` 里 **director 档且 n==1 时强制 `n=2`**（gen_iframes:1148-1152，"关键镜头多一次抽样机会；operator 显式 --variants > 1 时以 operator 值为准（不降级）"）。gen_iframes **不读 generation_config / episode_config**（grep=0）。
3. p11b 侧：`p11_iframe_n > 1` 时向 goal 注入"生成 N 组候选"指令（p11b:1982-1987）——但如 A-15 所证，该指令落在被禁止执行渲染的子代理头上，且宿主引擎从不传 first_frame。

**判定：两轨在代码上永不相遇，不存在"覆盖"关系，也不会产生"数量不符任一配置"的运行态——因为配置轨（p11_video.iframe）根本产生零效果。**
- 唯一真实生效的变体决定因素 = operator 手敲的 `gen_iframes --variants N` + director 自动升 2 规则（后者只在 N==1 时触发，N>1 时以 operator 值为准，gen_iframes:1147-1152 注释明示）。
- 因此这不是"双轨冲突"，而是**"一轨真活（operator 脚本自带的 variants）、一轨全死（generation_config 的 iframe knob）"**。真正的问题是文档性欺骗面：`generation-config-system/SKILL.md:71` 让 operator 相信改 config 能控制首尾帧冗余，实际唯一通道是手动跑脚本时的 CLI flag。
- 顺带排除一个潜在误读：p11b 的 `filenamePrefix 加 _v1/_v2/...` 指令（p11b:1992-1994）与 2026-08-16 定稿的 variant_id 标准（`{base}_v{n}`，commits c204186/f63a318）在命名上兼容，但因渲染在宿主单次执行，该后缀路径同样不触发。

---

## C. 配置系统自身完整性

### C-1. `_DEFAULT_GENERATION_CONFIG` 缺 `p11a_preview` key 的影响
见 A-14：功能无损（resolver default 兜底 + 测试钉住），影响限于 (a) 合并结果 `_DEFAULT | req`（runner.py:2302）里 operator 永远看不到这个 key 的存在；(b) 三份 skill 文档宣称"12 种资产"实为 13 种可配（第 13 种半埋没）；(c) 08-15 降本决策（3→1）的"可恢复"承诺只活在代码注释里。定级 P2。

### C-2. `ALLOW_CANDIDATES_SLOTS` 5 slot vs 实际候选机制——谁会被 Layer4 拒？
先厘清门的真实语义（phase_output_schema.py:248-252）：只拒绝 **dict payload 里出现字面 `"candidates"` key** 且 slot 不在白名单。而 Pattern B 注入指令要求子代理用的是 **`variants` 数组**（p04:1073、p07:646-652、p11b:1985/1994）——`variants` key 不在任何检查之列，天然通过。逐 slot 判定：
- `hook-candidates`：p01 实际写 `{"type","variants","k","selector_state"}`（p01:685-690）用 variants key；且 ASSET_SCHEMA 里 `writer_phase: None`（asset_bus.py:108-114）→ Layer3 跳过，PHASE_OUTPUT_SCHEMA 的 p01 检查只查 topic-kernel/hook-design（phase_output_schema.py:329-345）→ **Layer4 从不校验它**。白名单成员资格运行时惰性。
- `character-assets`/`scene-images`/`script-draft`/`video-clips`：只有当子代理自发用 `"candidates"` 包裹时才需要白名单护体；Pattern B 的 variants 措辞不会触发。
- **P02 `story-framework` 缺位**：非问题（只落最优单条，见 A-2）。
- **P11a `rapid-preview-clips`**：JSONL + `_MICRO_GATE_PHASES` 豁免 Layer4（phase_output_schema.py:774-786），无需入白名单。
- **结论**：n_candidates>1 时**没有任何 slot 会被 Layer4 误拒**。白名单的真实作用仍是 CONTRACT-05 的 LLM 反模式防御（tests/test_schema_forbid_candidates.py:110-135 钉死），phase_output_schema.py:115-117 那段"GENERATION-CONFIG: slots that legitimately carry a candidates/variants wrapper"的注释**夸大了它在冗余生成里的角色**——variants 措辞根本不查。

### C-3. P02/P03 共用 env var 的语义纠缠
确认成立，且方向比"文档不一致"更糟：runner.py:2326-2338 的 env 转写只写 `p03_script`，于是出现三种状态——
| operator 动作 | P02 | P03（Form C 默认） | P03（fallback） |
|---|---|---|---|
| 什么都不设 | 3（模块默认） | **Form C，n 无效** | 3 |
| req.json 写 p03_script.n_candidates=1 | **仍 3** | Form C，无效 | 1 |
| export KAIS_PIPELINE_N_CANDIDATES=1 | 1（import 直读） | Form C，无效 | 1（env>req） |

即"统一配置层"对 P02 是零管辖（模块直读 env），对默认 P03 也是零管辖（Form C 旁路）——env var 反而是唯一能同时触及两者的开关，与 generation-config-architecture 文档宣称的映射关系（该 env→p03_script only）自相矛盾。

### C-4. default 参数值 vs `_DEFAULT_GENERATION_CONFIG` 漂移核查
数值层面**无漂移**：p04/p07/p10/p11b 的 `_resolve_n_candidates(..., default=1)` 与默认表全 1 一致；p11a `default=VARIANTS_PER_SHOT`（=1）一致；p01 fallback `creative_hook_k`（RunnerConfig 默认 3，runner.py:556）与默认表 3 一致；p03 fallback `_N_CANDIDATES`（=3）与默认表 3 一致。但有两点结构性不齐（Hermes 未报）：
1. **p01/p03 未走 `_resolve_n_candidates`**（手工扁平读，p01:433、p03:596）——不支持嵌套格式。单资产 phase 用扁平本就够，但意味着"resolver 统一 API"的宣称（generation-config-system §Resolver API）对这两处不成立。
2. **resolver 调用点计数**：`_resolve_n_candidates` 实际 10 处（p04×3、p07×2、p10×2、p11b×2、p11a×1）+ 手工读 2 处（p01/p03）。Hermes 报"13 个调用点一致"计数有误。

### C-5. 新发现：P01 bounds 与 generation_config 契约冲突
`_DEFAULT_GENERATION_CONFIG` 顶部注释宣称通用契约"1 = 单次生成（当前行为）"（runner.py:2159-2160）。但 p01 读入 config 值后执行硬校验：`if not isinstance(creative_hook_k, int) or not 3 <= creative_hook_k <= 5: raise ValueError`（p01:437-443）。operator 若照通用契约写 `"p01_hook": {"n_candidates": 1}` 省钱 → **ValueError 直接炸 phase**（fail-loud 可诊断，故 P2）。`p01_hook.n_candidates` 的合法域是 [3,5]，与系统级 N≥1 契约不一致，文档未声明。

### C-6. 新发现：三处"接线但无效"的死 knob 汇总
| knob | 死因 | 证据 |
|---|---|---|
| `p04_character.bible.n_candidates` | 解析后从未使用 | p04:1005 唯一命中 |
| `p10_voice.voice_design.n_candidates` | 宿主去重跳过同角色第 2+ 候选 | p10:370-379 `char in ref_map: continue` |
| `p11_video.iframe/video_render.n_candidates` | P1-2 后子代理禁渲染 + 宿主单次 render + 不传 first_frame | p11b:1982-1995 vs 2006-2016、2218-2226 |

加上 A-3 的 p03（默认分支旁路），配置系统 12 个宣称资产里 **5 个在默认生产路径下改了等于没改**。这是比"漏接 3 个 phase"更严重的系统性问题：配置层的行为宣称与执行层的真实能力出现了大面位错配。

---

## D. 修复建议矩阵（只建议，不改码）

| # | 缺口 | 建议 config key | 接入模式 | 工作量 | 风险 |
|---|---|---|---|---|---|
| 1 | P02 游离（P0） | `"p02_outline": {"n_candidates": 3}`（单资产，扁平） | **Pattern A**：run() 增加 `episode_config` kwarg（签名补参，runner 内省自动注入）→ `_resolve_n_candidates(episode_config, "p02_outline", "framework", _N_CANDIDATES)` 以 env 兜底，替换 L117/L122 的 `_N_CANDIDATES` 直读 | S | 低（P03 同款照抄；注意 default 传 `_N_CANDIDATES` 保住单测 monkeypatch 路径；Hermes 草案的 `default=1` 会砍掉 env 兜底，且漏了补签名这步） |
| 2 | p11 子树死 knob（P1） | 二选一：**(a)** 从 `_DEFAULT_GENERATION_CONFIG` + 三份文档移除 `p11_video` 子树，标注"H3 ref2va 时代退役"；**(b)** 真实现：`_run_engine_render` 加 N 次循环（seed 变化 + `_v{n}` 输出）做真 best-of-N | (a) 文档/S；(b) **新 Pattern C：engine-level fan-out**（宿主循环，非 goal 注入） | S / M-L | (a) 零风险；(b) M-L：GPU 成本×N、p11_progress/skip_log 语义需扩展、下游 first-wins 需选优器。**不建议 Hermes 的"goal 里带 --variants"方案**——gen_iframes 是 operator 手动 prep 脚本，不在 p11b 子代理手里，方向性错误 |
| 3 | p10 voice_design 死机制（P1） | 修法同上二选一：文档降级声明，或 `_run_synthesis` Phase 1 支持每角色 N 次 design + 宿主侧选优（如均值音量/时长启发式或 qwen-ear 听感判定） | engine-level（Pattern C） | M | 中：ref 命名/进度文件按 variant 扩展；IndexTTS25 兜底路径需同步 |
| 4 | p03 Form C 旁路（P1） | 文档修正为主：generation-config-system 明示"n_candidates 仅作用于 KAIS_FORM_C=0 分支"；或给 Form C 加 outer best-of-N（每轮完整 5-agent ×N——成本 5N 倍，不建议默认） | 文档 / 可选 Pattern A 外层 | S / L | 文档零风险；外层 fan-out 成本爆炸 |
| 5 | p12b BGM/SFX（P1） | `"p12b_audio": {"bgm": {"n_candidates": 1}, "sfx": {"n_candidates": 1}}`，默认 1 按需开 | Pattern B goal-text（专家 skill 已有 SA3/ACE 通道） | M | 中：GPU 排队放大（kais-audio 侧有 Music3/H3 互斥报告——本仓未验证，标待验证）；建议排在真 BGM 引擎接入收口后 |
| 6 | p04 bible 死 knob（P2） | 文档删除该行，或 goal 注入实现（低价值不建议） | 文档 | S | 零 |
| 7 | p11a 默认表缺 key（P2） | `_DEFAULT_GENERATION_CONFIG` 加 `"p11a_preview": {"variants": {"n_candidates": 1}}` + 三份文档从"12 种"改"13 种"；统一 p11a L827/L120 的 key 写法 | 配置表补行 | S | 零 |
| 8 | p01 bounds 契约（P2） | 文档明示 p01 域 [3,5]；或 config 值越界时 warn+clamp 而非 raise | 文档/小改 | S | 低（改 raise→clamp 需过 p01 bounds 单测） |
| 9 | p09c（P2） | 暂不动；若 operator 反馈看板次品率高再 Pattern B（需同步 ALLOW_CANDIDATES_SLOTS 加 storyboard-board——注意该 slot 是 display-only，variants 数组今天也不会被拒，见 C-2） | Pattern B | M | 低 |
| 10 | gen_iframes 脱钩（P2/INFO） | 文档：generation-config-system 增补"首尾帧冗余由 `templates/gen_iframes.py --variants` 控制，与 generation_config 无关；director 档在 N==1 时自动升 2" | 文档 | S | 零 |

---

## E. 与 `/tmp/hermes-generation-config-audit.md` 交叉对比

> 本节在完成上述独立审计后撰写。对比基准：Hermes 报告全文（142 行，代码基线标注 commit 37ff3fd）。

### E-1. Hermes 四个核心发现逐条验证

| Hermes 发现 | 验证结论 | 证据比对 |
|---|---|---|
| **1. P02 游离（P0）** | ✅ **完全正确** | 证据链逐条复核成立（p02:51 / 117-141 / 签名无 episode_config / 对照 p03:594-596）。env var 纠缠、诊断假象两点也成立。story-framework 白名单"今天不触发"的降级判断同样正确（与 A-2 一致）。**唯修方案有瑕疵**：草案 `_resolve_n_candidates(..., "framework", 1)` 的 default=1 会丢掉 env 兜底语义，且漏了 run() 必须先补 `episode_config` kwarg（runner 内注注入的前提） |
| **2. P11a key 缺席（定 P1）** | ✅ 事实正确，**严重度偏高（建议 P2）** | 半接入事实与"12 种实为 13 种"判断成立。但 Hermes 未注意到：override 路径被 5 个测试文件钉死、resolver default 兜底行为正确、JSONL+`_MICRO_GATE_PHASES` 双重豁免 Layer4——这是纯文档/可发现性缺口而非功能缺口，且 08-15 降本决策本身是有意为之（p11a:116-121 注释）。修复建议（默认表补行）双方一致 |
| **3. P09c 无候选（定 P1）** | ✅ 事实正确，**严重度偏高（建议 P2）** | "唯一未接冗余的图像生成 phase"在 registry 范围内成立。但 Hermes 漏了三个降档要素：registry 明示 DISPLAY-ONLY no downstream consumer；never-raises degrade-tolerant；其标"待验证"的 qwen-eye 兜底**实际存在**（p09c:505-509 storyboard-qc advisory QC）——本审计替其完成验证：存在且生效。另外其引用"p09c:57-58 MAX_RETRY"位置不准（那是 `_GEN_TIMEOUT` 注释；MAX_RETRY=3 在 KAP 仓 gen_storyboard_sheets.py:41） |
| **4. P12b 无冗余（P1）** | ✅ **正确**，两处细节需修正 | grep 零命中复核成立；P1 定级与本审计一致；"验证失败→重跑整 phase 连带 voice/sfx/mix"的代价分析成立。修正：(a) "SA3/ACE 音频生成在 delegate goal 里（:1002）"不精确——SA3/ACE 边界规则在子代理加载的 movie-experts/audio_pipeline/SKILL.md:525-532，goal 文本本身反而自认 real BGM API "not yet integrated (deferred)"（p12b:131-132），引擎接入是半集成状态，这直接影响修复排期；(b) "与 Music3/H3 GPU 互斥"引用的是 kais-audio 外部知识，本仓无证据（本审计标待验证） |

**四项核心发现：4/4 事实成立，0 项事实错误；2 项严重度定级偏高（P11a、P09c），1 项证据引用位置不准（P09c MAX_RETRY），1 项机制描述不精确（P12b SA3/ACE 位置）。**

### E-2. Hermes 的遗漏（按重要性排序）

1. **🔴 最大遗漏：p11_video.video_render 也死了**（本审计 A-15）。Hermes 的 C 节只怀疑了 iframe 半轨（且措辞 hedged："实际不生效（或 subagent 自行决定）"），完全没查 video_render 轨。本审计给出三层铁证：execution_hint 明文禁止子代理渲染（p11b:2006-2016）与注入指令直接矛盾；`_run_engine_render` 每 shot 单次 engine.render 无变体循环（p11b:2218-2226）；`first_frame` 参数引擎支持但从不传入。iframe 轨 Hermes 的 hedge 也可以收紧为确定性结论。
2. **🔴 P03 Form C 默认分支旁路配置**（本审计 A-3）。`_USE_FORM_C` 默认 True，Form C 在 config 读取点之前 return（p03:440→581 vs 594-596）——生产默认路径下 `p03_script.n_candidates` 完全无效。Hermes 把 P03 标"✅ INFO 已覆盖"，是**方向性错误**（该发现若只看 fallback 分支会得出与 Hermes 相同的错误结论）。
3. **🔴 p10_voice.voice_design 去重致死 + tts 无选择器**（本审计 A-12）。Hermes 总表 P10 行"✅ Pattern B ✅ 2 个子 key INFO 已覆盖"——voice_design 的候选被 `_run_synthesis` 的 `char in ref_map: continue`（p10:370-379）机械性跳过；tts 多 entry 会污染下游（p11b first-wins / p12b 全量摆轨）。"已覆盖"言过其实。
4. **p04 bible 死 knob**（本审计 A-5）：`p04_bible_n` 解析后零使用。Hermes 的 D.3 讨论 bible 价值时也没发现这行代码是死的。
5. **P01 bounds [3,5] vs "1 = 单次生成"契约冲突**（本审计 C-5）：config 值 1/2/6+ 会 ValueError 炸 phase。
6. **ALLOW_CANDIDATES_SLOTS 的运行时惰性**（本审计 C-2）：Layer4 只拦字面 `candidates` key，Pattern B 全用 `variants` 措辞不触发；hook-candidates 因 writer_phase=None + 不在 PHASE_OUTPUT_SCHEMA 检查里，Layer4 从不校验。该白名单的真实角色仍是 LLM 反模式防御，其"GENERATION-CONFIG"注释夸大自身。Hermes 的 C-1/修复建议里两次提到"需同步 ALLOW_CANDIDATES_SLOTS"，均建立在对门语义的过高估计上。
7. 小项：resolver 调用点实为 10+2 处（Hermes 称 13）；p08 模块 docstring 自称"no LLM"但实际 delegate（陈旧注释，INFO 级）；Hermes P09 行引用"L2 9-expert 评审"在本仓 P09 路径无出处（P09 是 3 专家单原子 delegate p09:8，P09b 是确定性无 LLM 审计 p09b:4）。

### E-3. Hermes 的错误结论

1. **P03 "✅ INFO 已覆盖"——错**（见 E-2.2，默认路径无效）。
2. **P10 "✅ INFO 已覆盖"——错**（见 E-2.3，voice_design 机械死亡）。
3. **修复矩阵 #3"p11b goal 显式带 --variants"——方向性错误**：gen_iframes.py 是 operator 手动 prep 脚本，pipeline 零调用（grep 证实），不在 p11b 子代理手里；把 flag 写进 goal 没有执行者。正确修法是引擎层变体循环或官宣退役（本审计 D-2）。
4. 总计口径："3 个 P0/P1 级接入缺口"低估——按本审计 P0×1 + P1×4，且其中 3 个 P1 是"已接线但无效"类（Hermes 分类学里没有这一类，它只找"未接入"，没审"接入了但两端断裂"）。

### E-4. 双方一致的结论

P02 为 P0 且修复模式照抄 P03；p11a 默认表补 key（S 工作量）；p12b P1 且默认 1 按需开、GPU 成本需警示；P035/P06/P08/P09/P12a/P13/P14/P15 及三个确定性 micro-gate 的设计性排除定性；default 值数值无漂移；文档"12 种"表述需更新；story-framework 白名单缺位今天不触发。

### E-5. 综合评语

Hermes 审计在"**哪些 phase 没接 config**"这一维度上扎实可靠（4/4 核心发现全部成立，证据行号基本准确），其 P0 判断可直接采纳。但它的盲区是系统性的：**只沿"配置 → 是否被读取"单向追，没有沿"读取 → 指令 → 执行者能力 → 宿主消费"全链验证配置是否真的改变行为**。P1-2/P1-3 引擎化改造（渲染/TTS 收编进宿主进程）让 Pattern B 的 goal 注入在 p10/p11 大面积失效，这类"接线完好但两端断裂"的死 knob 共 5 个（p03 默认/p04 bible/p10 voice_design/p11 iframe/p11 video_render），Hermes 一个都没抓到，其中两个还反向标成了"✅ INFO 已覆盖"。修复排期上应先做 D-1（P02）与 D-2(a)/D-6/D-7（低成本文档收口），把"死 knob 官宣退役或真实现"作为一个独立批次决策，避免 operator 继续在无效开关上做成本预算。

---

## 摘要（≤300 字）

全量审计 21 个 registry phase + 1 个 registry 外生成脚本（gen_iframes）。总缺口：**P0×1 + P1×4 + P2×6**，设计性排除×8。P0：P02 outline 完全游离 generation_config（env 直读 3 候选，run() 连 episode_config 参数都没有）。P1×4：①P03 默认 Form C 分支在配置读取点之前 return——`p03_script.n_candidates` 生产默认路径无效；②P10 voice_design 候选被宿主去重循环机械跳过；③P11 iframe+video_render 两 knob 在 P1-2 引擎化后全死（goal 禁止子代理渲染 + 宿主单次 render + first_frame 从不传引擎）；④P12b BGM/SFX 无冗余。最重要三发现：(1) 配置系统 12 宣称资产中 5 个"接线但无效"，配置层宣称与执行层能力系统性错配；(2) P09 director×gen_iframes 与 p11 config 不是双轨冲突而是"一轨真活一轨全死"，gen_iframes 与 config 完全脱钩；(3) ALLOW_CANDIDATES_SLOTS 只拦字面 candidates key，Pattern B 的 variants 措辞天然豁免，白名单角色被注释夸大。Hermes 四核心发现 4/4 事实成立（P11a/P09c 严重度应降 P2），但漏掉全部 5 个死 knob，且 P03/P10 误标"已覆盖"，其"p11b goal 带 --variants"修复方向错误。
