# kais-movie-center 代码审查报告

## 概述

**审查范围：** `skills/kais-movie-pipeline/pipeline/` 全部 48 个 Python 文件，共 27,128 行代码。

**审查方法：** 逐文件完整阅读。核心编排模块（`runner.py` / `routing_bridge.py` / `blackboard.py` / `structured_output.py` / `phase_contracts.py` / `phase_output_schema.py` / `phases/__init__.py`）由主审亲自通读；15 个 phase 模块 + 辅助模块由 7 个并行子代理逐文件深读（其中 P06-P08 代理首轮触发 GLM 429，重试中；该区间由主审通读 `blackboard.py` 兜底）。所有 CRITICAL 发现（命令注入、p14 LLM 宕机误回滚、p10c `text=None` 崩溃、ffprobe 缺失即判 valid、Form C 审计造假、debate 批处理中断等）均由主审回查源码行号核实。引用行号均为真实代码行。

**代码库画像：** 一个 13 步（实际注册 18 个 phase，含 p09b/p10b/p10c 微门 + p15）AI 短剧生产管线。架构清晰：`PHASE_REGISTRY` 线性 DAG → `run_episode` 串行循环 + checkpoint resume → 每个 phase 通过 4 个注入 callable（`asset_bus_read/write`、`delegate_task`、`trigger_gate`）与外界交互。四层数据防护（L2 pydantic / L2b emit-side contract / L3 slot 存在性 / L4 内容 schema）设计成熟。`phase_contracts.py` 的 `extra="forbid"` + frozen 契约边界、`ratelimit._resolve_max_concurrent` 自动对齐 Hermes 上限、`engine_adapters/duration.py` 的 ABC+registry 是该仓库的工程亮点。

**主要风险集中在：**
1. `routing_bridge.py` 的 tmux/shell 命令构造（无 `shlex.quote`，C1）；
2. 错误恢复中 retry/gate 预算不持久化（C4）+ LLM 宕机/超时被静默降级为"分数=0"或"回退 hermes"，触发代价高昂的误回滚（C2、routing M11）；
3. ffprobe 缺失/超时即把损坏文件判为 valid，直接放行 dreamina 批量下载截断类损坏（C9）；
4. 单点失败击沉整批并发（P11 C10、p10b M19、debate C16）；
5. 校验覆盖不均：多数 phase 无 pydantic 模型，Layer-4 schema 是唯一有牙的层，且多个 phase（p03/p09b/p10b/p10c/p14/p15）连 L4 都没有；blackboard 模式下 `shots` 数组无任何校验；
6. 大量 `except Exception` 吞异常（全仓 101 处）+ checkpoint manifest 大文件 `read_bytes()` 全量入内存做 sha256（C5）。

---

## 严重问题 (Critical)

> 影响正确性 / 数据安全 / 可能导致管线失败或代价高昂的误操作。

### C1. RoutingBridge tmux 命令构造存在命令注入（无 `shlex.quote`）— `routing_bridge.py:697, 736-763`

**问题：** `_launch_tmux_driver` 把 `workdir`、`config_dir`、`task_file`、`output_file`、`output_json_path` 通过 f-string 直接拼成 bash 命令字符串，再经 `tmux send-keys ... claude_cmd Enter` 投递到 pane 由 bash 解释执行。全仓库 **无任何 `shlex` 引用**（已 grep 确认）。未加引号的插值点：
- L697 `f"cd {workdir}"` — `workdir` 裸插值，含空格即断命令。
- L749-759 `claude_cmd` 中 `config_dir`、`> {output_file} 2>&1`、`echo '{DONE_TOKEN}' >> {output_file}` 全部裸插值；`_task_prompt`（含 `task_file`、`output_json_path`）仅被一对单引号 `'{_task_prompt}'` 包裹。
- `task_file`/`output_file`/`output_json_path` 派生自 `Path(handoff_dir) / episode_id / phase_id`（`_handoff_path` L386）。

**影响：** 若 `episode_id` 含单引号即可闭合 L755 单引号上下文 → tmux pane 内任意命令执行。该 claude 以 `--dangerously-skip-permissions`（L753）运行，拥有完整 FS+命令权限且无确认。`episode_id` 来自 `--episode` CLI（operator）与 delegate context JSON，属"半信任"。即便无恶意输入，task.md 内容被喂给 claude 作 prompt 执行 tool call，结合 skip-permissions，episode 内容里的 prompt-injection 可致任意动作。

**建议：** 对所有插值进 shell 的路径用 `shlex.quote()`；优先改写脚本文件 + `tmux new-session 'bash script.sh'`；重新评估 `--dangerously-skip-permissions` 在生产链路的必要性。

### C2. P14 质量审计在 LLM 宕机/429/解析失败时误触发 P11 全量重渲染 — `p14_quality_audit.py:1053, 1074-1076, 1130`

**问题：** `_holistic_audit` 在 HTTP 失败时返回 `{"error": ...}`（无 `overall_score`），在 JSON 解析失败时返回 `{}`。L1053 `overall_score = quality_audit.get("overall_score", 0)` → 取默认 `0`。L1074 `if overall_score < 30: rollback_target = rollback_target or _CONTENT_ROLLBACK_TARGET`（P11）。L1130 `raise QualityGateRejection`。

**影响：** 当规则引擎（确定性，无 LLM）发现 0 个 critical、但 GLM holistic 审计因瞬态 429（文档明确 GLM=3 并发上限）、网络抖动或返回畸形 JSON 而失败时，`overall_score=0 < 30` → 回滚到 **P11（最贵的 GPU 重渲染）**，即使零 critical。`_call_llm`（L739-787）零重试，单次 429 即触发。GLM 429 在该管线是已知高频事件。

**建议：** 区分"审计失败"与"审计得低分"：`overall_score` 键缺失时记 audit-unavailable、不参与分数阈值回滚（仅告警），而非默认 0。

### C3. P10c 在 `text=None` 语音片上崩溃（AttributeError）— `p10c_voice_audit.py:110, 175`

**问题：** 两处 `v.get("text", "").strip()`。`dict.get(k, default)` 当 key **存在且值为 `None`** 时返回 `None`（而非 default），`None.strip()` 抛 `AttributeError`。`VoiceClip` 契约（`phase_contracts.py:209`）显式 `text: Optional[str] = None`；KMC-P10-FIX 还把空文本对白降级为 sfx，使 `text=None` 合法且常见。

**影响：** 任何含 `text: null` 片的 episode 让整个 P10c 质量门在发 finding 前崩溃，runner 以 Python 栈而非审计报告中止。该崩溃在 `isinstance(voice_clips, list)` 守卫内的循环里，单片即致命。

**建议：** `_t = v.get("text"); text = _t if isinstance(_t, str) else ""`（L110、L175 及所有同型位点）。

### C4. retry/gate 预算仅存内存，跨 resume 可无限重试放大成本 — `runner.py:2155-2156, 2364-2452, 2501-2554`

**问题：** Layer-4 重试计数 `_layer4_retry_counts`（L2156）与 quality-gate 拒绝计数 `_gate_rejections`（L2155）都是 `run_episode` 局部 dict。失败 re-raise 且**不写 checkpoint**（L2554 / L2362），下一进程 resume 计数器清零，又获 `_MAX_LAYER4_RETRIES=2` / `_MAX_GATE_RETRIES=3` 次额度。

**影响：** 对确定性失败（LLM 持续坏 schema、phase 持续被 gate 拒），每次 resume 都重置预算 → 跨 N 次 resume 累计 2N/3N 次 LLM 调用，成本/时间无限放大，无持久化上限告警，operator 难察。

**建议：** 把已用次数写入 checkpoint payload（或 fix-required slot），resume 时恢复，超累计上限 fail-loud 要求人工。

### C5. checkpoint manifest 对大视频文件全量 `read_bytes()` 做 sha256 — `runner.py:1387-1390, 1467`

**问题：** `_build_phase_manifest`（L1389）与 `_verify_phase_manifest`（L1467）对每条 manifest 路径 `hashlib.sha256(Path(path).read_bytes())`，整文件一次性入内存。P11 `video-clips`（expected="dynamic"）会 manifest 实际视频路径。

**影响：** 一集 20 镜头 × ~50MB ≈ 1GB 视频在每 phase checkpoint 写入与 resume 校验时全量载入。多 episode 并发或高分辨率下 OOM / GC 抖动。

**建议：** 分块哈希；对 >阈值的媒体只记 size+mtime 弱指纹，仅小 JSON 元数据做强 sha256。

### C6. P10 语音片 emit 校验从不检查音频文件存在/非空/可解码 — `p10_voice.py:472-549`

**问题：** CONTRACT-04 emit 校验仅跑 `VoiceClip(**filtered)` pydantic。`audio_path` 是裸 `Optional[str]`，无 FS 校验。`/nonexistent/x.wav`、0 字节、截断/坏 WAV 头、`anullsrc` 静音占位全部通过，直写 `voice-clips` slot 流向 P11/P12。

**影响：** 部分 TTS 失败（subagent 返回 6/9 片，另 3 路径失效）静默传播；P12 对缺失片 "glob 容忍"直接跳过 → 视频旁白空洞，全程不报错。P10c 是唯一兜底，仅查最显眼的失败。这与 C9 叠加形成"损坏/缺失媒体直达 master"的完整通路。

