# Beat Performance Pipeline

> 把"3D角色一段连续表演"(如 揭小贤走到茶桌坐下冲茶讲解)拆成 Beat(与配音cue逐句对齐的最小动作单元), 每个 Beat 走 设计→实现→多维evaluator(workflow)→loop 的生产管线。核心物料=【坐标契约 scene-layout.json】 (角色/道具/落座点/抓取点共用一份坐标,authoring与runtime一致=手到壶到)。当任务涉及:分镜化角色表演、 与配音同步的动作序列、道具拿放(re-parent)、走路/坐下/倒茶类动作、茶汤/重力类物理合理性检查、 逐beat评审 —— 时使用本 skill。配套 workflow:项目 workflows/beat-eval-loop.star。 ⚠️ 活文档:揭小贤茶艺表演的每个 beat 实作经验都往本文件追加。

- Skill: `cyl19970726/beat-performance-pipeline` (Agent Skill)
- Install (CLI): `npx skillmds@latest add cyl19970726/beat-performance-pipeline`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cyl19970726/beat-performance-pipeline/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: cyl19970726 (https://skillmd.com/u/cyl19970726)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/cyl19970726/beat-performance-pipeline

---


# Beat 表演生产管线(分镜化 3D 角色表演)

## -1. 目标 / 最终验收标准 / 技术栈(评审与决策的基准,先读)

### 目标(一句话)
扫功夫茶冰箱贴 → 揭小贤完整演出【临现招手 → 走到茶桌 → 坐下 → 拿壶 → 六式冲茶(烫杯/纳茶/高冲/低斟/关公巡城/韩信点兵) → 敬你一杯】的 29.6s 表演,**与配音逐句同步**,AR 尺度下可爱、对版、物理可信、流畅 —— 让用户的体感是"**一个小向导刚刚请我喝了杯功夫茶**"。

### 最终验收标准(全部满足才算这一期完成)
1. **功能**:点「听讲解」后 B1–B14 全序列自动演完;每 beat 起止与配音 cue 误差 <0.3s;「再听一次」从 B3 重入且道具已复位。
2. **质量闸**:每个 beat 过 Tier A 全部不变量 + Tier B 五维(**判定规则:每维 双模型≥1 pass,且 ≥4/5 维 pass;双模型对同一维分歧→以"更严"一方的 issues 为修复输入,修后复评**);正面机位全程无可见穿模;所有道具拿/放无瞬移(re-parent 连续)。
3. **物理**:坐姿支撑(髋在凳上/脚踩地)、壶倾角规则(非倒茶<15°)、茶流重力(±5°)、端杯水平(<10°)全部机检通过。
4. **性能**:iPhone Safari 真机流畅(无 >1s 卡顿);本期新增 3D 资产合计 ≤3MB;模型可靠加载(此前 5.3MB 曾加载失败,3.2MB 稳)。
5. **体验(Tier C,唯一终审=用户)**:真机看整段"像小向导请你喝茶"——生动、可爱、不出戏;用户签字才封板。
6. **工程**:vitest 全绿;走 worktree+PR+merge;skill/契约文件同步更新。

### 技术栈选择(及为什么——都是验证过的结论,别再重选)
| 层 | 选型 | 为什么(替代方案为何不用) |
|---|---|---|
| 角色动画 | **Blender 程序化 IK+bake** | 已验证(Greet/Pour);Mixamo 要登录且 502 过;Tripo preset 与 mixamo-spec 骨架不兼容且无茶艺动作 |
| 走路位移 | clip 原地走 + runtime position lerp | 游戏标准技法;烘根位移会和锚定/重播打架 |
| 道具建模 | **Tripo text_to_model** + gltf-transform 狠减(28MB→<1MB) | 静物是 Tripo 甜区;手工建模不必要 |
| 对版 2D | **codex 图生图**(-i 锁参考) | 已验证可出对版可爱图;是"美术手感"的来源 |
| 口型 | **2D 说话脸 + 离线振幅包络** | 3D 口型在此资产上四法皆败(死路已证);Web Audio 在 iOS 截断配音(已踩) |
| 时序编排 | **audio.currentTime = 唯一时钟**驱动 beat 状态机/口型/字幕 | 单时钟零漂移(已验证);不用独立计时器 |
| 空间布局 | **scene-layout.json 坐标契约**(authoring与runtime共读) | "手到壶到"的根基;杜绝散落硬编码 |
| 茶流/热气 | billboard sprite(条件显隐) | 移动端流体模拟不现实;sprite 够读、够轻 |
| 评审 | Tier A python/Blender 机检 + Tier B harness 双模型 workflow + Tier C 用户 | 三层闭环已验证(口型一役);缺陷只下沉不上浮 |
| 运行时/部署 | MindAR+A-Frame / GCE+cloudflare | 既有栈不动;iOS 非 meshopt+webp 铁律 |

## 0. 为什么用 Beat
一段 30s 连续表演没法整体评审/迭代;**Beat = 与配音 cue 逐句对齐的最小动作单元**(0.7–4s),
每个 beat 有自己的【物料/动作规格/可机检不变量/视觉评审点】→ 评审环才有抓手,失败只重做一个 beat。

## 1. 生产流程 v2(2026-06-12 全量复盘后重设计;旧"拿到什么先做什么"流程作废)
> 任务本质=一出微型舞台剧。**工程件自底向上(runtime/管线),内容件自顶向下(剧本→分镜→资产→舞台→表演)**——旧流程所有返工都源于把内容也自底向上做。

```
阶段0 立项   北极星+验收标准+工具能力实证清单(依赖的能力先真实成功调用一次才进计划)
阶段1 剧本   配音文案→TTS→cue表=唯一时钟
阶段2 分镜★  先出**空间设定包**(2a):场景平面图/侧视图(从坐标契约正交渲染,非画的)+人-桌-凳-茶具尺寸关系图
            +道具三视图(turnaround)——作为分镜与建模的**输入**;再(2b)codex 逐cue生成分镜
            (每格带全套茶桌茶盘,空间一致,有位移的格要画出移动轨迹/起止位)→评审→【用户批G0】
阶段3 资产★  从分镜推导道具清单→输入标准先行(对版参考/A-pose/无道具)→生成→压制gate
            →Tier V 视觉美感闸(§2.5)→资产总览用户过目;原点/bbox实测,不许拍脑袋
阶段4 舞台★  照分镜摆3D:原点修正+bbox进契约+可达性预验(臂长vs所有交互点)
            →"3D vs 分镜"并排图→【用户批G1】→契约冻结(冻结前禁止下游并行)
阶段5 表演   逐beat串行闸:codex设计→codex脚本(/tmp工件)→主循环Blender执行(每次干净导入)
            →Tier A(本地确定性,不依赖provider)→渲染(机位=分镜机位)
            →Tier B(基准=像不像分镜那格)→过闸入库;walk/sit优先动捕,程序化只做手势
阶段6 集成   白名单导出→资产链路检查表(新模型必须确认动画已重绑)→【无视觉证据不得部署】→真机=用户终审
```

### 1.5 验收矩阵(2026-06-12 G0-v3 批准后立;每步三层验收,缺一层=该步未完成)
| 步骤 | ① 自动脚本验收(确定性,本地) | ② 视觉验收 | ③ 代码验收 |
|---|---|---|---|
| 3a 道具参考图 | 文件存在>100KB;**轮廓比例机检**(PIL 前景 bbox,如桌 h/w∈[0.32,0.48]) | 主循环对版分镜+美感初筛 | — |
| 3b 建模+压制 | GLB 可 parse;bbox 三轴比例进容差;压后≤单件预算、总和≤3MB | 4 视角渲染→双模型 Tier V(对版/美感/压缩损伤/语义,基准=参考图+分镜) | 压制参数有出处 |
| 4 舞台 v3 | 接地误差<0.005;桌上物 y=桌面±0.005;bbox 互不穿插;出生点+路径点距桌 footprint≥0.05;全交互点 reach≤0.95×臂长 | 正交三视图+与 sb_S 对位并排图→双模型+主循环(G1') | 契约 diff 审:**每个数字写得出推导来源** |
| 5 动画重校 | 每 clip 自检 print PASS(脚穿/髋漂/触达/躯干上限/循环闭合) | (并入渲染步统一看) | 脚本 diff 审:只许坐标/时序变,逻辑不许漂 |
| 6 渲染+Tier B | 帧数=窗口×24±1;**角色入画机检**(前景 bbox 占画面高 25-70%);strip 拼装 exit 0 | 每 beat 单帧先审→全量;beats-visual-batch 双模型 5 维,**基准=对应分镜格**;主循环终筛 GIF | 机位表逐项=分镜镜头表 |
| 7 集成 | 导出 clip 清单=JYX_BEATS 引用集合(脚本读 GLB 比对);beat 窗口铺满 [0,29.6] 无缝隙无重叠(脚本验);build/tsc 0 错;资产总预算复核 | 本地 preview 逐 beat 截图证据包(=「无视觉证据不得部署」的证据) | runtime diff claude 关键位审 |
| 8 部署+终审 | 部署后 curl 200;线上资产 hash=本地 | **真机=用户终审** | — |

**横切纪律(根因→规则)**:
- 交付=验收物在手且过闸;"命令跑完/HTTP200/机检绿"都不算完成。
- 产出物校验闸:子任务空产出/只有开场白=FAILED 熔断上报,禁止静默当"没问题"下传。
- provider 评审撞 infra=阻断,绝不降级放行;核心不变量本地确定性化。
- 单变量迭代+两轮不涨熔断;一切渲染先单帧过目再全量。
- 角色分工:codex=生成产能(分镜/参考图/动作设计/脚本/取证);主循环=关键位(审实物/Blender执行/美术终筛/集成);用户=G0/G1/终审三个拍板点。
- 完整复盘报告(6根因+证据)见 session-forensics-v2 run(2026-06-12)。

## 2. 核心物料:坐标契约 scene-layout.json(一切空间关系的唯一真源)
```json
{ "units":"character_height=1.0", "anchor":"magnet center, x右 y上 z朝用户",
  "P0_spawn":[0,0,0.05], "table_pos":[...], "table_top_z":0.xx,
  "stool_pos":[...], "seat_hip":[...], "pot_on_table":[...],
  "cups":[[c1],[c2],[c3]], "gaiwan":[...], "walk_path":[[..],[..]] }
```
- **Blender 动画 authoring 与 runtime 布局读同一份契约** → 手伸向 `pot_on_table` 的 IK 目标 = runtime 壶的真实位置 = 手到壶到。
- 改布局=改契约文件,动画与场景同步变;**不许在代码/脚本里散落硬编码坐标**。
- ⚠️ **坐标系映射(已实证冻结,2026-06-11 B0)**:`blender = (c_z, c_x, c_y) × H + (0,0,z0)`,其中 H=角色 Blender 世界身高(实测 0.997≈1.0,契约单位≈Blender 米),z0=脚底。循环置换、行列式+1,相对几何全保。**推导曾给出错的 `−c_x`(把桌放反到角色右侧),靠"放标记物→渲染→对照 AR ry=270 真实朝向"才抓出来——映射永远实证,别信推导。**角色在 Blender 面朝 +X=契约 +z(朝用户);角色左手侧=+Y=契约 +x。runtime 侧契约直接用(A-Frame 锚系原生),无转换。
- **时序契约**:`audio.currentTime` 是唯一时钟;**beat 状态 = currentTime 的纯函数**(给定 t 能无状态推导出:当前 beat、walk 位移比例、壶/杯父子关系、茶流显隐)→ 暂停/seek/重播/锁屏中断天然正确。**禁止事件驱动累积状态**(中断后对不上)。重听=seek 到 7.5s(B3 起点)。

## 2.5 道具(资产)evaluator —— 任何道具进场景前必过(2026-06-12 被用户抓漏后强制化)
**字节/三角数 gate(Tier A)只是底线,不是验收。** 每个道具(桌/凳/壶/杯/盖碗/…)必须再过:
- **Tier V 视觉美感闸(双模型 workflow)**:Blender 渲 正面+3/4 两视图 → codex+claude 评 ①**对版**(潮汕红绿金描金、Q版潮玩质感,和揭小贤同一世界观?)②**美感**(造型/比例/贴图好看吗)③**压缩损伤**(狠减面后破相/穿洞/UV撕裂?)④**语义正确**(空茶盘就该是空的——Tripo 常夹带私货,如"桌上自带一把壶"与可抓壶打架,必须重生成而不是凑合)。
- **不过 → 重生成/换提示词/换来源,不许"先用着"。** 道具是每一帧都在镜头里的,丑一件毁全场。
- 流程:`Tripo 生成 → 压到 gate → 渲两视图 → 双模型视觉评审 → 过才进契约摆位`。

## 2.6 Beat 验收的三条铁律(同日补,防再犯)
1. **Tier B 视觉评审(beat-eval-loop.star)是强制步,不是可选**——"主循环自己瞄 filmstrip"只能当预筛,不算评审;每个 beat 必须跑双模型五维,记录在案。
2. **涉及位移的 beat(走路/移动)验收物必须带空间位移**:Blender 预览时给 armature 对象层加沿 walk_path 的位移动画(只为渲染,不烘进 clip),GIF 里看到"从 A 走到 B"——原地踏步的 GIF 验收不了"走到茶桌"。
3. **验收 GIF 一律上线 `public/beats/`**,用户看的是动图不是静帧。

## 3. 通用不变量(所有 beat 都检;**每条标注测量层** B=Blender scrub / R=runtime 探针 / V=视觉评审)
> **测量层原则(评审纠出的关键)**:Blender 只测得到骨骼/物体变换;**走路 lerp、re-parent 连续性、茶流 sprite、音频生命周期是 runtime 现象,必须用 R 层测**——做法:页面挂 `window.__jyxBeatProbe`(每帧记录 t/beat/关键世界坐标/倾角),headless Chromium(Playwright)跑一遍录下来→python 断言;V 层=渲染给视觉 agent/人。

- (B) 坐姿/站定期间 髋漂移≈0;**双脚 y≈地面**;躯干-桌沿距>0(不入桌)。
- (B) **可达性**:任何 IK 目标到肩的距离 ≤ (上臂+前臂)×0.95(Q版短臂,坐姿高冲/伸桌拿壶都要先验这条,不可达就改契约位)。
- (R) **道具 re-parent 帧世界变换连续**:快照世界矩阵→换父→反算 local;探针记录切换前后世界位置差 <1% 身高。
- (R) 走路防滑步:lerp 位移速度 = 步频×步长 ±10%。
- (B+R) **液体物理(统一阈值,单位一律【度】)**:出流阈值全局唯一 **>25°**(B 层检壶倾角轨迹,R 层检 sprite 显隐与倾角一致,滞回 ±3° 防闪烁);非倒茶时段壶倾角 **<15°**;端杯全程杯倾角 **<10°**;(R)茶流方向≈重力 ±5°,起点=壶嘴,终点≈正下方;高冲流长、低斟流短。
- (R) **茶汤状态机(从无到有)**:三杯液面(小圆片)= t 的纯函数——B9 巡城起出现、到 B10 末升至 70%("七分满");B13 端起的杯必须带茶面。简化声明:不模拟烫杯弃水,液面只进不退(AR 尺度够)。
- (R) **音频生命周期**:播放必须由用户手势启;锁屏/来电/切后台 → audio 暂停 → beat 纯函数自然冻结,恢复后从 currentTime 继续;「再听一次」= seek 7.5s 且道具状态由纯函数即时重算(无须手工复位)。
- (R) 道具拿起期间手-道具距恒定;表演结束(t>29.6)所有道具回契约初始位。
- 性能:**本期新增 3D 资产逐项预算**(总 ≤3MB):桌 ≤1.0MB(2.74 还要再压)/凳 ≤0.4/盖碗 ≤0.3/杯×3 ≤0.45(单杯 0.15)/壶 0.34(已达)/茶流+热气+液面 sprite ≤0.1;角色 GLB 增 4-6 个 clip 约 +0.05MB(clip 很小)。AR GLB 非 meshopt+webp。

## 4. 揭小贤·茶艺表演 Beat 细化表(活记录,29.6s 配音对齐)
> 配音 cue:0-4.5 你来啦/小向导 | 4.5-7.5 来先坐下 | 7.5-12 待客泡茶 | 12-14 工夫二字 | 14-14.7 烫杯 |
> 14.7-15.4 纳茶 | 15.4-16.1 高冲 | 16.1-16.9 低斟 | 16.9-18 关公巡城 | 18-19.2 韩信点兵 |
> 19.2-21.1 都有学问 | 21.1-26.3 品字/七分满/人情 | 26.3-29.6 敬你慢慢品

> **时间戳=cue 显式绑定**(状态机/机检/重播 seek 的依据);倾角单位一律【度】;不变量后括号=测量层(B/R/V)。

| Beat | [t_start,t_end] | 物料 | 动作要点 | 特别注意(物理/空间) | 关键不变量 |
|---|---|---|---|---|---|
| B0 茶席组装 | (离线) | 桌/凳/壶/3杯/盖碗 GLB + **scene-layout.json** | Blender 统一尺度朝向;桌高≈角色腰;**第一件事:实证坐标系映射** | 尺度=角色身高1.0;按概念图 scene_1 摆 | 桌高=0.45±0.05;契约真值回写;映射函数冻结 |
| B0.5 临现招手 | (点击前,不计时) | Greet clip(已有) | 循环招手,茶席已在右侧 | 等「听讲解」;**点击=用户手势解锁 audio** | (已验收的 Greet) |
| B1 走步 | **[0.0,4.5]** 哎/你来啦/我是揭小贤/小向导 | WalkInPlace clip | 2-3步弧线+lerp位移+渐转向 | **防滑步**;下摆穿模→抬腿小;绕开桌角;bob 2-3%;4.5s走~0.5单位=慢悠悠正好 | (R)速度匹配±10%;(B)脚不入地;(R)终点=凳前±5% |
| B2 坐下 | **[4.5,7.5]** 来/先坐下/喝杯功夫茶 | SitDown clip+凳 | 髋**先后移到凳上方再降**;膝~90°;脚触地 | 长袍穿模→桌面遮挡;凳高=契约seat_hip | (B)落座髋=凳面±2%;脚触地;髋xy=凳心±5% |
| B3 拿壶 | **[7.5,9.0]** 我们潮汕人待客 | (壶在桌) | 右手IK→pot_on_table→抓→提 | mitten手:壶贴掌心柄朝外;**先验可达性**;re-parent快照法 | (B)抓帧手壶距<2%;(B)可达;(R)切换位置差<1% |
| B4 持壶介绍 | **[9.0,14.0]** 头一桩…工夫二字 | — | 持壶轻晃+左手比划 | **壶口朝上不漏茶** | (B)倾角<15° 全程 |
| B5 烫杯 | **[14.0,14.7]** 烫杯 | 茶流sprite | 壶横扫三杯上方浅倒(snap) | 0.7s=17帧:**只做"扫"一个要素**,无预备 | (B)壶x扫过≥2杯;(R)流随壶 |
| B6 纳茶 | **[14.7,15.4]** 纳茶 | — | 左手向壶口捏指撒茶 | 左手别穿壶/脸 | (B)左手-壶口距<5% 一次 |
| B7 高冲 | **[15.4,16.1]** 高冲 | 茶流(长) | 壶猛提向下倒,标志动作 | **壶别穿帽/头**;**先验坐姿可达**——够不到头顶就改"尽臂长最高点" | (B)壶y>可达上限90%;倾角>40°;壶-头距>阈 |
| B8 低斟 | **[16.1,16.9]** 低斟 | 茶流(短) | 壶降到杯沿高贴近倒 | 与高冲对比明显 | (B)壶y<桌面+2杯高;倾角25–40° |
| B9 关公巡城 | **[16.9,18.0]** 关公巡城 | 茶流 | **持续出水匀速横扫三杯往返** | 壶嘴对准杯列;流终点依次划过三杯 | (B)壶x单调扫3杯再返;倾角>25°全程;平滑 |
| B10 韩信点兵 | **[18.0,19.2]** 韩信点兵 | 茶流(滴) | 三次点滴,一杯一点 | 每点壶嘴对准杯心 | (B)倾角峰=3;每峰壶x=杯x±半杯距 |
| B11 放壶 | **[19.2,21.1]** 一道道都有学问 | — | 壶放回桌+直腰点头 | 壶底贴桌;放下倾角→0 | (R)壶位=初始±2%;(B)倾角<5° |
| B12 品字手势 | **[21.1,26.3]** 品字/七分满/人情 | 液面sprite升至70% | 左手摊掌指三杯 | 茶汤状态机在此段完成"七分满" | (R)液面=纯函数(t) |
| B13 敬茶 | **[26.3,29.6]** 来/这第一杯敬你/慢慢品 | 前杯re-parent | **双手捧前杯向用户前伸+微躬定格** | **杯水平<10°(带茶面)**;杯不穿桌 | (B)抓帧手杯距<阈;前伸>15%;杯倾<10° |
| B14 复位 | **[29.6+]**(ended) | — | 杯归位→坐姿呼吸idle | 状态=纯函数(t),重播seek 7.5s即自洽 | (R)道具=契约初始位 |
## 5. 实现技法(已验证的,引用配套 skill)
- 动画 authoring:Blender IK 摆位→渲染迭代(我当眼)→`nla.bake`(visual_keying;Blender 5.x 用 `bpy.ops.pose.select_all` 不是 `bone.select`)→ ACTIONS 导出多 clip。见 `blender-mcp`。
- 走路位移:clip 原地走 + runtime 对 `a-gltf-model` position lerp(标准游戏技法)。
- re-parent:`bone.add(obj)`/快照世界矩阵;A-Frame 实体可被 three bone.add 收编(`jyx-handprop` 组件)。
- 口型=2D 说话脸(振幅包络),与 beat 并行不冲突;字幕=HTML 层。
- 道具来源:静物=Tripo text_to_model(28MB 原始→gltf-transform simplify 到 <1MB);对版 2D=codex 图生图。

## 7. Runtime 重构清单(2026-06-11 代码库交叉审计,30 条对抗验证确认;开工前必读)
**Blockers(不修没法做 beat)**
1. **无 beat 状态机**:jyx-anim 是 2 态事件机,从不读 currentTime → 重写为 **beat→clip 调度器**:startNarration 的 rAF(已是唯一时钟消费者)解析 beatId 并发布;每 beat 配 clip 名/loop|one-shot/`action.time = t − beatStart`(纯函数,支持任意 seek 进场)。
2. **jyx-talk-start 立刻播 Pour**(与 walk-first 冲突)→ 删 toTalk/toIdle 二元;Greet 只作 B0.5 待机。**顺带抓到现行 bug:actions 注册表只有 Greet/Idle/Talk,'Pour' 从没注册——线上讲解时其实在播 Talk!**
3. **jyx-handprop 永久挂手+只切可见性**(壶凭空出现在手=瞬移)→ 重写为 beat 驱动 re-parent:`Object3D.attach` 语义,B0 在桌上、B3-enter 挂手、B11-enter 放回;**永不拿可见性当抓取**;杯(B13)复用同组件。
4. **无位移系统**(角色 position 静态)→ B1 walk lerp + B2 髋路径要建,且=纯函数(t)。
5. **GLB 只有 4 clips**——WalkInPlace/SitDown/GrabPot/六式/敬茶全缺(Blender 排产清单)。
6. **teatable 2.61MB 独占预算 87%** → 再压(≤1.0MB)。
7/8. **打断与重听**:二元状态机表达不了"走一半静音";再听一次永远从 t=0(违验收#1)→ 全由"纯函数(t)+seek 7.5s"解决。

**Majors(精选)**
- **talkface 是固定位 sibling——角色一走,2D 嘴脸就和头分家** → talkface 必须挂头骨(同 handprop 思路)或每帧跟随头骨世界位。
- **scene-layout.json 零代码读取**(坐标全硬编码/URL)→ runtime 加载契约。
- **双时钟问题**:mixer 走 wall-clock dt、beat 走 currentTime → iOS 卡顿/中断会撕开 → mixer 也改由 t 驱动(`action.time = t − beatStart` 一并解决)。
- 2D teaboard.webp 与 3D beat 冲突(杯是像素抓不了)→ B0 后弃用,换 3D 茶席。
- **B1 反直觉:4.5s 是超预算**(0.5 单位拉满 4.5s = 慢动作)→ 走 ~2.5s + 到位后 0.5s 整理 + 招手过渡填满。
- B5–B10 不变量轴写错(对照契约应为 x 横向)→ 已在表中用 x。
- 结束/静音都回"站立 Greet"=瞬移 → 结束态=坐姿 idle(纯函数 t>29.6 段)。
- char GLB 3.33MB 已贴"稳定上限",新 clip 须以 ACTIONS 增量导出(clip 很小)验证总量。
- targetLost 中 narration 继续但按钮卸载(无法停)→ targetLost 暂停 audio(纯函数自然冻结)。
- B3 可达性实测 97–116% 臂长(临界!)→ **契约 pot_on_table 朝角色挪近**(B0 校准时定)。
**Minors(记账)**:crossfade 按 beat 配置(快拍 0.08–0.12s,位移拍 0.2–0.3s);茶流 sprite 系统列入 B0 todo;**删除死重 11.8MB teapot.glb**;cues 4.5s 处 50ms 重叠;audio.play() 拒绝被吞(加 catch 提示);组件卸载取消 rafRef;页面总重 ~10.4MB 需关注。

**二跑补全(36 条确认+完整综合)的精确化增量**
- **阈值定值**:B7 壶-头距 >0.08(B0 按头网格 bbox 标定);B13 抓帧手-杯距 <0.02(与 B3 同量级)。
- **B1 子分配(定稿)**:招手 0–2.0s(盖住"哎/你来啦/我是揭小贤")→ 走路 2.0–4.0s(0.26u/s,**3 步**@1.5步/s)→ 转身 4.0–4.5s(~250°/s,Q版可接受)。原"4.5s 慢悠悠正好"作废(=1/3 步速的慢动作漂移)。
- **契约 schema 增 4 字段**(B0 从 GLB bbox 自动提取回写):`pot_spout`(茶流起点/B6 左手目标)、`pot_grasp`(抓取点≠壶中心,柄朝外)、`cup_dims{height,radius}`(B8 用)、`table_bbox`(躯干-桌沿距的"沿")。
- **结束态规则**:t≥B2 末 → 桌边 **SitIdle**(新 clip,排产+1);否则 → 出生点 Greet。静音/中断=冻结(pause+保持姿态),**绝不路过站立 Greet**(防穿桌瞬移起立)。
- **MJ-11 targetLost 中场**:narration 播放中丢锚 → `audio.pause()`+冻结 mixer+pill 提示"对准冰箱贴继续看表演";re-found → 同 t 恢复(纯函数天然续上)。静音按钮在播放中常驻(不随 cameraState 卸载)。
- **MJ-12 单时钟落实到 mixer**:`mixer` 不喂墙钟 dt,改 `dt = currentTime − lastT`(或 `setTime(beatLocalT)`);补 onPause/onStalled/onWaiting;**beat 转换一律电平触发**(状态=纯函数,re-parent 幂等),漏帧自愈。
- **MJ-9 GLB 体积工程**:导出保持动画优化(基线 4 clips=18.7KB,导出后断言动画总字节<200KB);需要余量时 **gltf-transform quantize(KHR_mesh_quantization,iOS 可用、不是 meshopt!)可把几何 2.79MB→~1.4MB**——⚠️ 早前"quantize 会 prune skin"的坑要先小样验证再上。
- **逐资产 gate(实测锚定)**:桌≤0.8(**已达 790KB/21.6K tris ✓**)/盖碗≤0.4/杯≤0.25(**1 个 GLB 运行时克隆 3 实例,绝不出 3 份文件**)/凳≤0.25/sprite≤0.05/角色clip增量≤0.12 → 总计 ~1.7–2.1MB,3MB PASS 有余量。
- **re-parent 不变量的家**:vitest(真 three 场景+GLB 骨架)+ Playwright runtime scrub;Blender 只保诚实代理(抓取帧手骨世界位 vs 契约 <2%)。"手-道具距恒定"删除(同义反复)。

## 5.5 决策记录(用户授权自主决策后,每个 gate 决定记录在此,用户保留否决权)
- **2026-06-12 G0 分镜**:有条件通过——审分镜时发现**配音"来,先坐下"的主语是观众**(主人邀客),不是角色自己坐下 → 叙事重定向为"坐姿主人版":格1=坐席后招手、格2=向观者请坐手势(重生成),格3-7 不变。walk/sit 从关键路径移除。
- **2026-06-12 纪律**:「无视觉证据不得部署」即刻生效(阶段6 硬闸)。
- **2026-06-12 战略**:C 先行(坐姿主人版到可发布)+ B 增强(Mixamo 动捕 walk 入场,等用户下载后插回;runtime beat 表加行即支持)。
- **2026-06-12 G0 用户反馈(部分否决上一条)**:分镜方向获认可,但①**走路必须回来**——补"走入→落座"过程格,角色是真实位移不是静止换姿势(纯坐姿版作废,改为完整版:走入→招手→落座→请坐→茶艺);②每格分镜必须显示全套茶桌茶盘(场景一致性);③暴露 workflow 重大缺口:图像生成缺**空间设定层**(平面图/三视图/尺寸关系)→ 已补为阶段2a,空间设定包=分镜与建模的输入。3D 茶桌与分镜不对版,批 G0 后照分镜重建。
- **2026-06-12 分镜 v3(走路版)**:叙事=走入挥手(0-3s)→落座(3-4.5s)→请坐手势(4.5-7.5s)→茶艺(同前)。sb_0/sb_1b 已生成,原 sb_1(坐席挥手)退役。待用户批 G0-v3。
- **2026-06-12 道具全面设计先行(用户两次斥茶桌丑后立规)**:**每个道具都要"设计→多候选参考图→美感筛选→建模→Tier V 对版"**,不许直接文生3D 拍脑袋。新茶桌三视图设定已出(矮长条深红木+金雕花,对版分镜);存量道具(壶/盖碗/杯/凳)逐个对版分镜重审,不对版的重做。
- **2026-06-12 道具链终态**:桌v3(mv多视图,h/L 0.343)PASS;壶v3(紫砂)WARN接受;盖碗(青花)WARN接受;杯v4(空杯)重抽过损伤闸;凳=v2(对版过/太矮胖)**本地 z×1.6 拉伸定型**——因 **Tripo 积分耗尽(code 2010)**,凳v3建模未跑成,本地拉伸是免积分折衷(凳90%被遮挡)。⚠️ 需要更多建模时请用户充值。
- **2026-06-12 桌高 0.45→0.36**:Tier B 揭示坐姿可见带只剩 0.17(肩仅高出桌面0.07,俯拍又全是Q版巨帽=「只见帽子」),分镜里桌面只到坐姿下胸口。降到 0.36 → 可见带 0.26(头+肩+上胸)。教训:**道具尺寸要按"坐姿可见带"反推,不是按"站高比例"拍**。
- **2026-06-12 渲染欠曝总根源=会话引擎被切到 BLENDER_WORKBENCH**(无视灯光+AgX压暗)——查 engine 应是渲染排障第一步,在调灯之前。
- **2026-06-12 Tier B 渲染评审三轮熔断**:轮1全必修(暗+机位平=桌面墙)→轮2涨分仍必修(俯拍=帽伞)→轮3 改分镜对齐中景(sb_4构图:正面略俯20°人居中半身)。教训:**特写机位对Q版大帽角色 = 低仰角中景,俯角越大越只剩帽**。
- 决策原则:每个决定亮牌给用户、附理由、可否决;执行中发现决定错了→改决定并记录,不硬扛。

## 6. 踩坑追加区(每做一个 beat 往这里记)
- **2026-06-11 开工前评审(双模型,11/11 critic 产出)在 B0 之前抓出 12 个设计缺口**——时间戳列缺失/单位混用/茶流阈值矛盾/坐标系映射缺失/Tier A 层错配/茶汤无状态/可达性没人验/音频生命周期/3MB无清单 等,全部已回写本文档。教训:**生产文档自己也要过评审环**,"写得顺"≠"自洽可执行"。其中最值钱的一条:把 beat 状态做成 **currentTime 的纯函数** 一举解决 暂停/seek/重播/中断 全部边界。
- **2026-06-11 B0 真机首炸:scale 弹出动画 × 单位测量 = 道具巨型堆叠**。角色入场有 `scale 0→1.5` 弹出动画,`jyx-locomotion` 在 model-loaded 时量身高 → 量到 ~0 → `__jyxUnit` 无效 → 所有 `jyx-place` 永远等待 → 道具以原始尺寸(米级)堆在原点,巨型白杯吞掉角色。修:① 删 scale 弹出动画(正确性>花哨)② 测量改 tick 重试直到"值>0.3 且连续两帧稳定" ③ **道具就位前一律 visible=false**(防原始尺寸闪堆,这条升级为通用规则)。教训:**任何"等模型加载后测一次"的逻辑都要怀疑入场动画/异步时序**;隐藏直到就绪是默认姿势。
- **2026-06-12 B1-walk 首个 beat 闭环跑通,沉淀 5 条**:
  ① **脚踝≠脚底(必踩坑)**:foot IK 目标是脚踝,authoring 写"着地=y0"会让脚底入地 ~2%身高 → 所有脚部 IK 目标统一 +`ANKLE_FIX≈1.8%H`;Tier A 的"脚不入地"逮住了它(这就是闸的价值)。
  ② **beat-build workflow 实战形态**:codex 设计+落脚本(/tmp/beat_scripts/<beat>.py 工件)质量可以很高(631行体系完整);**claude 评审 worker 掉线时,主循环顶上当关键位**(本来就更强:能直接跑验证)。
  ③ **脚本必须自带自检 print**(逐帧脚z/髋漂移/循环角差/PASS 总判)→ 主循环 grep 即 Tier A,零额外脚本。
  ④ **验收 GIF 标准通道 = repo `public/beats/<beat>.gif` 随部署上线**,用户手机开 URL 看动图签字;Next 生产模式不认 build 后塞进 VM 的文件(404),别走捷径。
  ⑤ **坐姿一致性(B2 起的硬规矩)**:B2 坐下的**末帧姿势存成 /tmp/beat_scripts/seated_pose.json**(骨→四元数),B3-B14 所有坐姿 clip 从它加载作基底——否则每个 clip 的"坐姿"各坐各的,切 beat 必跳。
- (逐 beat 继续填)

