--- title: 0.4.0 版本规划:渲染产能、配合稳定性与效果扩面 --- # 0.4.0 版本规划:渲染产能、配合稳定性与效果扩面 > 0.1.0 打通「模型 → AnimationSpec IR → MP4」全链路,0.2.0 把工作台「亮出来」,0.3.0 把元素类型从 4 种撑到 13 种并补上资产管线与导演 preset。 > 0.4.0 的主题是**产能**:真机用下来,制约出片效率的已经不再是「能不能画出来」,而是三件别的事——模型写错烧来回、改一帧等整片重渲、想配乐没音轨。所以三条主线: > 1. **稳与准**:继续压掉「模型 ↔ 工具 ↔ 宿主」配合上的摩擦(别名收敛、大纲对账、渲染期警告进回执、渲染成本预期管理),产出与预期不符的形态逐一堵住; > 2. **快**:渲染运行时提效——编辑器加载等待先测量后动态化、vite/浏览器实例常驻复用、**场景级增量渲染**(微调后重渲从「整片分钟级」降到「只重渲改过的幕」); > 3. **广**:效果扩面——音轨(audio 图层)、字体资产消费、旁白字幕条、转场扩族与幕尾退场、缓动扩族、代码演化动画、preset 视频技巧包。 --- ## 1. 遗留问题盘点与处置 ### 1.1 README「已知限制与说明」逐条处置 | # | 已知限制 | 现状核实 | 0.4.0 处置 | | --- | --- | --- | --- | | 1 | 旁白 / 字幕轨道是 IR 预留字段,未实现(涉及 TTS 与音画对齐) | `narration` / `subtitles` 零消费方 | **部分落地**:字幕不依赖 TTS——`narration.cues`(文本旁白)本版本渲染成字幕条(§4.3);TTS 语音合成与音画自动对齐继续推迟 0.5(§8) | | 2 | 工作台面板只读(P2 交互未做) | 卡片无按钮;阻塞点是真机拆包 `dsh-client-connection` | **M3 执行,拆包前置**(§5):先真机拆包验证 client→host 通道,通过才落「撤销/预览/渲染」按钮;不通过则按钮继续推迟,只做 preview 后台化 | | 3 | `/dsh-anim/media` 放行规则(outputDir 内或回执精确路径 + 扩展名白名单) | 安全边界,工作正常 | 维持。audio 产物(mp3/wav 等)若要面板试听,扩展名白名单按需补充 | | 4 | 插件事件落 sidecar、不进宿主会话日志 | fail-closed 约束仍在,机制稳定 | 维持。宿主若开放 `ignorable` 写入再评估切回,无代码动作 | | 5 | 渲染依赖 MC 3.17 编辑器 UI 自动化,单次约 4 秒编辑器加载 | `runtime.ts` 固定 `sleep(4000)` + 每渲重启 vite/浏览器(O10,0.3.x「先测再动」留真机) | **本版本主体之一**(§3 M1):真机测量 → 动态等待替代固定 sleep → vite/浏览器实例常驻复用 | | 6 | Windows `dsh plugin add` 的 pnpm 转发问题 | 文档已有绕过方案 | 维持文档,无代码动作 | | 7 | (本轮核实新增)`anim_asset_import` 支持 `kind: font / audio`,但渲染端**零消费** | `copyAssetsToPublic` 会把 font/audio 文件复制进项目 public,codegen 与项目模板没有任何引用——导入字体静默无效、导入音频无处可挂(详见 §1.4 N1) | **补账**:audio 图层让 audio 资产变活(§4.1);font 资产生成 `@font-face` 注入(§4.2)。素材四类在本版本全部可用 | ### 1.2 0.3.0「明确不做(推到 0.4+)」逐项处置 | # | 项 | 0.4.0 处置 | | --- | --- | --- | | 1 | TTS 旁白与音画对齐 | **继续推迟到 0.5**。依赖外部服务与授权;本版本先把「文本旁白 → 字幕条」做了(§4.3),音轨 mux 基建也顺手落掉(§4.1),0.5 的 TTS 只需在音轨清单上多一种来源 | | 2 | 字幕轨道 | **本版本落地**(跟随 1.1 #1):以 narration cues 为唯一事实渲染字幕条,`subtitles` 字段维持预留不动,不做双轨 | | 3 | audio 图层(静态音轨 + ffmpeg mux) | **本版本主体之一**(§4.1):BGM / 音效、场景内音轨、渲染尾步 ffmpeg mux | | 4 | loopback RPC 直改 spec | **继续推迟**。面板按钮走 `agent.followup()`(§5.2),两条路径产出的事件相同;直改通道等 followup 路径真机跑稳后再评估 | | 5 | Remotion / Manim 渲染 Provider | 继续推迟。渲染 seam 就绪,多后端维护成本高;本轮把 Motion Canvas 单后端的产能与效果做厚 | | 6 | 场景 camera(平移/缩放) | 维持放弃(0.3.0 已正式放弃,不挂账) | | 7 | store 快照优化 | 维持观察 | | 8 | 画布拖拽式编辑、3D / 粒子效果 | 维持不做 | ### 1.3 0.3.x 优化清单的遗留处置 | # | 项 | 0.4.0 处置 | | --- | --- | --- | | 1 | O10 渲染固定开销(4s 编辑器等待 + 每渲重启 vite/浏览器) | **升级为本版本主体**(§3 M1)。0.3.x 的口径「沙箱无浏览器无法测量、先测再动」在 M0 真机复验时一并补上测量 | | 2 | O16 cards.tsx 拆分(触发点 = 面板交互启动) | **M3 执行**(§5.4):面板按钮一旦启动,每张卡片都要加状态与事件逻辑,先拆 `styles.ts` + `primitives.tsx` + 按卡分文件 | | 3 | O19 `anim_preview` 同步挂住模型回合 | **M3 执行**(§5.3):jobs 化 preview,复用 BackgroundTicket 卡片模式(轮询缩略图) | | 4 | O12/O13 微效率(量级不支撑) | 维持不做,记录在案 | | 5 | 真机复验待办(圆形修复、line/arrow/ellipse/star/svg/code/math 出片复核;离线打包验证) | **M0 开工基线**(§2.5):0.3.0 的真机复验在本版本开工时闭环,作为一切渲染侧改动的对照基线 | | 6 | 告警重复打印(O21 留观察:同批告警按幕 × 渲染次数重复刷屏) | **并入 §2.3 告警治理**:渲染期警告进回执时按内容指纹去重 | ### 1.4 本轮体检新发现(2026-09-16,为 0.4.0 规划做的全库复查) > 复查范围:`packages/{spec,store,render-mc,tools,client}` 全部源码 + 真机文档三份。编号 N1~N7,均已排进本版本;后续体检发现续编。 | # | 发现 | 证据 | 处置 | | --- | --- | --- | --- | | N1 | **font / audio 资产是「死登记」**:`anim_asset_import` 放行四种 kind,`copyAssetsToPublic` 照抄不误,但 codegen 与项目模板对 font/audio 零引用——模型导入字体后静默无效(中文若靠它解决就直接豆腐块),导入音频无处可挂 | `ops.ts` ASSET_KINDS 四类;`adapter.ts` copyAssetsToPublic 不分类型复制;`codegen.ts` 全文无 font/audio 消费 | audio 由 §4.1 落地;font 由 §4.2 落地。落地前先在 `anim_asset_import` 工具描述标注「font/audio 当前不生效」,避免模型继续踩 | | N2 | **属性别名纠错逻辑两处手工同步**:codegen 在渲染期改写 `color→fill`、`strokeWidth→lineWidth`;`coerceScene` 只认 strokeWidth。同一模型错形,写幕时不纠正(anim_get 里落库的是别名),渲染时才改写(且 color 是静默改写,无警告无 repairs) | `codegen.ts` emitNode 两处别名分支;`ops.ts` coerceScene 仅 strokeWidth | **收敛为一份共享别名表**(§2.1):spec 包导出 `PROP_ALIASES` 唯一权威源,codegen(静态+轨道两侧)与 coerceScene 共同消费,冒烟加一致性断言;coerceScene 补 `color→fill`,让规范名在写库时就落对 | | N3 | **大纲只进事件不进状态,无从对账**:`opPlan` 落库即发 `anim/outline-updated`,但 `foldEvents` 明确忽略它,store 里没有 outline——大纲写了 5 幕、实际只 draft 了 4 幕、总时长偏差 40%,工具全程不说话,模型和用户都不容易察觉 | `events.ts` AnimOutlineData;`store/index.ts` foldEvents default 分支注释「outline 不影响 spec 状态」 | **outline 进状态 + 三处对账**(§2.2):store fold 保留最新 outline,`anim_draft_scene` / `anim_render` 回执追加「大纲 vs 实际场景」软警告 | | N4 | **渲染期警告不进回执**:codegen 的生成期降级(不支持属性、兜底、资产解析警告)只 `console.warn` 进宿主日志——模型在 `anim_render` / `anim_preview` 回执里看不到任何一条,看片才发现「描边没了、图没加载」 | `adapter.ts` #renderFrames 仅 console.warn;RenderResult / PreviewResult 契约无 warnings 字段 | **警告进回执与事件**(§2.3):契约加可选 `warnings`,渲染卡片展示;按内容指纹去重防刷屏 | | N5 | **无渲染成本预期管理**:`anim_plan` 的 pacing 只体检单幕(<1.2s / >8s),全片总时长无任何档位提示——模型可以规划出 5 分钟的片子,然后面对 30 分钟渲染超时;`anim_render` 回执也不报预期帧数 | `ops.ts` opPlan 体检逻辑;RenderResultView 无 expectedFrames | **成本预期管理**(§2.4):plan 增加全片时长档位提示,render 回执带 expectedFrames 与预期口径 | | N6 | **转场只有 4 种且只有入场**:`Transition.kind = none/fade/slideLeft/slideUp`,语义是「本幕入场动画」;没有 slideRight/slideDown/zoom,也没有幕尾退场(教学片常要「本幕内容淡出再切下一幕」,现在模型只能手工给每层写 opacity 轨道) | `types.ts` Transition;`codegen.ts` 转场生成段 | **转场扩族 + `Scene.exit` 退场**(§4.4) | | N7 | **代码演化动画已存在但无人知晓**:MC 的 CodeSignal 自带 diff 形态补间 tweener(已拆 3.17 包核实:`CodeSignalContext.tweener`),`props.code` 轨道写多个字符串关键帧(atMs 递增)经现有轨道机制就生成 `code(新值, 时长, 缓动)` 的 morph 动画——零生成端改动。但 types 注释写着「字符串/布尔为离散跳变」,模型被引导着不这么用 | `@motion-canvas/2d/lib/code/CodeSignal.d.ts` tweener 签名;`types.ts` Keyframe 注释;`codegen.ts` emitTracks 对字符串关键帧照常生成带时长补间 | **零改动能力转正**(§4.6):工具描述加「代码演化」写法 + 冒烟断言生成 TSX 含带时长的 code 补间 + 真机验收 | --- ## 2. 主体 A:配合稳定性与产出准确性(稳·准) ### 2.1 属性别名收敛为一份共享表(N2) - `packages/spec` 新增 `PROP_ALIASES: Record`(别名 → 规范名),首批 `strokeWidth → lineWidth`、`color → fill`;后续再踩到新别名只改这一处; - 三个消费方:codegen `emitNode`(静态属性)、codegen `emitTracks`(轨道目标)、`coerceScene`(写库边界)。「规范名已显式给出时别名不覆盖、冗余别名照常告警」的既有语义表驱动化; - `coerceScene` 补 `color→fill`:规范名在写幕时就落库,`anim_get` 读到的就是规范形态,repairs 清单同步回报; - 冒烟:别名表 × 三消费方一致性断言(防止再长出第四份手抄)。 ### 2.2 大纲进状态 + 三处对账(N3) - store:`foldEvents` 消费 `anim/outline-updated`,`SpecRecord` 增 `outline` 字段(最新一次大纲;重建/回放天然一致); - `anim_draft_scene` 回执:写入后比对 outline——幕数已超出大纲、场景 id 与大纲 id 对不上、单幕实际时长与大纲建议偏差 >50% 时给软警告(`repairs` 同级的「对账」区块,中性色); - `anim_render` 回执:大纲存在但场景数不符或总时长偏差 >30% 时提示「先对齐再渲,避免整片重渲」; - 面板:OutlineCard 已靠 presentationMeta 重建,无需改动。 ### 2.3 渲染期警告进回执与事件(N4 + O21 观察项) - 契约:`RenderResult` / `PreviewResult` 增可选 `warnings: string[]`(向后兼容);`anim/render-finished` 事件载荷同步带 warnings(渲染期警告与回执同源); - 去重:按「图层 id + 属性 + 兜底动作」指纹去重——同一错形在多幕重复出现只报一条(附幕数),O21 的百行刷屏从此收敛; - 卡片:渲染/预览卡片区分两类展示——「已自动兜底」(中性色,与 SceneCard 的 repairs 同风格)与「需要处理」(黄色 warnings); - 同步与后台两条渲染路径都要过(sync 回执 + job done 载荷)。 ### 2.4 渲染成本预期管理(N5) - `anim_plan` pacing 增加全片档位:总时长 >60s 提示「渲染以分钟计,建议控制节奏或接受长渲」、>180s 提示「接近默认 30 分钟渲染超时的一半,谨慎扩片」; - `anim_render` 回执增 `expectedFrames`(fps × 总时长,即渲染目标帧数),与进度事件的 total 同口径; - 工具描述同步「长片耗时以分钟计;微调后可用 scenes 抽查 + 增量渲染(0.4.0 起)省时间」。 ### 2.5 真机复验基线(0.3.x 收尾,开工第一件事) 沙箱没有浏览器与 dsh CLI,0.3.0 留下的真机复验在本版本开工时闭环,作为渲染侧一切改动的对照基线: 1. `dsh web` 真机出片复核:circle 修复四形态、line/arrow/ellipse/star/polygon/svg/code/math 全类型各出一幕; 2. O20(首写自动纠错)与 O21(strokeWidth/textAlign 别名 + vite CJS 告警)两批修复的回归确认; 3. O10 测量:记录「canvas 出现 → Render 按钮可点」实际耗时,为 §3.1 的动态等待定参; 4. 离线打包验证(offline-packager → tgz → 安装 → 出片)。 --- ## 3. 主体 B:渲染产能(快) ### 3.1 编辑器等待动态化(O10,先测量后动手) - `await sleep(4000)` 改为动态等待:`canvas` 出现后轮询「Render 按钮存在且 enabled」(或编辑器全局状态),就绪即走;兜底超时(取自 §2.5 实测分布的 P95 × 2)回落为固定等待,绝不让多环境差异变成渲染失败; - 复用场景下(§3.2)编辑器已热,动态等待天然退化为近零等待; - 约束不变:headless 参数与 SwiftShader 红线一根手指都不碰。 ### 3.2 vite + 浏览器实例常驻复用 - **浏览器**:进程级单例,插件生命周期内复用;每次渲染开新页 goto → 渲完关页;空闲 10 分钟回收整个浏览器; - **vite dev server**:按 specId 一份(root=workDir,见 §3.3),同一 spec 的连续「patch → preview → render」热循环全程复用;空闲随浏览器回收策略一并关停; - **健壮性红线**:每次复用前健康检查(浏览器进程存活、页面可评估、server 可达),任一失败即丢弃实例走完整冷启动——**复用是优化,冷启动永远是保底正道**;渲染串行闸维持,实例状态不存在并发竞争; - 预期收益:单片省 4~10 秒固定开销;热循环里(预览 → 微调 → 再预览)收益翻倍。实测数字写进 §9 进度表。 ### 3.3 workDir 按 specId 分子目录 - `work` → `work/`:vite 依赖预打包缓存(`work//.vite`)按项目隔离并复用,不再「每个 spec 首渲都付一次预打包」; - 增量渲染(§3.4)的段缓存、音频物化等中间产物随之获得独立目录,互不踩; - 兼容:现有 `resetDir` 只清 `work//output`,不动缓存;spec 删除不回收(与 outputDir 产物同生命周期,`anim_diagnose` 报告磁盘占用)。 ### 3.4 场景级增量渲染(本版本最大的「快」) > **实施修订(M1 验收实测后)**:分段方式从「切片一次渲染、按声明时长切段」改为「**缺失幕逐幕 solo 渲染**」。实测发现 MC 编辑器内每幕实际占帧带 reset 帧 / 补全帧(tween 收尾的幕多渲一帧端点帧),任何按声明时长预测的边界都恒有 ±1 漂移——切片段尾会裹进下一幕的帧,下一幕改动后这帧以旧缓存泄入成片(真机复现:改色后上一幕段尾残留旧色一帧)。逐幕 solo 渲染的段内容恰好是本幕画面(实测 solo 帧与整片中该幕逐帧 MAD≈0),正确性由构造保证、不依赖 MC 内部边界规则,代价是 k 幕缺失付 k 次编辑器加载(§3.2 常驻复用下单次加载仅 ~1.4s)。段文件名带 `r2` 代次隔离旧方案缓存。 - **可行性根据**:场景之间没有跨场景状态——转场是「本幕入场动画」、每幕是独立 generator,单幕画面只由本幕 JSON + 渲染参数决定。场景级缓存因此是安全的; - **指纹**:场景 JSON 稳定序列化(键排序 + 去格式化)+ 渲染参数(fps / scale / 分辨率)联合 hash;资产内容变化不参与指纹(src 相同即命中,文档写明「换图不换名要先删资产」); - **流程**:`anim_render` 时逐幕比对段缓存(`work//segments/seg--r2-.mp4`)→ 未变幕直接复用,缺失幕逐幕 solo 渲染成段(原子写:临时名 + rename)→ ffmpeg concat(段间同 fps / 同分辨率 / 同编码参数,concat demuxer `-c copy` 零重编码)+ ffprobe 时长校验(ffprobe 缺装时跳过校验,concat 退出码兜底); - **音轨不缓存**:audio mux 永远在 concat 之后从现行 spec 重新执行(§4.1),改音量不用清缓存; - **保底**:`cache: false` 参数强制全量渲染;增量流程任何一步失败(solo 渲染 / 切段 / 拼接 / 校验)自动回退全量并告警——**绝不静默交残片**的既有红线不变; - **配套**:`anim_diagnose` 增缓存占用报告(各 spec 的段数与体积); - **验收**:改一幕关键帧后重渲,耗时 ≈ 单幕渲染 + concat(对照数字见 §9 M1 行);增量产片与全量产片逐帧比对内容等价(边界槽位允许 ±1 帧的相邻幕静止内容差异,无内容错位、无陈旧帧);删除缓存目录后全量渲染结果不变。 --- ## 4. 主体 C:效果扩面(广) ### 4.1 audio 图层:BGM 与音效(P0) - **IR**:`LayerType` 增 `audio`;props:`src`(`asset:` 引用 audio 资产)、`volume`(0~1,默认 1)、`loop`(默认 false)、`stop`(`'sceneEnd' | 'specEnd'`,默认 sceneEnd——`specEnd` + loop 即「从这里响到片尾」的全片 BGM 写法); - **渲染**:audio 图层不进 codegen 帧合成(MC 的 Audio 节点不参与导出);adapter 在 encodeFrames 之后按「音轨清单」ffmpeg 二次 mux(多轨 `amix`,`adelay` 对齐全片绝对时间、`-stream_loop` 处理循环、`-shortest` + 显式时长钳制);音轨清单由 codegen 从 spec 收集(场景起点换算全片时间); - **契约**:`RenderResult` 增 `audioTracks: string[]`;成片带音轨写进 README;`anim_preview` 抽帧无音频(写进工具描述预期管理); - **四张面**照例全过(types / validate / codegen 收集逻辑 / register 描述);audio 资产从此变活(N1 销账一半); - **风险**:音画同步与编码器兼容(有音轨时 libopenh264 退路是否仍成立)在真机首验。 ### 4.2 font 资产消费(P0,N1 销账另一半) - codegen 检测到 font 资产时生成 `fonts.css`(`@font-face`,family = 资产 id 净化串,src = `/assets/`)并在 `project.tsx` 注入 import;无 font 资产不生成,项目保持最小; - 工具描述写明用法:导入字体 → text 图层 `fontFamily` 填导入时的 assetId; - **加载时机风险**:headless 下字体未就绪会渲出兜底字形——渲染前在编辑器页 `document.fonts.ready` 等待(点 Render 之前),真机验证; - N1 全面销账后,`anim_asset_import` 的「素材四类」描述从「登记能力」变成「全部可用」。 ### 4.3 旁白字幕条(P1,无 TTS) - **契约**:`narration.cues` 启用:`{ atMs, text, durationMs? }`,`atMs` 定为**全片绝对毫秒**(与场景内时间轴区分,写进类型注释与工具描述);`durationMs` 缺省按中文语速估算(≈4 字/秒,下限 1200ms);`subtitles` 字段维持预留; - **渲染**:codegen 生成期把 cues 展开为各幕的字幕文本图层(底部居中、muted 半透明底条 + 主题文字色、按 cue 时段显隐),与场景图层互不干扰;cue 越出全片时长给软警告; - **不做**:TTS、自动断句分行(超长单行截断 + 警告); - 价值:教学片的旁白有了视觉呈现;0.5 的 TTS 只需把 cues 从「显示」升级为「发声 + 显示」,IR 不用再动。 ### 4.4 转场扩族 + 幕尾退场(P1,N6) - 入场 `Transition.kind` 增 `slideRight` / `slideDown` / `zoomIn`(zoomIn = view.scale 0.6→1 + opacity 0→1); - 新增 `Scene.exit?: Transition`(fade / slideLeft / slideRight / slideUp / slideDown):codegen 在场景尾部预留 `exit.durationMs` 做整体退场(`lastEnd` 计算纳入 exit,避免与轨道补齐逻辑打架); - 向后兼容:exit 缺省无退场,存量 spec 行为不变; - 四张面同步 + 冒烟(exit 与 tail waitFor 的时间分配断言)。 ### 4.5 缓动扩族(P1,低成本) - `EaseSpec` 增 `{ kind: 'bounce' }` / `{ kind: 'elastic' }` / `{ kind: 'back' }`,映射到 MC 的 `easeOutBounce` / `easeOutElastic` / `easeOutBack`(已拆 3.17 包核实导出;强调入场最常用 out 形态,需要 in/inOut 再扩); - 同步四处:`EASE_KINDS`(validate)、`easeExpr`(codegen 映射表)、`anim_draft_scene` 描述、preset 方法论;冒烟加「映射表 × MC 实际导出」断言(`import('@motion-canvas/core')` 断言函数存在,防 MC 升级漂移); - `coerceScene` 的 ease 字符串包装对新品类自动生效(包装后走统一校验)。 ### 4.6 代码演化动画(P0,零生成端改动) - 已核实 MC CodeSignal 自带 diff 补间:`props.code` 轨道写 2+ 个字符串关键帧(atMs 递增)即生成 `code(新代码, 时长, 缓动)` 的逐词 morph——现有轨道机制、现有校验全部直接支持; - 动作只有三件:工具描述加「代码演化」写法示例(并修正「字符串关键帧 = 离散跳变」的绝对化表述:**除 code 外**成立);冒烟断言生成 TSX 含带时长的 code 补间;真机验收 morph 形态(重点看中文注释与高亮共存时的 diff 质量)。 ### 4.7 视频技巧包:preset 方法论 + examples(P1,零代码) 把「IR 已能表达、但模型不知道可以这么用」的技巧沉淀进 `anim-studio` preset 的 persona 方法论,并在 examples 增加演示幕: | 技巧 | IR 写法(现有能力) | | --- | --- | | 片头/片尾结构 | 标题幕(text + fade 入场)+ 总结幕模板,plan 阶段就排进大纲 | | 高亮强调框 | rect 无 fill + stroke + `scale` 呼吸轨道(1→1.06→1 循环两拍) | | 强调星弹出 | star + `{kind:'back'}` 弹入(4.5 的直接受益者) | | 手写线/划重点 | line 的 `end` 轨道 0→1(0.3.0 已支持,配合 4.5 更顺滑) | | 代码演化讲解 | code 图层 `props.code` 轨道(4.6) | | 全片 BGM | 第一幕 audio 图层 `loop` + `stop:'specEnd'`(4.1) | | 节奏模板 | 「讲 30 秒概念」的分镜骨架:引入 4s → 展开 3×8s → 收束 5s(pacing 体检的正面样例) | 验收:新会话选 preset 一句话出片,成片能观察到 ≥2 种技巧;方法论段落有冒烟锚点校验(沿用 preset 文件断言的既有模式)。 ### 4.8 视排期(P2,做不了写 §8) - **逐字打字机入场**:codegen 特判 text 图层的 `props.reveal` 轨道(0~1)→ 生成按进度裁剪文本的 signal;价值真实但涉及文本度量,先给 4.6/4.3 让路; - **video 图层**:嵌入实拍视频片段(MC 3.17 有 Video 节点)——headless 导出下的帧同步未验证,真机验证后再定排期。 --- ## 5. 面板交互与 DSH 配合(M3) ### 5.1 前置:真机拆包 `@deepseek-ai/dsh-client-connection` 0.3.0 §4 的阻塞点原样保留:client(浏览器)触达 host agent inbox 的 RPC/loopback API 确切形态未实证。M3 开工第一件事是真机拆包 + 最小验证(仿 `agentPreset.*` 通道发一条探测指令),**结论写进本文件**;验证不过则 §5.2 不动,只做 §5.3/§5.4。 **✅ 结论(2026-09-17 真机实证,dsh 0.1.6-alpha.1)——验证通过,§5.2 放行:** - **传输**:宿主 HTTP 上的单发信封 RPC。`POST /api//`(endpoint 恰两段,段字符集 `[A-Za-z0-9_$.-]`),请求体 `{type:'client-request', rpcId, method, payload:{args:{<线名>:…}}}`(Content-Type 必须 application/json),响应 `{type:'server-response', rpcId, result:{ok:true,value}|{ok:false,error:{code,message,details}}}`。`method` 必须与 URL endpoint 一致,否则 `gateway/bad-request`。`/api` 由 dsh-api-gateway 的 interceptor 统一接管,仅 typert 已注册的 namespace/method 被 claim(未注册 404)。 - **鉴权**:`GET /?token=`(仅根路径 GET、单 token)→ 303 + `Set-Cookie: dsh-auth-=v1..`(HttpOnly、SameSite=Strict;签名 secret 持久于 credential store,launchToken 每次进程重启重生成)。此后所有 `/api` 请求凭 cookie,缺失/过期 401,Host/Origin fence 不符 403。**面板挂宿主 webServer `/dsh-anim` 前缀、与 `/api` 同源 ⇒ 面板 JS 直接 `fetch('/api/…', {credentials:'same-origin'})` 自动带 cookie**,无需自己铸 cookie。 - **线名(wire)按方法而异,不要猜**:typert.host.js 描述符是唯一权威——`session/list` 用 `_request`,而 `session/prompt`/`session/page`/`session/create` 等用 `request`。args 字段严格校验(多/少/错名都是 `gateway/arguments-invalid`)。 - **`session/prompt` 契约(agent.followup 等价物)**:`{args:{request:{requestId, sessionId, mode:'queue'|'steer', content:[{type:'text',text}|{type:'image',mediaType,data}|{type:'file',receiptId}], clientTimeZone?}}}` → `{accepted:true}`。`requestId` 全程回传进事件 `source.rpcId`(面板指令对账可用);`queue` 投递到 inbox 的 `next-turn` 队列(空闲会话立即开新回合),`steer` 投 `next-step`(运行中插步)。 - **真机探测记录**:向 ptc 预设会话发 queue 探测指令 → `accepted:true` → 会话事件日志依次出现 `agent/inbox/spliced`(target next-turn,source.rpcId 回传探测 id)→ `user/message`(surfaceOp append)→ `assistant/message`「面板通道探测成功。」→ `turn/end`(reason completed),全程 ~4s。通道端到端成立。 - **读回放附注**:会话日志是带 seq 的持久事件日志;`session/page` 只从 head cursor **向后**翻页(`throughSeq` 为含头截断,越界报错信息会回显当前 cursor),live 订阅走 `session/follow` 流式 carrier。面板 v1 不依赖 follow:prompt 发出后由事件流/卡片自身状态呈现结果即可。 ### 5.2 面板最简交互(拆包通过后) - 「撤销这步 / 预览第 N 幕 / 渲染成片」按钮 → 生成结构化指令 → `agent.followup()` 进入下一模型回合执行(设计草案 §6 路径 2,零新增机制,事件流天然可回放); - cards.tsx 先拆分(§5.4)再加按钮,避免单文件破千行。 **✅ 完成(0.4.0)**:落点为「回执盖 sessionId 章(register 收口)+ client `promptAgent` 同源直发 + PanelActions 三按钮 + preset 指令契约」,指令通道即 §5.1 实证的 `session/prompt`(queue);真机端到端验收通过(详见 §9 M3 行)。已知边界(README 同步):指令发往「出卡片的那次会话」——回放/分叉出的旧卡点按钮会把指令发回原会话,v1 接受该边界不做会话存活校验。 ### 5.3 `anim_preview` 后台化(O19) preview 转宿主 jobs(立即返回 + 卡片轮询缩略图),复用 BackgroundTicket 的「落定停表 + 不可达退避」模式;无 jobs 环境维持同步回退。 **✅ 完成(0.4.0)**:代码面(jobs 降级链 / preview 事件 / 任务簿 kind=preview / PreviewTicket)全部落地并冒烟护航。真机 0.1.6-alpha.1 实证 `jobsOnCtx=false`——本部署插件上下文拿不到 jobs 服务,preview/render 走同步回退(设计内),同步预览也进任务簿;后台化在宿主暴露 jobs 的部署上即自动生效。 ### 5.4 cards.tsx 拆分(O16 触发点已到) `styles.ts`(样式常量)+ `primitives.tsx`(Card/Fallback/Warnings/VideoPanel/ProgressBar)+ 按卡分文件;纯移动零行为变更,冒烟的卡片插槽断言护航。 ### 5.5 dsh 版本跟进 真机基线 0.1.6-alpha.1;新 alpha/rc 发布时跑一遍挂载冒烟 + 真机出片,破坏性变更对比清单记进本文件;peer 依赖维持 `>=0.1.5-rc.2`。 **0.1.6-alpha.1 对比发现(2026-09-17 真机)**: - `@deepseek-ai/dsh-persona` 配置 schema:`prefix` 必填、无 `text` 字段——anim-studio 预设此前按旧写法(`text`)维护,真机首次安装即 mount 失败,已改 `prefix`;预设安装位 `~/.dsh/.agent-presets//`; - jobs 服务对插件上下文不可见(`jobsOnCtx=false`),后台票据链路在宿主侧的启用条件待 0.5+ 跟进(或确认需要 preset/宿主组合加载 `dsh-tool-jobs`)。 --- ## 6. 里程碑与验收 | 里程碑 | 内容 | 验收标准 | | --- | --- | --- | | **M0 稳·准基线** | §2 全部(别名收敛、大纲对账、警告进回执、成本预期)+ §2.5 真机复验闭环 | `pnpm smoke` 全绿 + 新增断言;真机全类型出片复核通过;O10 测量数字落档;渲染回执可见 warnings | | **M1 渲染产能** | §3 全部(动态等待、实例复用、workDir 分片、场景级增量渲染) | 单片固定开销下降(前后对照数字落档);增量渲染验收:改一幕后重渲 ≈ 单幕 + concat,与全量产片抽帧等价;`cache:false` 全量保底可用 | | **M2 效果扩面** | §4.1~4.7(4.1/4.2/4.6 优先,可与 M1 并行) | audio 成片有声、font 生效、字幕条出现、exit 退场与 zoomIn 可用、新缓动三族可用、code morph 出片;examples 演示幕更新 | | **M3 面板交互与收口** | §5 + README 已知限制刷新 + 离线打包验证 | 拆包结论落档;按钮驱动真实 patch(若通道可用);preview 后台化;文档与实现一致;tgz 安装可用 | 版本内顺序:M0 → M1 → M2 → M3;M2 的 4.1/4.2/4.6 可提前与 M1 并行。peer 依赖维持 `>=0.1.5-rc.2`。 ## 7. 风险 | 风险 | 等级 | 应对 | | --- | --- | --- | | 增量渲染的段拼接正确性(`-c copy` 时基、尾帧补偿与分段的交互、重编号错位) | 高 | 段级验收 + 抽帧比对;拼接校验失败自动回退全量;`cache:false` 保底开关;指纹含渲染参数防串档 | | 常驻浏览器/vite 实例的稳定性(内存泄漏、僵尸进程、编辑器状态残留) | 中 | 复用前健康检查,失败即冷启动重建;空闲回收;串行闸下无并发状态;headless 参数不动 | | 动态等待在多环境的鲁棒性(按钮选择器变化、加载卡住) | 低 | 兜底超时回落固定等待;按钮定位沿用「按文本找」既有策略 | | audio mux 的音画同步与编码器兼容(libopenh264 退路 + 有音轨的组合) | 中 | 真机首验;`-shortest` + 显式时长钳制;音轨清单与帧时间轴同一口径换算 | | font `@font-face` 在 headless 的加载时机(未就绪渲出兜底字形) | 中 | 点 Render 前 `document.fonts.ready` 等待;`anim_diagnose` 报告字体资产状态;真机验证 | | 字幕条遮挡场景内容 / 超长 cue 溢出 | 低 | 固定底部安全区;超长截断 + 软警告;样式走主题 token | | MC 缓动/转场映射与 IR 语义的偏差(如 zoomIn 的观感) | 低 | 冒烟断言映射表;真机出片人工验收 | | code morph 的 diff 质量(中文注释、高亮共存时)不可控 | 中 | 真机验收形态;不理想则工具描述收窄建议场景(英文代码 / 相近结构) | | dsh 0.1.x 破坏性变更(持续风险) | 持续 | 锁 verified 版本;IR / 工具层与宿主解耦的既有策略不变 | ## 8. 明确不做(推到 0.5+) TTS 语音合成与音画自动对齐(本版本只做「文本旁白 → 字幕条」与手动音轨)、**多会话并行渲染**(串行闸维持——SwiftShader 吃 CPU,并行收益不稳,增量渲染已吃掉大部分重复渲染量)、loopback RPC 直改 spec(按钮走 followup,直改通道继续推迟)、Remotion / Manim 渲染 Provider、图表 / 思维导图等复合图层(preset 配方教学覆盖)、逐字打字机与 video 图层(4.8 若真机验证不过则顺延)、画布拖拽式编辑、3D / 粒子效果、场景相机(维持放弃)、store 快照优化(维持观察)。 --- ## 9. 实施进度 分支:`feat/0.4.0-throughput`。 | 项 | 状态 | 说明 | | --- | --- | --- | | 0.4.0 规划 | ✅ 完成 | 本文件:遗留盘点(含本轮体检 N1~N7)+ 三大主体 + 里程碑 | | M0 稳·准基线(§2.1~2.4 代码) | ✅ 代码完成 | **§2.1 别名收敛**:spec 包新增 `aliases.ts`(`PROP_ALIASES`:strokeWidth→lineWidth、color→fill 唯一权威源);codegen 归一移入 `normalizeLayerProps` 最前(先于颜色/尺寸兜底,否则无色兜底会把别名遮成主题色——冒烟抓出后修正)、轨道目标保留表驱动改写;`coerceScene` props 键 + 轨道目标两侧表驱动归一并补 color,repairs 回报「别名→规范名」。**§2.2 大纲对账**:store 增 `OutlineItem` / `SpecRecord.outline` / `setOutline`,foldEvents 消费 `anim/outline-updated`(乱序回放跳过);ops 新增 `reconcileOutline` 五类提示(超纲/离纲/单幕偏差>50%/全片偏差>30%/未写完),opPlan 落 store、opDraftScene 回执 `outlineNotes`、opRender 票据与同步回执带对账。**§2.3 警告进回执**:contract 两包成对加 `PreviewResult/RenderResult.warnings` 与 `RenderResult.expectedFrames`;adapter `dedupeWarnings` 同类合并(真机百行刷屏收敛,宿主 console 保留全量);`anim/render-finished` 事件与 `/api/state` 任务簿、BackgroundTicket 完成卡都带 warnings;client 卡片两类拆分(「已忽略/不可动画/未登记」黄色,「兜底/换算」中性「已自动处理」),SceneCard/RenderCard 增「大纲对账」中性块。**§2.4 成本预期**:opPlan 全片 >60s「分钟计」、>180s「30 分钟超时预算 + scenes 抽查」档位;工具描述同步(anim_render 预期口径、anim_asset_import 标注 font/audio 暂不生效——N1 落地前止损);基线 42 → **53 项冒烟全绿**(新增 11:别名一致性/color 别名静态回归/coerceScene 表驱动/outline 状态三路径/reconcileOutline 五形态/draft 对账接线/dedupeWarnings/preview 警告回执/sync 警告+expectedFrames/票据 outlineNotes/plan 档位),typecheck 五包全绿,build + 宿主挂载冒烟通过;examples 生成物 byte 级不变 | | M0 真机复验(§2.5) | ✅ 完成(2026-09-16,dsh 0.1.6-alpha.1 真机 + Edge + ffmpeg) | **全类型出片**:13 种图层类型各就位渲成 12.5s / 375 帧成片(code 高亮 / math 公式 / asset 图 / group 入场在预览帧逐项可见);**O20/O21 回归**:故意夹带 5 类手误(ease 字符串、type 大写、漏 name、漏 tracks、strokeWidth 别名)全部被 repairs 捕获,渲染回执零 strokeWidth 泄漏;**O10 测量**(runtime 已埋点,落 `work/editor-timing.json`):goto 2022ms / canvas 11ms / **Render 按钮可点 14ms** / 固定 sleep 纯余量 3986ms——§3.1 动态等待可把单片固定开销从 ~6s 压到 ~2s(goto 主导);**离线打包**:offline-packager 出 tgz 24.78 MiB 成功;**会话恢复**:宿主重启后 sidecar 完整恢复 4 幕 + 撤销历史 + 大纲,恢复态上 anim_undo 正常执行(期间模型把「undo 后读已删场景报错」误报为「恢复丢幕」,经 version/history/时长三方数据核实为误报,插件行为正确);CJS 告警从每渲刷屏降为**每进程一次**(来源是 MC vite 插件依赖链的服务首启加载,非本插件配置,观察项);旧构建遗留的 `work/vite.config.ts` 死文件已清理 | | M0 真机打磨修复 | ✅ 完成 | ① 草稿期「全片偏差」outlineNote 噪音——幕数未写齐时必然偏差大,改为写齐后才提示(真机逐幕 draft 时每幕都响,冒烟加断言);② `store.undo()` 先 pop 后 apply 的「撤销失败丢历史记录」隐患(排查恢复问题时的顺带发现),改为先应用成功再出栈,冒烟加漂移形态断言 | | M1 渲染产能 | ✅ 完成(2026-09-16,代码 + 真机验收) | **§3.1 动态等待**:按钮就绪即走 + 300ms 稳定拍,30 秒兜底到顶直接报可读错误;真机 editor-timing 实测编辑器加载总量 6.2s → **3.2s(冷)/ 1.36s(暖)**。**§3.2 实例常驻**:浏览器进程级单例 + vite dev server 按 workDir 一份,复用前健康检查(浏览器 connected + HTTP 真实应答)、空闲 10 分钟整套回收(unref 不拖进程退出)、beforeExit/exit 双钩兜底僵尸浏览器、复用实例「起手阶段」失败自动整套丢弃冷启动重试一次(等帧阶段失败不重试);插件卸载经 `renderer.dispose()` 优雅释放;真机 goto 2879ms(冷)→ **989ms(暖)**,全命中渲染 15150ms → **104~126ms**(编辑器零启动)。**§3.3 workDir 分片**:`work/` 各自持有 .vite 预打包缓存 / segments 段缓存 / output;dispose 接线进插件 effect。**§3.4 场景级增量**(真机验收中发现并修正了原方案的缺陷):~~切片一次渲染按声明时长切段~~ → MC 编辑器内每幕实际占帧带 reset 帧/补全帧(tween 收尾多一帧端点),预测边界恒有 ±1 漂移,段尾会裹进下一幕的帧(实测改色后上一幕段尾泄漏旧色一帧)——改为**缺失幕逐幕 solo 渲染**(实测 solo 帧与整片中该幕逐帧 MAD≈0,内容正确由构造保证、不依赖 MC 内部边界规则),段文件名带 `r2` 代次隔离旧缓存;指纹=场景 JSON 稳定序列化+渲染参数联合 sha256 前 8 位;concat demuxer `-c copy` 零重编码 + ffprobe 时长校验(ffprobe 缺装时跳过校验不阻断);失败自动回退全量 + `cache:false` 保底;`anim_render` 增 cache 参数,回执/事件/任务簿带 `incremental {scenesTotal, scenesReused, fallback?}`,卡片「渲染提速」中性块展示;anim_diagnose 增段缓存占用报告。**真机对照数字**:8s/240 帧/3 幕样片——全量 24.6s(冷)/ 15.2s(暖),未变重渲 **0.1s**,改一幕增量 **8.2s**(≈单幕渲染+拼接);增量片 vs cache:false 全量真值逐帧比对 avg PSNR 56.4dB、除边界槽位(±1 帧的静止内容,MC 内联分槽固有)外 MAD≤0.12,无内容错位、无陈旧帧;60 项冒烟全绿(新增 6)。验收工具:`scripts/m1-verify.ts` 直驱回归脚本保留 | | M2 效果扩面 | ✅ 完成(2026-09-16,代码 + 真机验收) | **§4.1 audio 图层**:`LAYER_TYPES` 增 `audio`(props:`src:"asset:"` / `volume` 0~1 / `loop` / `stop:"sceneEnd"|"specEnd"` / `atMs` 场景内偏移);audio 不进 MC 帧合成(emitNode 前跳过、轨道不撑长时间线),codegen `collectAudioTracks` 按场景起点累计换算成全片绝对时间的音轨清单,adapter 在编码/拼接之后 `muxAudioTracks` 二次混音(`adelay` 对齐 + `volume` + `atrim` 钳制 + `-stream_loop` 循环 + 多轨 `amix normalize=0` 求和;`-c:v copy` 零重编码,`-t` 显式钳到视频时长,临时名+rename 原子替换);混音失败只降级警告(保留无声成片);契约/事件/任务簿/回执贯通 `audioTracks`;**audio 图层整体剔除出场景指纹**(改音量只重混音,零重渲)。**§4.2 font 资产消费**:`generateFontsCss` 给每个 font 资产生成 `@font-face`(family=assetId 原文,文件 URL=safeName 净化串)并在 project.tsx 注入 import;runtime 渲染前 `FontFace.load()` 显式触发 + `document.fonts.ready`(5 秒封顶,fontsMs 落 editor-timing.json);真机 SimHei 字形确认生效。**§4.3 旁白字幕条**:`narration.cues`(atMs=全片绝对毫秒,durationMs 缺省 4 字/秒下限 1200ms)经 **`expandNarration` 在渲染入口展开成各幕 `scene.subtitles`(场景内本地毫秒)**——真机发现现场换算方案在场景级增量下错位(切片后全局时间语义被破坏,第二幕字幕整条丢失),展开后字幕成为场景数据,切片/指纹/抽查天然正确;字幕条 muted 半透明底条+主题文字色 150ms 渐变、超 40 字截断+警告。**§4.4 转场扩族+退场**:transition 增 `slideRight/slideDown/zoomIn`,`Scene.exit` 幕尾退场(fade/slide 四向、滑出半幅+240px 余量、lastEnd 纳入 exit);**真机发现并修复 0.2 起的潜伏缺陷:`view.x/y` 是位置分量(画布中心在 size/2),旧 slide 写法把整个 view 连同背景矩形贴到画布边缘**(slide 入场后半屏露黑、内容偏移半幅),现全部改为中心相对坐标;project.meta 背景兜底主题底色(zoomIn/slide 露出的条带不再发黑)。**§4.5 缓动扩族**:`bounce/elastic/back` → MC `easeOutBounce/easeOutElastic/easeOutBack`(冒烟静态断言 MC .d.ts 实际导出防升级漂移)。**§4.6 code morph 转正**:工具描述加「代码演化」写法,真机 mid-morph 帧确认逐词 diff 形态;code 缺 fill 兜底主题色(真机抓到「黑字黑底不可见」)。**指纹补丁**:新增 `CODEGEN_VERSION` 进场景指纹——codegen 输出语义变更(不改输入只改输出的修正)使旧段自然失效,否则段缓存会吃到旧渲染。**§4.7 视频技巧包**:preset persona 增技巧段(冒烟锚点校验),examples 增 0.4.0 演示幕(vite build 过);`anim_asset_import`「暂不生效」标注已撤。**真机数字**(`scripts/m2-verify.ts`,8s/240 帧/3 幕样片):A 全量建缓存+混音 25.8s → B 全命中重混 238ms → **C 只改音量 233ms(段缓存全命中,零重渲)**,响度 -16.4→-11.3dB 随 volume 变化;成片双流 h264+aac、时长 8.0s 精确;**隐患修**:`@lezer/*`(code-highlight 生成物的依赖)补进 render-mc 依赖声明(此前靠根 hoisting 侥幸可解析,源码直跑/离线安装即断)。冒烟 60 → **74 项全绿**(新增 14,含真 ffmpeg 的 muxAudioTracks 与 audio 渲染端到端),typecheck 五包、build 全绿 | | M3 面板交互与收口 | ✅ 完成(2026-09-17,代码 + 真机验收) | **§5.1 拆包**:结论与真机探测记录落档本文件 §5.1(传输信封 / cookie 鉴权 / session_prompt 契约 / 线名陷阱),探测指令端到端落进会话日志并被 agent 回复,§5.2 放行。**§5.4 cards 拆分**:629 行 cards.tsx → cards/ 目录(styles.ts + primitives.tsx + 一卡一文件,cards.tsx 转纯转发桶,index.ts 注册表不动);纯移动零行为变更(ProgressBar 为内联 JSX 同构提取),smoke-host 的 lazy-CJS 契约 + 10 卡片插槽断言护航。**§5.3 preview 后台化(O19)**:opPreview 与 opRender 同一降级链(owner→无主→同步),新事件 `anim/preview-start/finished`(帧清单+warnings 随事件落盘),RenderTracker 预览与渲染同簿(kind=preview + frames,/api/state 直查),PreviewCard 拆 FrameGrid + PreviewTicket(落定停表 + 不可达退避与渲染票据同一纪律)。**§5.2 面板最简交互**:register() 收口 `stampSession` 给全部工具回执盖 sessionId;client `promptAgent` 同源 POST /api/session/prompt(信封与拆包结论逐字段一致,queue 模式,requestId=rpcId 可对账);PanelActions 按钮——SceneCard「预览这一幕」/ PlanCard「渲染成片」/ PatchCard「撤销这步」,老回执无 sessionId 自然降级只读;指令正文 = 稳定前缀「[anim-studio 面板指令]」+ JSON(preview_scene / render_spec / undo_latest),agent 预设声明处理契约(直接执行不反问、一句话回报、specId 不存在只提示)。**真机端到端**(dsh web 127.0.0.1:3080 + anim-studio 预设会话):面板指令原文经 session_prompt 投递 → agent 解析 → `anim_preview {"atMs":[1000],"specId":"panel-e2e"}`(自行算出幕中点)→ 一句话回报缩略图路径;`/api/state` 出现 kind=preview 完成条目(含帧清单);anim_diagnose 回执带 sessionId(盖章真机生效)。**真机发现(§5.5 破坏性变更对比)**:① dsh-persona 配置 `text` → `prefix`(0.1.6-alpha.1 schema 必填 prefix,预设此前从未真机安装过、一装即炸,已修);② **`jobsOnCtx=false`:本部署不向插件上下文暴露 jobs 服务**(diagnose host 报告实证)——preview/render 在真机走同步回退(设计内行为),后台票据要等宿主把 dsh-tool-jobs 暴露给插件上下文,记 0.5+ 跟进;同步路径事件化后任务簿照收(预览完成条目可见)。**收口**:版本 0.4.0;`npm pack` tgz 211KB 验证(exports/lib/client/patch yml 齐全、bundle 自包含、含 session/prompt 通道代码);冒烟 74 → **78 项全绿**(opPreview 后台票据事件序 / job_kill→killed / 同步回退+任务簿观察 / promptAgent 信封三态映射),smoke-host 增回执盖章断言,typecheck 五包全绿 | | 收尾打磨:字幕折行 + 安全区 + README 重构 | ✅ 完成(2026-09-17,代码 + 真机验收) | 用户真机反馈三项:① **字幕太大出界**——字幕字号原取 `min(40, max(18, theme.font.size))`,默认主题 48 → 字幕 40px(720p 画布一行 32 字就顶满全宽);改为**按画布高度 4% 自适应**(14~56 夹取,720p→29px),与正文字号解耦。② **超宽自动折行**——原实现超 40 字直接截断(注释明言「字幕不支持自动分行」);新增 `wrapSubtitleText`(估宽与底条同一套:CJK 逐字可断、拉丁按词断、无空白长串字符硬切),codegen 折成 `\n` 多行,底条按最宽行计算、随行数增高、底边锚定画布底边上方 36px。**真机抓到两个 MC 语义坑**:MC 的 Txt 走 DOM 布局,`\n` 默认被折叠——必须 `textWrap:'pre'` 才按换行分行;MC 的 `lineHeight` 数字语义是 **px**(`lineHeight={1.4}` 即 1.4px,两行会叠字),倍数要传字符串 `'140'`。`expandNarration` 的 40 字截断改为超 80 字软警告。③ **字幕安全区**——genSceneFile 在有字幕的幕检查正文图层锚点(props.y / props.y 轨道关键帧 / line·arrow 的 points)是否落进字幕带,落带内给软警告点名图层并给出安全线上值(y≈0 的居中/全屏元素不误报);persona 与 `anim_create_spec` 描述同步「字幕带保留区、正文避开、一条 cue ≤40 字」守则。`CODEGEN_VERSION` 2→3(旧段缓存自然失效)。**真机验收**(examples 渲染链路,与 anim_render 同路径):60 字 cue 折成 36+24 两行、底条 1092×114 y=267、短字幕单行小底条,逐帧 PNG 确认;安全区警告实测点名 y=320 的越界矩形。**README 瘦身重构**:按「是什么 / 特性 / 怎么用」重组,删净版本叙事与事故长文(归档职责交给 docs/ 各规划档与设计草案),219 行 → 180 行;examples 入库生成物随新 codegen 重新生成(.meta 文件系 MC 渲染副产品,generate 本就不产出,连同删除)。冒烟 78 → **80 项全绿**(wrapSubtitleText 单元三形态 / 折行几何与 lineHeight·textWrap 生成物断言 / 安全区警告三形态与无字幕不误报),typecheck 五包全绿 |