**建议：** emit 校验后增一道：非空 `text`+非空 `audio_path` 片校验 `os.path.exists` + `getsize > ~1000`（可选 `wave.open` 验可解码）；失败写 fix-required 或降级标注。

### C7. P10b 快速预览所有变体派生自硬编码假基线 — `p10b_rapid_preview.py:646-669`

**问题：** `_derive_baseline_structure(shot, voice_timeline, shot_id)` 接收参数但从不读取（仅一个无上游产出的 opt-in override），固定返回 `{hook_position_sec:3, emotion_sequence:["suppress","thrill"], turning_points_sec:[3,15], ending_state:"new_suspense"}`。`_derive_new_value` 对此假基线做机械变换。

**影响：** 整个 P10b 产出的变体与该 episode 真实钩子位置/情绪弧/转折点无关，下游 A/B 比的是"幽灵 episode"变体。docstring 承认"Phase 41 才接真字段"，但当前生产链路该 phase 静默坏掉。

**建议：** 立即从 `shot`/`voice_timeline`/`shot-list` 接真基线；之前至少 `logger.warning` 标假或对无 override `raise NotImplementedError`。

### C8. P14 / P09b 误导入未使用的 `numpy`，违反 stdlib-only 约束 — `p14_quality_audit.py:227`、`p09b_shot_audit.py:60`

**问题：** 两处函数级 `import numpy as np`，全文无任何 `np.` 调用（grep 确认）。p09b docstring 更承诺"Pure rule-based check — no LLM, no delegate_task"。

**影响：** operator 侧若无 numpy，p09b/p14 import 阶段即崩溃——而二者根本不需要 numpy。额外 import 延迟 + 误导维护者。

**建议：** 删除两处导入。

### C9. ffprobe 缺失/超时即把文件判为"valid"，全管线完整性/音频/交付校验形同虚设 — `p11_video_render.py:268-271`、`p12_composition.py:135-149`、`p13_delivery.py:161-169`

**问题：** 三个验证 helper 在 ffprobe 缺失或超时时一律返回 `True`：
- P11 `_ffprobe_mp4_valid`：`except (FileNotFoundError, OSError, ValueError, subprocess.TimeoutExpired): return True`。
- P12 `_verify_master_audio`（其 docstring 自称"hard guarantee against mute masters"）：ffprobe 缺失/超时 → `return True, "skipped"`。
- P13 `_verify_master_mp4`：同样 `FileNotFoundError → return True`。

**影响：** 这三个 helper 是 Layer-4 对"损坏 clip / 静音 master / 非法交付"的唯一硬执行。ffprobe 缺失或慢时全部静默通过。P11 这条直接放行已知的 dreamina 批量下载截断类损坏（仓库既有教训：file 存在 ≠ 渲染成功，必须全解码校验）—— 截断 MP4（moov 头 valid 但缺尾部 mdat）因 `getsize>0` 通过 `_ffprobe_mp4_valid`，被 checkpoint 为完成并流入 master。

**建议：** 生产中把 ffprobe 缺失视为硬失败（或显式 dev flag `KMP_SKIP_FFPROBE=1` 才跳过）；P11 校验升级为解码探针（`ffmpeg -v error -i f -f null -` 或 `-count_frames`）而非仅 `duration>0`。

### C10. P11 单镜 BgmTriggerError 中断整批并发，已完成并发镜结果丢失 — `p11_video_render.py:1089-1118`

**问题：** 所有 future 一次性提交（L1091-1094），主线程 `for idx, future in enumerate(futures): future.result()`。shot K 抛 `BgmTriggerError` 时写 fix-required + `raise`（L1118）退出循环。**已完成但 idx>K 的并发镜永远到不了聚合/checkpoint 块**（L1119-1167），其 `video_clip`/`lip_sync_report` 被丢弃、不入 `_completed_shots`。重试时仅 `_try_cache_hit`（mp4+meta 存在）能救；缺 `_meta.json` 的镜被重渲染。

**建议：** per-future 捕获 `BgmTriggerError`，记录后继续收集其余 future，循环结束后再 raise。

### C11. P13 fallback（事实上的主交付路径）静默忽略 render_resolution + color_intent — `p13_delivery.py:296, 359-396, 436-450`

**问题：** `_render_master_mp4_fallback` 接受 `render_resolution` 与 `color_intent` 形参但函数体内从不引用 `render_resolution`；调色用硬编码 `eq=brightness=0.0:contrast=1.05:saturation=1.10:gamma=1.0`（L371）无视 `color_intent`；mux/watermark 用 `-c:v copy`/libx264 不强制分辨率。注释（L89-99）承认该 fallback 因"delegate 路径 8 个真实 episode 从未自动成功"而成为**实际主路径**。

**影响：** 实际生产中产出的 master 是错误分辨率、未按 color_intent 调色，却宣称 FORM-01 合规。

**建议：** 应用 `scale=W:H,setsar=1`；按 `color_intent` 应用 LUT 或删除误导性形参。

### C12. Form C 审计从空 dict 造假（traitor_r1 被无视）— `p03_form_c/orchestrator.py:416-419, 761-765`

**问题：** 当 `should_iterate=False`（"R1 已够好"的常见路径），`script_r2=None`、`traitor_r2=None`。`_select_final` 调 `_synthesize_audit(traitor_r2 or {}, ...)` 传入 `{}` → 每个 Form C 维度默认 `7/10=0.7`（L467-470），traitor_r1 的真实分数/攻击**全部丢弃**。注释"synthesize audit from traitor_r1"是谎——`traitor_r1` 从未传入 `_select_final`（调用点 L679 在作用域内却没用）。

**影响：** 每个不 iterate 的 Form C 运行（生产默认路径）向 asset_bus 写一份**伪造 audit-report**（全 0.7 + 一句样板弱点），真实 R1 审计发现永不到达下游与 gate。

**建议：** 把 `traitor_r1` 传入 `_select_final` 并作为 R1-only 合成来源。

### C13. Form C 分支跳过 `_validate_audit`——生产默认路径无 D6/D7/honesty/loop 强制 — `p03_script_audit.py:376-497`

**问题：** Form C 模式（`_USE_FORM_C` 默认 True，L69）在 L394-395 写 `script-draft`+`audit-report` **不调** `_validate_audit`；而单 agent fallback 分支调了（L556-559）。于是 D6 `reversal_depth<0.6` VETO、D7 `social_resonance_depth<0.5` VETO、"5 维全 ≥0.8"诚实规则、对白 loop 检测、silent-script(Q4) 检查在**生产默认路径全部被跳过**。

**影响：** `reversal_depth=0.3`（灾难性浅反转）的 Form C 剧本静默通过；同剧本在单 agent 模式会 `QualityGateRejection`。质量强制跨分支不一致。

**建议：** 在 L391-394 之间调 `_validate_audit(audit, creative_constraints, story_framework, script=script)`，或文档说明 Form C 为何豁免。

### C14. `_glob_agent_artifact` 递归搜全 skill 树且无 episode_id 过滤 → 跨 episode 数据污染 — `p03_form_c/orchestrator.py:225-271`

**问题：** 当 Form C agent summary 解析失败，glob fallback 递归搜 `os.path.join(_skill_root, "**", f"p03_{bare}_*.json")`（L261），`_skill_root` 是整个 `kais-movie-pipeline/`。`p03_blacksmith_*.json` 会匹配**任意 episode** 在 `runs/` 下的同名产物。`episode_id` 贯穿调用链却从未用于过滤 glob。

**影响：** episode B 的 blacksmith 写盘但 summary 畸形时，glob 会捞到 episode A 上次的 blacksmith 剧本，episode B 静默消费 A 的场景。也是安全问题：skill 树下任何被植入的 `p03_*.json` 都会被加载。

**建议：** pattern 含 episode_id（`*{episode_id}*/p03_{bare}_*.json`）或把递归 glob 限定在 `runs/{episode_id}/`。

### C15. P02/P03 单 agent 并行 dispatch 缺 `candidate_index` → tmux handoff 目录互踩 — `p02_outline.py:123-127`、`p03_script_audit.py:525-528`

**问题：** 两 phase 用 ThreadPoolExecutor 扇出 N 个并行 `delegate_task`，但给所有 N 个传**同一个** `context`。P01 恰好修过同 bug（KMC-FIX-01，`p01_hook_topic.py:545-547` 注入 1-based `candidate_index`，RoutingBridge 据此把每个并行调用隔离到 `cand-{N}` 目录）。P02/P03 单 agent 分支没应用此修复；Form C orchestrator 应用了（`orchestrator.py:322-324`）。

**影响：** N 个并行候选共享一个 `p02_outline/`（或 `p03_script_audit/`）tmux handoff 目录，互踩 `output.txt`、争抢 `***DONE***` token。Form C 里 `_glob_agent_artifact` 的存在强烈暗示此互踩在生产中已发生。

**建议：** 两文件 `pool.submit(...)` 时镜像 `p01_hook_topic.py:538-550` 注入 `candidate_index`。

### C16. debate `resolve_all_conflicts` 吞非-DebateError 异常，首个畸形 LLM 响应中断整批 — `debate.py:651-664, 438, 509, 292`

**问题：** 循环仅 `except DebateError`。但 `run_debate`→`_parse_proposal`（L292）`float(d.get("confidence", 0.5))` 在 GLM 返回非数值 confidence（"high"/"0.9 confident"/`null`）时抛 `ValueError`/`TypeError`——**非 DebateError**。该异常逃出 `run_debate`（内部无 try/except 包裹 `_parse_proposal`），再逃出 `resolve_all_conflicts` 的窄 catch。**首个畸形响应即中止整个批处理循环**，剩余冲突全部未解，blackboard 残留 `debate_pending=True`。

**影响：** 单个坏 subagent JSON 形状撤销同批所有后续冲突的解析；下游看到未解析 L2 冲突且无告警（部分失败连日志都不打）。

**建议：** 拓宽 catch 到 `except Exception` 并清晰日志；或 `_parse_proposal` 失败时回退 `confidence=0.5`。

### C17. creation_logger 跨进程 JSONL 追加非原子（>4KB 行可交错）— `creation_logger.py:128-136`

**问题：** `self._lock` 是 `threading.Lock`（进程内）。POSIX `O_APPEND` 仅对 ≤`PIPE_BUF`（≈4096B）写入原子；而 delegate goal/result/driver output 行常 >4KB。两进程同时追加同一 `creation-<episode>.jsonl`（resume 时旧进程仍活、两个 operator 窗口、并行 cron）→ 写入交错，相邻两行各是半个 JSON 对象。`read_log`（L514）`except json.JSONDecodeError: pass` 静默丢行。

**影响：** 审计跟踪条目静默丢失；`summary()` 低估事件/错误数且无信号。

**建议：** 每 entry 写入走 per-episode `fcntl.flock` 串行化；或大声文档"resume 需确保旧进程已死"。

### C18. blackboard 模式 Layer-4 跳过 `spatio-temporal-script.shots`——空 shots 列表静默流向 P07 — `p06_blackboard.py:401` vs `phase_output_schema.py:435-455`

**问题：** blackboard 变体写 `spatio-temporal-script` slot（L401，正是 P07 主输入），但 `PHASE_OUTPUT_SCHEMA["p06_blackboard"]` 只校验 `visual-direction`/`physics-precheck-report`。串行 `p06_spatio_temporal_script` 条目（schema:418-432）**会**强制 `shots` 为非空 list + `shots[*].pacing` 为 str——blackboard 模式（`enable_blackboard=True`，生产默认）两者都不触发。

**影响：** 当 `_consolidate_spatio_temporal` 产空 `shots`（如 cinematographer 输出缺 `shot_decisions.shots`——任何 L2 形状漂移即发生），orchestrator 写 `{"shots": [], ...}`、Layer-4 通过、P07 以零场景继续，无 fail-loud。串行 p06 的 `min_len=1` 安全网在生产默认模式下被静默关闭。

**建议：** 给 `p06_blackboard` schema 块加 `SlotCheck(slot="spatio-temporal-script", required_fields=[FieldCheck("shots", types=(list,), min_len=1), FieldCheck("shots[*].pacing", types=(str,), required=False)])`，逐字镜像串行条目。

### C19. blackboard 模式 editor pacing 因 wrapper-key 不匹配永远丢失——每镜 `pacing=""` — `p06_blackboard.py:717-782`（esp. 743-746）vs `596-643`

**问题：** `_consolidate_spatio_temporal` 读 `editor_out.get("shot_decisions", {}).get("shots", [])` 建 `editor_by_id`。但 editor 解析器 `_write_editor_entries`（L596-643）及其 docstring 明示 editor 顶层键是 `pacing_suggestion`，**非** `shot_decisions`。故 `editor_shots` 恒 `[]`、`editor_by_id` 恒 `{}`，`_serialize_pacing_str(None,None,None)` 对**每镜**返回 `""`。

**影响：** blackboard 模式下 consolidated `spatio-temporal-script` 每镜 `pacing` 全空；串行 p06 产真值如 `"fast|hard-cut|beat-aligned"`。这是相对串行模式的静默数据丢失回归，且恰是 SYNC-01 str 契约（schema 本应强制）；C18 也抓不到（空串通过 `types=(str,)`）。下游 P14 节拍同步回测失去 editor 侧计划。

**建议：** 改读 `editor_out.get("pacing_suggestion", {}).get("shots", [])`；更好——四个 L2 解析器统一过同一 normalizer 返 `{shot_id: {field: value}}`，使 `_consolidate` 与 wrapper-key 无关。

### C20. P08 `scene-selection` 裸 list 写入，但 schema 期望 `{"scenes":[...]}`——所有 FieldCheck 静默 no-op — `p08_scene_selection.py:117-119` vs `phase_output_schema.py:490-499`

**问题：** p08 写 `asset_bus_write("scene-selection", scene_selection)`，`scene_selection = expert_output.get("scene_selection", [])` 是裸 list。schema 条目检查 `FieldCheck("scenes", ...)`/`scenes[*].scene_id`/`scenes[*].selected`。`phase_output_schema.py:513-518` 自己的注释已文档化"裸 `FieldCheck` 对 list 永不 resolve——`_resolve_path` 仅对 dict 走 bare key"。正是此反模式：裸 list slot 上每个 FieldCheck 静默 no-op，仅 `min_items=1` 实际运行。

**影响：** `[{"scene_id": 123}]`（错类型）或 `[{"foo":"bar"}]`（缺 scene_id）通过 Layer-4。p09 `_inject_scene_refs` join `scenes[*].scene_id`；缺/错 scene_id 静默使 shot 与 scene 解耦——正是 F-02 注释（schema:487-489）称加该校验要防的失败模式。

**建议：** 要么写时包 `{"scenes": scene_selection}`（并改 p09 读者），要么把 FieldCheck 改 `scenes[*]` 形对裸 list（镜像 schema:513-518 记录的 p09 shot-list 修法）。

---

## 重要问题 (Major)

> 设计缺陷 / 代码质量，影响可维护性与稳健性。

### 校验 / 契约 / 数据流

- **M1. P14 阈值常量是死代码，docstring 与实现矛盾** — `p14_quality_audit.py:36-39, 64, 1074`。`_BLOCKING_SCORE_THRESHOLD=60`（L64）从未引用；实际门用字面量 `<30`（L1074）；docstring（L37）称 `<60`。三方不一致。
- **M2. P14 节拍同步阈值报告值与实际判定不符** — `p14_quality_audit.py:697-703, 730`。注释与上报 `threshold_ideal_f:1.5`，实际 `if max_err<=1`。0.5f 错分类。
- **M3. P14 规则引擎缺 `isinstance` 守卫，畸形条目即整引擎崩** — `p14_quality_audit.py:305, 339, 357, 362, 383-384`。Rule 3/4/5/6 直接 `v.get(...)` 无守卫；Rule 8 正确守卫——不一致。单非 dict 条目 `AttributeError` 杀死**整个确定性规则引擎**。
- **M4. P14 `_call_llm` 三重脆弱** — `p14_quality_audit.py:739-787`。(1) L770 `b["text"]` 直接下标缺字段即 `KeyError`；(2) 零 retry/backoff，单次 `requests.post(timeout=180)`，429 即审计失败→触发 C2；(3) 端点 URL 硬编码（L764），`temperature=0.6`（审计宜 ≤0.2）。
- **M5. `VoiceClip._dialogue_requires_text` 静默改写 clip_type，架空 CONTRACT-02** — `phase_contracts.py:259-276`。模块 docstring 称 CONTRACT-02 目的是"fail loud"，KMC-P10-FIX 却用 `object.__setattr__`（绕 frozen）把空文本对白静默降级为 sfx。契约文本与代码互相说谎；产空文本对白的上游 prompt bug 被掩盖。建议要么改回 `raise`，要么更新 docstring + warn log。
- **M6. `phase_output_schema._check_field` 类型宽松** — `phase_output_schema.py:188-199`。`int` 型字段接受 `float`（`beat_count=4.5` 通过）；`bool` 在 `int` 分支未被排除（`isinstance(True,int)` 为 True），数值字段接受 `True/False`。建议删"float 可充 int"分支 + `int` 分支显式拒 bool。
- **M7. `get_phase_checks` regex fallback 是死代码** — `phase_output_schema.py:702-708`。`PHASE_OUTPUT_SCHEMA` 所有 key 都带后缀（`p10_voice` 等），裸 `p10` 永不命中。docstring（L688）的"p10_voice→p10"误导。
- **M8. `SchemaValidationError` 定义两次，首具 body 死** — `phase_output_schema.py:86-106`。L86 定义后被 L104 重绑定覆盖。重构残留，删首定义。
- **M9. phase 契约为 opt-in，asset-bus 写路径不强制契约 round-trip** — `phase_contracts.py`（整文件）。契约模型仅在产 phase 显式构造时生效；写裸 dict 即绕过。fallback 是粗得多的 L4 `FieldCheck`（连对白 `text` 非空都不查）。docstring"central typed contract boundary"为理想。建议在 emit 路径统一强制。
- **M10. P02/P03/P04/P05 调 `parse_delegate_output` 不传 pydantic 模型** — `p02_outline.py:119`、`p03_script_audit.py:521`、`p04_character_design.py:570`、`p05_pain_discovery.py:112`。仅 P01 传 `P01Output`。P02 等输出畸形 JSON 静默入 asset_bus；P03 读到非 dict 调 `.get()` 级联 `AttributeError`。建议定义并传各 phase Output 模型。
- **M11. 校验覆盖空洞** — p03/p09b/p10b/p10c/p14/p15 在 Layer-4 无内容校验（`get_phase_checks` 返回 `[]`）；blackboard 模式下 `spatio-temporal-script.shots` 数组完全无校验（schema 仅查 visual-direction/physics-precheck-report）。`_PHASE_EXPECTED_COUNTS` 也未含 p09b/p10b/p10c/p14/p15，resume 时 manifest 校验对这些 phase 是 no-op。

### 并发 / 资源 / 恢复

- **M12. routing_bridge tmux 会话泄漏（kill-session 不在 finally）** — `routing_bridge.py:1314-1358`。`parse_driver_output`/`_write_result_file` 抛异常即跳过 L1355 kill-session → 长期累积泄漏。移入 finally。
- **M13. routing_bridge 超时/启动失败静默回退 hermes，运行中偷换模型** — `routing_bridge.py:1295-1311, 1321-1326`。operator 仅看日志，创意 phase 实际跑在不同模型（GLM→Hermes），与路由表声明不符，不上升质量门。破坏可复现性。
- **M14. routing_bridge 固定 `time.sleep` 假设** — `routing_bridge.py:693, 700`。慢机下 `cd workdir` 可能在 bash pane 就绪前执行。
- **M15. parallel_delegate ThreadPoolExecutor 无超时，卡死 worker 永阻** — `runner.py:780-797`。`fut.result()` 无 per-future 超时；worker 线程无法收 SIGALRM（已文档化），单卡死 subagent 永久阻塞 blackboard 并行 dispatch，仅 advisory 日志。
- **M16. blackboard `time.time()` 作 timestamp 非 monotonic** — `blackboard.py:119, 283`。docstring 称"monotonic"，NTP 回拨会倒退。用 `time.monotonic()` 或更正注释。
- **M17. blackboard 每次 write 全量 JSON flush** — `blackboard.py:286`。O(n) 每次、O(n²) 累计；随 episode 规模劣化。
- **M18. `_manifest.write_manifest` 读-合并-写无文件锁 + 非原子写** — `phases/_manifest.py:377-421`。并发 dispatch 下 read-modify-write 竞态 → 后写者覆盖；`json.dump` 非原子，崩溃中写留坏文件被下次 `except` 吞。temp+`os.replace` + `fcntl.flock`。
- **M19. p10b 单变体失败击沉整集预览** — `p10b_rapid_preview.py:494-503`。`fut.result()` 重抛 `PreviewEngineError`，顶层仅捕构造/传输类异常，单坏变体使整集 `preview_skipped=True`、已成功变体全丢弃，与 docstring"per-variant 静默降级"相悖。
- **M20. p10b `@glm_concurrent` 装饰器误用于非-GLM 引擎调用** — `p10b_rapid_preview.py:448-468`。`_generate_variant` 调 LTX-Video(ComfyUI) 却占全局 GLM=3 信号量，可饿死无关 GLM 调用；operator 调高 `GLM_MAX_CONCURRENT` 仍被串行在 3。ThreadPoolExecutor 已限并发，装饰器多余。
- **M21. `ratelimit.retry_on_429` 子串 "429"/"529" 误报** — `ratelimit.py:338-353`。裸子串匹配任何含该数字的文本（"pid 4295"/"529ms"/"port 5290"）→ 触发 60s→120s→240s 退避。状态码应用 `\b429\b` 词边界，仅人类可读 marker 用子串。**很可能已在引发神秘停顿。**
- **M22. `glm_limiter` 对 `async def` 是 no-op（latent）** — `ratelimit.py:222-230`。`func(...)` 立即返回协程，`finally` 在协程被 await 前释放信号量。当前仅用 ThreadPoolExecutor 故潜在，但无运行期拒协程检查，易踩。建议 `inspect.iscoroutinefunction` 检测并 raise。
- **M23. 畸形 `GLM_MAX_CONCURRENT` 静默忽略无 warn** — `ratelimit.py:137-146`。`except ValueError: pass` 静默回退默认，"resolved to N"日志对被拒 override 说谎。

### 代码质量 / 重复 / 超长函数

- **M24. 超长 `run()` 函数集中区** — p11 `run` ~673 行（L587-1259）、p10 `run` 401 行、p09 `run` 349 行、p12 `run` ~407 行、p13 `run` ~321 行、p01 `run` 533 行、p04 `run` 458 行、p03 `run` 281 行、p14 `run` 259 行、`_run_rule_checks` 206 行、p10b `_run_body` 194 行、p13 `_render_master_mp4_fallback` 195 行、p11 `_goal_for_shot` 311 行、`debate.run_debate` ~217 行。这些 `run()` 把 asset-bus 读、goal 构造、dispatch、1-5 个 post 校验、manifest、canvas sync 全内联，难单测，使多 bug（C2/C10/M19）相互作用难审计。
- **M25. 5 处复制 "fix-required + retry-then-warn + raise"** — p09 四处（`590-609, 664-681, 842-866, 1094-1134`）+ p10（`508-528`）。~120 行重复；p10 retry 路径已偏离（丢片非 raise）。抽 `_emit_fix_required_or_warn`。
- **M26. 5 处复制 `sync_canvas` try/except** — p09/p09b/p10/p10b/p10c。抽 `_safe_sync_canvas`。
- **M27. manifest-synthesis 块在所有 5 个早期 phase 复制** — p01:746-822、p02:158-196、p03:398-474 + 569-609、p04:682-745、p05:122-194（~400 行）。抽 `_build_phase_manifest_nodes`。
- **M28. p02 五个喜剧公式 goal builder 大量复制** — `p02_outline.py:582-866`，~55 行/个 ×5，骨架完全相同。抽 `_FORMULA_SPECS` 表 + 单 builder。其中 `_build_overkill_solution_goal`（811-866）是死代码（无 subtype 映射到 overkill_solution）。
- **M29. `_is_comedy_genre` + `_COMEDY_GENRE_KEYWORDS` 三处复制** — `p02_outline.py:58,61-66`、`p03_script_audit.py:73,97-102`、`orchestrator.py:540`。已 drift（orchestrator 内联）。移共享 helper。
- **M30. P05 文件孤死仍声明 Gate 4** — `p05_pain_discovery.py:1-216`（`GATE_ID="shot-prep"` L44，与 p04 同 ID）。已从 chain 移除但文件未清，残留 INPUT/OUTPUT slot 契约误导。
- **M31. Form C `_run_agent` 吞所有异常转 `_error:True`** — `orchestrator.py:308-354`。网络/JSON/RoutingBridge 崩/AssertionError 全软标志；`_select_final` 不查 blacksmith/alchemist 输出的 `_error` → 错误标志 dict 流入下游 context 被当真产物。建议缩窄 catch 并对每个消费点查 `_error`。
- **M32. P11 `_try_cache_hit` 伪造质量指标** — `p11_video_render.py:1053, 1056-1062`。缓存命中返回 `murch_grade:"pass"`、`alignment_score:1.0`、`mouth_frames_analyzed:0`，P12/P14 当权威消费，坏 lip-sync 对 QA 不可见。用哨兵 `null`/`"unverified"`。
- **M33. P11 启动 ffprobe 风暴** — `p11_video_render.py:295-298`。`_load_p11_progress` 对每镜串行 `_ffprobe_mp4_valid`（10s 超时）；resume 20 镜 → 启动最多 200s 延迟。线程化。
- **M34. P11 per-shot 重序列化同一大多载荷** — `p11_video_render.py:985-995`。`_goal_for_shot` 内每镜 `json.dumps({...scene_images, character_assets, voice_timeline...})`，N 镜 N 次。配合 M2（死预序列化变量）说明有人想做"序列化一次"却没接。循环外预序列化共享载荷。
- **M35. P13 无幂等/cache-hit，总是重 delegate** — `p13_delivery.py:637-639`。即便已有合法 master.mp4 也全跑 colorist+editor+compliance。P11 有 `_try_cache_hit`，P13 无。短路 `_verify_master_mp4(existing)`。
- **M36. P13 `_verify_master_mp4` 默认对缺失文件软跳** — `p13_delivery.py:141-144, 495`。`enforce_delivery=False` 默认下，**缺失** master.mp4 通过校验。生产须 runner 显式注入 opt-in；注入失败则交付静默坏掉。默认应 True，测试显式 False。
- **M37. P12 静音输入守卫仅 prompt 文字、无 L4 强制** — `p12_composition.py:61-74, 93-198`。post-delegate 只查音频流"存在"，-91dB 近静音 master 通过。守卫针对的 mute-master 失败模式实际未捕获。补 volumedetect + `mean_volume < -50dB` 且未标 `needs_review` 即失败。
- **M38. p15_canvas_sync 硬编码绝对 sqlite 路径** — `p15_canvas_sync.py:232`。无 env 覆盖，换机/CI/repo 改名后覆盖率校验静默退化为 0%，sqlite 错误被吞（L130-132）。读 `KAIS_CANVAS_DB_PATH`。
- **M39. p15_canvas_sync sqlite 连接异常路径泄漏 + 状态文件读取未守卫** — `p15_canvas_sync.py:106-132, 163-164`。close 不在 finally；`.pipeline-state.json` `json.load` 无 try/except，半写状态文件抛未捕获 `JSONDecodeError`，违反该 phase 自身"canvas 不可达则告警"契约。
- **M40. `_canvas_helper.auto_canvas_sync` `int(pid)` 校验推迟到调用期** — `_canvas_helper.py:29-38`。外层 try/except 包闭包定义非调用；`KAIS_CANVAS_PROJECT_ID=abc` 成功返闭包，调用方任意后续点抛 `ValueError`，debug 日志永不触发。
- **M41. p14 quality-audit 在 gate 阻断时不同步画布** — `p14_quality_audit.py:1108, 1113-1135, 1205-1209`。raise 跳过 sync 块，最该看的"被拒交付"canvas 永远收不到审计节点。
- **M42. `_manifest` 内容长度门静默降到 1，docstring 仍称 20** — `phases/_manifest.py:106, 92-105, 214-216, 244-253`。`MIN_DESCRIPTION_LEN=1` 使校验沦为 theater；docstring 三处仍称 20 字符门槛。
- **M43. `_reference_loader._assert_safe_url` 只捕 `gaierror`，破坏 fail-fast 契约** — `phases/_reference_loader.py:90-96`。`getaddrinfo` 还抛 `socket.herror`/`socket.timeout`（非 gaierror 子类），DNS 超时（L91 无 timeout）抛错类型非契约 `ValueError`，调用方 `except ValueError` 捕不到，管线内部栈崩。
- **M44. p09 `_inject_scene_refs` 路径遍历** — `p09_shot_breakdown.py:999-1011`。`scene_ref=f"{path}/{view}.png"`，`path` 取自 P07 无 `..` 校验，L1003 接受任意绝对路径，P11 盲跟。内部信任边界，但对 replay fixture/跨 episode 复用是隐患。`realpath().startswith(workdir)`。
- **M45. p09 静态镜头阈值 prompt(30%) 与 runtime(35%) 不符** — `p09_shot_breakdown.py:641`。错误消息写"Max 30%"却用 `>0.35`。
- **M46. creation_logger 吞所有写错误零可观** — `creation_logger.py:128-136`。`except (OSError,IOError): pass` 完全静默，disk-full/权限/NFS 抖动不可见，operator 可丢整条审计跟踪而不知。加 `logger.warning(..., exc_info=True)`。
- **M47. `_parsing_utils` 不修 trailing comma/控制字符/截断** — `phases/_parsing_utils.py:40-99`。声称 robust，实际修复全推 `json.loads`，三常见 LLM 失败模式均返 `{}`；与 `structured_output`（json_repair）两套 JSON 哲学并存。
- **M48. `run_episode` 用类名字符串判生产 bus** — `runner.py:2487`。`type(bus).__name__=="AssetBus"`，子类/重命名即静默跳过 Layer-4。`isinstance` 或显式 flag。
- **M49. gate 回滚靠改 for-loop 变量** — `runner.py:2451`。`idx=rollback_idx-1; continue` 修 `for idx in range(...)` 变量，可工作但脆弱。
- **M50. P02 `_select_final` 死 if/else + 变量误名** — `orchestrator.py:432-442`。两分支都返相同 `(script_r2,audit_r2)`；`r1_score` 实为 R2 分（key `overall_quality_r2`）。docstring 承诺的 R1-vs-R2 比较从未发生，R2 总赢。
- **M51. blackboard `debate_pending` 半接线** — `p06_blackboard.py:316-329`。`detect_shot_design_conflicts` 跑、`mark_debate_pending` 设标志，但 `debate.run_debate` 从未在此 phase 调（grep 确认）。标志设了永不清（`clear_debate_pending` 仅被未用的 `run_debate` 调），且 `latest_entry` 无视该标志——对消费者纯噪音，又使被标条目永不再 conflict。
- **M52. blackboard `final_audit` fallback 到整个 `script_auditor_out` 产错形状** — `p06_blackboard.py:399`。`script_auditor_out.get("audit_report", script_auditor_out)`——形状漂移时整个含非审计 wrapper 键的 dict 成 `final-audit` 资产，且 blackboard 模式无 `final-audit` 的 L4 校验。改默认 `{}`。
- **M53. blackboard manifest 吞 `ValueError`，与 p07/p08 语义分歧** — `p06_blackboard.py:436-437` vs `p07:649-652`/`p08:170-173`。p06_blackboard catch 含 `ValueError`，吞掉了"缺 required params MUST 传播"的硬错；serial p06/p07/p08 仅 `(OSError, RuntimeError)`。blackboard 模式静默丢畸形 manifest 节点，serial 模式 raise。
- **M54. p07 过宽 `except (ValueError, Exception)` 掩盖 parser bug** — `p07_scene_generation.py:578-589`。`Exception` 已覆盖 `ValueError`，捕一切非 KeyboardInterrupt/SystemExit；`_parse_expert_output` 内 `KeyError`/`TypeError` 被当"空 summary"触发文件系统 fallback，含 `filePath:""` + `_pending_generation:True` 的 `_partial` 路径流向下游，P11 在远离根因处才崩。缩窄到 `(ValueError, StructuredOutputError)`。
- **M55. p07 fallback 硬编码调色板/LUT 无视 genre/mood** — `p07_scene_generation.py:111-127`。`_build_style_and_color_from_requirement` 固定返 "warm amber/emerald/golden" + `pixinxar_warm_forest` LUT。horror+dread episode 若 P07 被打断即得暖林调色，p13 按 `color-intent` 调色，错色静默应用。`_fallback:True` 标志无消费者分支。
- **M56. p07 硬编码 operator 专属 workdir 候选** — `p07_scene_generation.py:205-207`（`/data/workspace/...` + `/home/kai/runs/...`）。换机即 fallback 失败返 `{}` → L588-589 重抛原 parse 错，丢失恢复路径。应仅靠 `KAIS_RUN_WORKDIR` + hint_paths。
- **M57. p06/p07/p08 死的 JSON 字符串局部变量** — `p06_spatio_temporal_script.py:160-161`、`p07:485-486`、`p08:69-70`。`json.dumps(...)` 后从不引用（goal 用 `.context(raw dict)`）。大多载荷浪费序列化 + 误导读者。删除。
- **M58. p08 无 retry/fallback，与 p06/p07 不对称** — `p08_scene_selection.py:111-112`。p06 有 retry 循环、p07 有文件系统 fallback，p08 直接传播 parse 失败。p08 在 schema 注释中被称"previously the pipeline's weakest phase"，此不对称意外。
- **M59. blackboard parser 失败静默丢整 agent 数据** — `p06_blackboard.py:297-307`。宽 `except Exception` 零一 agent 贡献仅 WARNING；result 仅 `l2_entry_count` 间接反映损失，无 parser_failures 结构化指标。建议 parser 失败且该 agent 无 dispatch error 时 fail-loud（真 bug）。
- **M60. blackboard `_summarize_l2` 过滤 episode 级条目，致 L3 看不到 audit** — `p06_blackboard.py:695-714` vs `562-568`。`_write_script_auditor_entries` 写 `shot_id=None` 的 episode 级 audit_report 到 L2，`_summarize_l2` 跳过所有 `shot_id is None`，故 L3 physics_precheck 看不到 audit_report——与解析器 docstring 声称"L3 消费 audit_report"矛盾，跨 agent 仲裁弱于设计。
- **M61. blackboard 无 `(layer,agent,shot_id,field)` 唯一性强制** — `blackboard.py:28`（不变量声明）vs `275-287`（write 仅 append 不去重）+ `p06_blackboard.py:646-679`。LLM 同 shot_id 重复时每次写一条，`detect_shot_design_conflicts` 跳同 agent 对故不可见；`latest_entry` 返最高版本掩盖 LLM 实际输出；持久文件随重跑无界增长。
- **M62. p06_blackboard `run` ~380 行 / p07 `run` ~225 行 / p07 `_filesystem_fallback` ~175 行** — `p06_blackboard.py:103-483`、`p07_scene_generation.py:449-673`、`174-348`。加入 M24 超长函数清单。

---

## 一般问题 (Minor)

> 风格 / 性能 / 小 bug。精选（完整明细见附录）：

- **m1. p14 ffprobe 重复调用** — `p14_quality_audit.py:998-999` vs `235,265`，4 次可合 1。
- **m2. p15_feedback 反思文件非原子写且覆盖历史** — `p15_feedback.py:235-242`。
- **m3. p15_feedback 反思写失败仍写 asset_bus 标"written"** — `p15_feedback.py:237-273`，应标 `write_failed`。
- **m4. p15_canvas_sync 双重 sys 引用 + 硬编码 fallback path** — `p15_canvas_sync.py:196-202`。
- **m5. p15_canvas_sync `expected_phases` 前缀碰撞** — `p15_canvas_sync.py:236`，p15_canvas_sync 与 p15_feedback 同塌缩为 `p15`，覆盖率虚高。
- **m6. p14 LLM key 泄漏面** — `p14_quality_audit.py:744-767`，开 requests DEBUG 即落日志。
- **m7. p10c 顺序调 ffmpeg N 次** — `p10c_voice_audit.py:216-233`，可线程化。
- **m8. p09 `re.compile` 在热循环内** — `p09_shot_breakdown.py:1042`，每禁词每镜重编译。
- **m9. p09 `_count_cjk_chars` 标点表不全** — `p09_shot_breakdown.py:710-717`。
- **m10. p10b `_resolve_keyframe` cwd 相对 exists** — `p10b_rapid_preview.py:640-642`，应 resolve workdir。
- **m11. p09 `_compute_element_durations` 原地改 input 无原值审计** — `p09_shot_breakdown.py:720-781`。
- **m12. routing_bridge `DRIVER_PROFILES` import 期 expanduser / `_REPO_ROOT` 假定布局** — `routing_bridge.py:159-163, 170`。
- **m13. runner fallback project_id 用 `int(time.time()*1000)`** — `runner.py:3093`，同毫秒碰撞。
- **m14. blackboard `_flush_to_store` 吞所有异常仅日志** — `blackboard.py:266`。
- **m15. structured_output 4 处 `except Exception: pass/continue` + 硬编码 `_SCHEMA_KEYS`** — `structured_output.py:91,122,168,291-296`。
- **m16. p11/p12/p13 死的预序列化 JSON 变量** — `p11:682-684`、`p12:364-367`、`p13:587-589`（`si_json` 等从未引用）。
- **m17. P11 硬编码绝对路径 cd 指令** — `p11_video_render.py:904-905`，应用 `_SKILL_ROOT`（L59）。
- **m18. P11 子串匹配误报** — `p11_video_render.py:371-372, 387-388`，"hum" 匹配 "human"。
- **m19. P11 `_save_p11_progress` O(N²)** — `p11_video_render.py:308-327`，每镜整集重序列化。
- **m20. P13 AIGC watermark 强制全片 libx264 重编码** — `p13_delivery.py:438-450`（burn-in 固有代价，但是最慢 fallback 步）。
- **m21. ffprobe 超时不一致** — P11 10s / P12 5s / P13 10s，应统一常量。
- **m22. P02/P03 import 期 `int(env)` 可崩** — `p02_outline.py:50`、`p03_script_audit.py:57`，非数值 env 抛 `ValueError` 断整管线。
- **m23. P03 manifest 块死重算** — `p03_script_audit.py:400-405`，先算 scenes/weaknesses 再覆盖。
- **m24. Form C 死常量/未接线 prompt** — `orchestrator.py:67`(`_OVERDESIGN_THRESHOLD`)、`prompts.py:412-425`(`AUDIENCE_JUDGE_FORM_C_PROMPT`)、`384-397`。
- **m25. 拼写 `_CicHE_HIGH_RISK`** — `orchestrator.py:66,390`，应 `_CLICHE`。
- **m26. P04 硬编码用户名 `/home/kai/` + episode_id 未消毒入路径** — `p04_character_design.py:234-244`。
- **m27. P01 `_validate_requirement_feasibility` 写未声明 slot `requirement-feasibility`** — `p01_hook_topic.py:305,82`。
- **m28. Form C prompt 含未替换 `{placeholder}` 字面传 LLM** — `prompts.py:111-118` 等多处。
- **m29. `_utc_iso` TOCTOU（两次 `datetime.now()`）** — `creation_logger.py:58-61`。
- **m30. `read_log` 整文件入内存** — `creation_logger.py:503-516`。
- **m31. debate `assert ruling is not None` 在生产（-O 被剥）** — `debate.py:522`。
- **m32. debate `max_rounds>2` 静默按 2 处理但 warn 暗示支持** — `debate.py:394-402`。
- **m33. glm_advisory 无 429 retry + 5 处宽 `except Exception`** — `glm_advisory.py:243-262, 208/219/260/270/279`。
- **m34. `_manifest` 无 schema_version 字段** — `phases/_manifest.py:361-367`，旧格式被原样合并。
- **m35. 全仓 101 处 `except Exception` 吞异常**，多数 best-effort 可接受，但关键路径丢根因；建议加 `exc_info=True` 并区分"预期降级"与"编程 bug"。

---

## 架构建议

1. **统一 JSON 提取层。** `_parsing_utils`（stdlib）与 `structured_output`（json_repair）两套并存，Form C orchestrator 又有第三套 marker-search。收敛到单一 `structured_output`，删除或降级其余。

2. **持久化 retry/gate 预算（C4）。** checkpoint payload 加 `retry_counts`；resume 恢复并设累计上限。这是"错误恢复"维度根本缺口。

3. **抽出 phase 公共骨架（M24-M29）。** 提供 `@phase(input_slots=..., output_slots=..., manifest_type=...)` 或 `PhaseRunner` 基类，phase 只写 `build_goal`+`post_validate`。消除 5+5+5 处复制粘贴，使 C2/C3/C10/M19 类 bug 局部化。

4. **明确 P10↔P10c 信任契约（C6）。** P10 emit 校验不足，P10c 重做一遍——说明设计默认 P10 不可靠。二选一：P10 emit 全验证（P10c 仅冗余），或 VoiceClip schema 显式标注 `audio_path` 为"未验证，由 P10c 校验"。

5. **RoutingBridge 命令构造改参数列表 + `shlex.quote`（C1）。** 重评 `--dangerously-skip-permissions` 暴露面。

6. **统一 LLM 调用 retry/backoff 抽象。** P14 `_call_llm`（M4）/ glm_advisory / debate 零重试，单次 429 即触发 C2/C16。共享 `call_glm_with_retry(...)`（指数退避 + 429/5xx 重试）。

7. **ffprobe 缺失改硬失败 + 大文件 manifest 弱指纹（C9/C5）。** 媒体完整性校验不应因工具缺失而放行；大媒体不应全量入内存哈希。

8. **校验覆盖补齐（M10/M11）。** 为 p03/p09b/p10b/p10c/p14/p15 + blackboard 模式 `shots` 数组补 L4 检查；`_PHASE_EXPECTED_COUNTS` 补齐微门 phase。

9. **统一原子 JSON store（M17/M18）。** `_manifest`（无锁非原子）与 blackboard（temp+rename 但每写全量 flush）共享一个 `atomic_json_store` 工具（flock + temp+replace + 增量）。

10. **单点失败隔离（C10/M19/C16）。** P11/p10b/debate 的并发批处理都应 per-item 捕获预期异常、继续收集、循环后聚合上报，而非首错即中止整批。

---

## 统计

- **审查文件数：** 48 / 48（全量逐文件阅读）
- **代码行数：** 27,128
- **发现问题总数（主表已归类）：**
  - Critical：20（C1-C20）
  - Major：62（M1-M62）
  - Minor：35（m1-m35，精选；另有零散风格项见附录）
  - 合计主表：117
- **>100 行函数（高密度区）：** p11 `run` 673、p01 `run` 533、p04 `run` 458、p12 `run` 407、p10 `run` 401、p06_blackboard `run` 380、p09 `run` 349、p13 `run` 321、p11 `_goal_for_shot` 311、p07 `run` 225、p07 `_filesystem_fallback` 175、p14 `run` 259 / `_run_rule_checks` 206、p13 `_render_master_mp4_fallback` 195、p10b `_run_body` 194、p10c `run` 230、p09b `run` 254、debate `run_debate` 217、p03 `_build_audit_goal` 221、orchestrator `run_form_c` 228、p02 各 comedy builder ~55×5。
- **`except Exception` 吞异常：** 101 处（全仓）。
- **`shell=True` / `eval` / `exec`：** 0（已 grep）。命令注入风险集中在 routing_bridge 的 `tmux send-keys` 拼串路径（C1），非 `subprocess shell=True`。

---

## 附录 A：P14 / P15 详细发现

### CRITICAL（已并入主表 C2）
- **P14 LLM 宕机→false P11 re-render** — `p14_quality_audit.py:1053,1074-1076,911-913,787`。详 C2。

### CRITICAL（主表 C9 关联）
- **P14 硬编码 sqlite 路径**（主表 M38）— `p15_canvas_sync.py:232`。

### MAJOR（已并入主表）
P14 死阈值常量（M1）、节拍阈值不符（M2）、规则引擎缺守卫（M3）、`_call_llm` 三重脆弱（M4）、quality-audit 阻断不同步画布（M41）；p15 sqlite 泄漏（M39）、状态文件未守卫（M39）、`_canvas_helper` int 推迟（M40）。

### MINOR
- p14 ffprobe 重复（m1）；p15_feedback 非原子写/覆盖历史（m2）、写失败仍标 written（m3）；p15_canvas_sync 双 sys 引用（m4）、p15 前缀碰撞（m5）；p14 LLM key 泄漏面（m6）。
- p14 bare-except 吞根因清单：`176-177`/`192-194`（返 0,0 隐 ffprobe 损坏信号）、`519`（config probe）、`1187-1190`（manifest）、`1208-1209`（sync_canvas）；p15 `130-132`（DB 查询丢栈）、`223-227`（per-phase sync 截 200 字符）。
- 正向：`_canvas_helper._safe_filename` 正确消毒；p15_feedback `_safe_filename`（L60-65）路径安全。

### >100 行函数
p14 `run` 259（953-1211）、`_run_rule_checks` 206（225-430）、`_run_beat_sync_regression` 168（565-732）；p15_canvas_sync `run` 153（135-287）；p15_feedback `run` 118（193-310）。

---

## 附录 B：共享 helper 详细发现（`_manifest` / `_prompt_template` / `_reference_*` / `_framing_perturbations` / `quality_gate_error`）

### MAJOR（已并入主表）
`_manifest` 写无锁非原子（M18）、内容门降到 1（M42）、`write_manifest` 149 行多职责；`_reference_loader._assert_safe_url` 只捕 gaierror（M43）；`extract_formula_params`/`derive_judge_exemplars` 各 ~106/103 行。

### MINOR（精选）
- `_manifest.validate_text_coverage` 一旦有任何 description 即无法检出 orphan（`530-558`）。
- `_manifest` 边 `node['id']` 直接下标可在校验前 `KeyError`（`322`）；无 schema_version（m34）。
- `_prompt_template` prompt-injection 面：未消毒串入 LLM goal（`128-130,159-187`）；`extract_section` 未知 marker 抛未捕 `ValueError`（`398`）；`count_constraints` 不强制 MUST/MUST NOT 格式（`307-326`）。
- `_reference_loader` 本地文件全量 `read_bytes()` 后才查 size（`260-274`，与 URL 流式读不一致）；`getaddrinfo` 无 timeout（`91`）。
- `_reference_judge.derive_judge_exemplars` 顺序调 delegate 欠用 GLM=3 并发（`164`）。
- `quality_gate_error._auto_instructions` 直接下标 finding（`49-55`）。

### 正向（值得肯定）
- `_reference_loader` SSRF 防御彻底：scheme allowlist、private/loopback/link-local/reserved/multicast 拒绝、fetch 前 defense-in-depth 再查、流式 size 守卫。已验证 `ipaddress` 正确分类 IPv4-mapped IPv6（`::ffff:169.254.169.254`→is_private）。
- `_reference_formula`/`_reference_judge` 篡改缓解细致：显式拒 bool、`[0,1]` 范围、非空串、换行折叠。
- 7 文件无 `eval`/`exec`/`shell=True`/`pickle`。

---

## 附录 C：P09 / P09b / P10 / P10b / P10c / `_parsing_utils` 详细发现

### CRITICAL（已并入主表）
p10c `text=None` 崩溃（C3）；p10b 假基线（C7）；p09b numpy 死导入（C8）；p10 音频不校验（C6）。

### 该代理其余 CRITICAL（经主审定级下调/并入）
- **p10c `_measure_mean_volume` ffmpeg 参数注入（audio_path 以 `-` 开头）** — `p10c_voice_audit.py:66-70`。list 形已防 shell 注入，但 ffmpeg 自身参数解析仍把 `-` 开头路径当 flag。内部信任边界，defense-in-depth。**定为 MAJOR。**
- **p09 `_inject_scene_refs` 路径遍历** — 主表 M44。
- **p10c `_measure_mean_volume` 宽 `except Exception` 关闭安全网** — `p10c_voice_audit.py:79-80`。未来 typo（`match.group(2)`）会静默禁用静音检测。**定为 MAJOR。**

### MAJOR（已并入主表或此处补充）
p10 retry 丢片无审计（`p10_voice.py:511-518`）；p09 5 处 fix-required 复制（M25）；5 处 sync_canvas 复制（M26）；p09 静态阈值 30/35 不符（M45）；p10b `@glm_concurrent` 误用（M20）；p10b 单变体击沉整集（M19）；p09 `next_shot_link` 强制失败静默保留（`p09_shot_breakdown.py:1460-1470`）；p09 `_compute_element_durations` 原地改 input（m11）；p10b `_derive_new_value` 对字符串基线崩（`p10b_rapid_preview.py:124-125`）；`_parsing_utils` 不修 trailing comma/控制字符/截断/大写 fence（M47）。

### MINOR（精选）
p09 `_count_cjk_chars` 标点表不全（m9）；`re.compile` 热循环（m8）；p10b `_resolve_keyframe` cwd 相对（m10）；p10c 顺序 ffmpeg N 次（m7）；p09/p10 manifest 写复制（m11 关联）。

### >100 行函数
p10 `run` 401、p09 `run` 349、p09b `run` 254、p10c `run` 230、p10b `_run_body` 194 / `run` 110、p09 `_inject_scene_refs` 101。

### 跨切观察
- 两套 JSON 提取哲学并存（`_parsing_utils` vs `structured_output`），p09b/p10c 都不用——建议收敛。
- P10 emit 校验不足 + P10c 重做 = 设计上默认 P10 不可靠（见架构建议 4）。
- p09 多个 post-delegate 校验器原地改 `expert_output` 且无 asset_bus 审计轨迹，operator 无法对比 LLM 原始输出与最终 shot-list 的 diff。

---

## 附录 D：P01-P05 + `_creative_hook_selector` 详细发现

### CRITICAL（已并入主表）
Form C 审计造假（C12）、Form C 跳过 `_validate_audit`（C13）、`_glob_agent_artifact` 跨 episode 泄漏（C14）、P02/P03 缺 candidate_index（C15）。

### 该代理 CRITICAL（经主审定级）
- **P02 story-framework 无 schema 校验** — `p02_outline.py:151-156`。下游 P03 `.get()` 非 dict 级联 `AttributeError`。**定为 MAJOR（M10 关联）。**

### MAJOR（已并入主表或补充）
`_select_final` 死 if/else + 误名（M50）；5 个 comedy builder 复制（M28）+ `_build_overkill_solution_goal` 死代码；P05 孤死仍声明 Gate 4（M30）；`_is_comedy_genre` 三处复制（M29）；超长 `run()`（M24）；manifest 块 5 phase 复制（M27）；Form C `_run_agent` 吞所有异常转 `_error:True`（M31）；`_validate_requirement_feasibility` 114 行多职责（`p01_hook_topic.py:207-321`）。

### MINOR（精选）
P03 manifest 死重算（m23）；死常量/未接线 prompt（m24）；拼写 `_CicHE`（m25）；P04 硬编码 `/home/kai/` + episode_id 未消毒（m26）；P01 写未声明 slot（m27）；Form C 未替换 `{placeholder}`（m28）；P02/P03 import 期 `int(env)` 可崩（m22）；`_glob_agent_artifact` 冗余非递归 glob（`orchestrator.py:254-266`）；`_detect_dialogue_loops` 参数误名 `max_scenes`（`p03_script_audit.py:128-156`）。

### >100 行函数
p01 `run` 533、p04 `run` 458、p03 `run` 281、p02 `run` 178、p05 `run` 168、`_creative_hook_selector._select_impl` 168、`run_form_c` 228、`_build_audit_goal` 221、`_validate_audit` 144。

---

## 附录 E：P11 / P12 / P13 详细发现

### CRITICAL（已并入主表）
ffprobe 缺失/超时即 valid（C9）、P11 `_ffprobe_mp4_valid` 仅查 duration>0 不解码（C9 关联）、BgmTriggerError 中断整批（C10）、P11 非 dict shot 崩 `_dispatch_shot`（C10 关联，`p11:1091-1094,1034`）、P13 fallback 忽略 resolution+color_intent（C11）。

### MAJOR（已并入主表或补充）
超长函数（M24：p11 `run` 673、`_goal_for_shot` 311、p12 `run` 407、p13 `run` 321、`_render_master_mp4_fallback` 195、p12 `_verify_master_audio` 106）；死预序列化 JSON（m16）；P11 per-shot 重序列化（M34）；P11 缓存命中伪造指标（M32）；P13 无幂等（M35）；P13 `_verify_master_mp4` 默认软跳缺失（M36）；P12 静音守卫仅 prompt（M37）；P11 启动 ffprobe 风暴（M33）；P11 硬编码 cd 路径（m17）；P11 子串误报（m18）；宽 except 清单（`p11:646,1064`、`p13:529-532`）。

### MINOR（精选）
P11/P12 未用 `_oss_url` 导入（`p11:1185`、`p12:662`）；ffprobe 超时不一致（m21）；P11 `take-log` slot 形状歧义（`p11:1182`）；P12 LUFS -16 vs 平台 -14（`p12:479,485-490`）；P11 并发 set 读+写（`p11:1078,1159-1160`，GIL 下最坏冗余渲染）；P11 BGM retry 无本地 cap（`p11:1107`）；函数内 import（`p11:1013`、`p12:161-162`）；P11 `_save_p11_progress` O(N²)（m19）；P13 watermark 全片重编码（m20）；P12 beat 分析走 subprocess 而非 import（`p12:264-272`）。

### 正向
三文件无 `shell=True`，全用 list 形；P13 fallback concat list 由整数 idx 构造（注入安全）；P11 checkpoint 用原子 `os.replace`（`p11:325`）；P11 `@glm_concurrent`+`parallel_shots=MAX_CONCURRENT_GL` 正确双限 3（`p11:1067,595`）；P12 FIX-03 pairwise `amix=inputs=2` 避已知 3+ 输入 hang（`p12:418-444`）。

---

## 附录 F：基础设施模块详细发现（`ratelimit` / `creation_logger` / `debate` / `glm_advisory` / `engine_adapters` / `phase_models` / `phase_output_schema` / `phase_contracts`）

### CRITICAL（已并入主表）
debate `resolve_all_conflicts` 吞非-DebateError（C16）；creation_logger 跨进程追加非原子（C17）。

### MAJOR（已并入主表）
`retry_on_429` 子串误报（M21）；`glm_limiter` 对 async no-op（M22）；畸形 `GLM_MAX_CONCURRENT` 静默（M23）；`VoiceClip._dialogue_requires_text` 静默改写（M5）；`_check_field` 类型宽松（M6）；`get_phase_checks` regex 死代码（M7）；`SchemaValidationError` 双定义（M8）；creation_logger 吞写错误零可观（M46）。

### MINOR（精选）
`_utc_iso` TOCTOU（m29）；`episode_id` 消毒不全（`/` 与空格 only，`creation_logger.py:104`）；per-line fsync 昂贵（`creation_logger.py:132-133`）；`close()` 非线程安全（`creation_logger.py:499-501`）；`read_log` 整文件入内存（m30）；debate ```json fence 脆弱（`debate.py:310-317`）；`run_debate` ~217 行；`assert ruling is not None` 生产 -O 被剥（m31）；`max_rounds>2` 静默按 2（m32）；`resolve_all_conflicts` 串行（`debate.py:650-664`，N 冲突 2N 串行 round-trip）；glm_advisory 无 429 retry + 5 宽 except（m33）；`MAX_CONCURRENT_GL` import 期解析无 reload（`ratelimit.py:158,267`）；`_resolve_max_concurrent` 重 import（`ratelimit.py:121,128`）。

### 正向
- `ratelimit._resolve_max_concurrent` 自动发现 Hermes 上限并强制 pipeline 为最保守消费者——设计精良。
- `glm_advisory` 非阻塞契约实现正确：每失败路径记 `degraded`/`skipped` 并返，`finally` 总 kill tmux，按设计不可死锁。
- `engine_adapters/duration.py` 干净：ABC + LTX/Identity 具体 + registry + 无状态线程安全单例。
- `phase_output_schema._resolve_path` 支持 `key.subkey`/`[*]`/`[0]` 三形（但嵌套 wildcard 未被任何 check 覆盖，值得加单测）。

---

## 附录 G：P06 / P07 / P08 详细发现

> 本区间专职子代理首轮触发 GLM 429 限流失败，重试后完成；下述为重试代理逐行阅读 + 主审交叉核实（`blackboard.py` / `phase_output_schema.py` / `runner.py` 并发接线）合并结果。

### CRITICAL（已并入主表 C18/C19/C20）
- **blackboard 模式 Layer-4 跳过 `spatio-temporal-script.shots`**（C18）— `p06_blackboard.py:401` vs `phase_output_schema.py:435-455`。空 shots 静默流向 P07。
- **editor pacing wrapper-key 不匹配 → 每镜 `pacing=""`**（C19）— `p06_blackboard.py:717-782, 596-643`。相对串行静默数据丢失。
- **P08 scene-selection 裸 list vs schema `{"scenes":[...]}` → FieldCheck 全 no-op**（C20）— `p08_scene_selection.py:117-119` vs `phase_output_schema.py:490-499`。scene_id/selected 完全未查。

### 该代理其余 CRITICAL 候选（经主审定级 MAJOR）
- **blackboard 内存/盘分歧 + subagent 直写被覆盖** — `p06_blackboard.py:261,271-274` + `blackboard.py:225-226,256-271`。goal 指令让 subagent 直写 blackboard.json，但 orchestrator 每次 `_flush_to_store` 从内存整文件重写会**覆盖** subagent 直写条目；又 `BlackboardAgentTool` 全仓 grep 仅出现在 docstring、从未接线——"synthesis is no-op"注释自相矛盾。当前生产流（orchestrator 串行 parse 后写）下 RLock 实际够用，但设计面是陷阱。**M51/M61 关联，定为 MAJOR。**

### MAJOR（已并入主表 M51-M62）
debate_pending 半接线（M51）；final_audit 错形状 fallback（M52）；p06 manifest 吞 ValueError（M53）；p07 过宽 except 掩盖 parser bug（M54）；p07 fallback 硬编码调色板无视 genre/mood（M55）；p07 硬编码 operator workdir（M56）；p06/p07/p08 死 JSON 局部（M57）；p08 无 retry/fallback（M58）；parser 失败静默丢 agent 数据（M59）；`_summarize_l2` 过滤 episode 级致 L3 看不到 audit（M60）；无唯一性强制（M61）；超长函数（M62）。

### blackboard（blackboard.py，主审手记，已并入主表）
- `time.time()` 非 monotonic（M16）；每 write 全量 flush O(n²)（M17）；`_flush_to_store` 吞所有异常（m14）。
- 并发模型澄清：`runner._make_production_parallel_delegate` 用 **ThreadPoolExecutor（线程，进程内）**，worker 返回结果后由**主进程**写 blackboard → `threading.RLock` 对当前 L2 writer 有效。但仍是单进程抽象；goal 文本却声称 subagent 直写（见上 MAJOR），二者矛盾。建议文档化"单进程内并发"前提，或加 `fcntl.flock` 支持直写。
- `_load_from_store` 逐条 `from_dict(cls(**d))` 遇 schema 漂移抛 `TypeError`，被 L244 吞 + warn——可接受但丢条目。
- `detect_shot_design_conflicts` O(n²) pairwise（`blackboard.py:507-509`），大 board 可察觉。

### MINOR（精选）
- bare-except 清单：`p06_blackboard.py:299,436,481`；`p07:125,238,580,672`；`p08:185`；`p06_spatio_temporal_script.py:387`；`blackboard.py:236-240,244-248,266-271`（load/flush 吞，corrupt blackboard.json 不可见）。
- 路径遍历面：`episode_id` 入 `f".../runs/{episode_id}"`（`p07:205-217`）+ hint_paths `split("/assets/S07")`（L210-217）可注入任意根；operator 控制故低危，仍建议 `^[A-Za-z0-9_-]+$` 校验。
- p07 `img = {}` 循环内重赋值死代码（`p07:613-616`），致 `desc_parts` 查找全 None。
- p06 串行 `_shots`/`shot_count` 冗余双算（`p06_spatio_temporal_script.py:291-298`，首遍死）。
- `_serialize_pacing_str` 用 `*args` 易调错（`p06_blackboard.py:785-803`），改显式签名。
- 魔法数：`p06_blackboard.py:349`("Run 19 physics checks" 无源)、`473`(conflicts[:5])；`p06_spatio_temporal_script.py:267`(_DIALOGUE_RATIO_THRESHOLD=0.30 函数内)、`100`(sentinel set)、`324`(截 120 字符)。

### 该代理 Top-3 优先修
C18（一行 SlotCheck）、C19（一行 wrapper-key）、C20（包 dict 或改 FieldCheck 形）——均为相对串行基线的静默正确性回归，修复小而独立。

---

## 附录 H：核心编排模块主审手记（runner / routing_bridge / structured_output / blackboard / phase_contracts / phase_models / phase_output_schema / phases.__init__）

- **runner.py**：四层防护 + manifest 校验 + 多重 timeout 沙箱设计扎实；主要问题为 C4（retry 不持久化）、C5（大文件哈希）、M15（parallel_delegate 无超时）、M48/M49（bus 类型字符串判定 / 回滚改循环变量）。`_make_sandboxed_delegate` 的 SIGALRM 在 worker 线程失效已诚实文档化。
- **routing_bridge.py**：C1（命令注入）为最大风险；M12（会话泄漏）、M13（静默回退偷换模型）、M14（固定 sleep）。done-token + `__EXIT__<rc>` 契约设计合理；`_session_name` 用 `secrets.token_hex` 防同秒碰撞是亮点。
- **structured_output.py**：json_repair 多策略提取稳健；`_SCHEMA_KEYS` 硬编码（m15）+ 4 处 `except Exception: pass`（m15）。
- **phase_contracts.py**：`extra="forbid"` + frozen 契约边界优秀；唯 `object.__setattr__` 绕 frozen（M5）破坏不变量。
- **phase_models.py**：仅 P01 有模型（M10 根因）；docstring 诚实承认其余 phase "parses bare"。
- **phase_output_schema.py**：L4 引擎设计良好（dotted path / `[*]` / candidates 拒绝）；M6/M7/M8 为细节 bug。
- **phases/__init__.py**：`PHASE_REGISTRY` 线性 DAG + `_GATE_PHASES` SSoT + `derive_gate_id` 统一——清晰。注意 p15_canvas_sync **不在 registry**（仅 p15_feedback 在），canvas_sync 经 Phase 37 on_phase_complete 订阅者触发，非独立 phase。
