首页
/ HyperFrames changelog-video 技能:把每周 Changelog Markdown 变成 45–60 秒品牌化口播视频

HyperFrames changelog-video 技能:把每周 Changelog Markdown 变成 45–60 秒品牌化口播视频

2026-09-05 14:05:34作者:羿妍玫Ivan

本文基于 HyperFrames 仓库中 .agents/skills/changelog-video/SKILL.md 完整展开。这个技能定义了一条从「changelog .md(主题 + 条目)」到「lint-clean、seam-gate 全绿的 HyperFrames 项目」的固定流水线:Annie 配音、动画品牌背景、mock-UI 可视化演示、底部字幕轨。读完本文,你可以完整掌握这条流水线的 6 个步骤、每一步的硬门槛(hard gate)与反模式清单,并能理解字幕对齐脚本 align-captions.mjs 的底层实现原理。

技能定位:输入、输出与前置依赖

技能的输入是一份 changelog .md(主题 + 条目,类似 HyperFrames 每周 digest);输出是一个位于 projects/active/weekly-changelog-<range>/ 的 HyperFrames 项目,且要求 lint 通过、seam-gate 全绿;渲染只在用户明确要求时才执行

SKILL.md 开头声明了一条不可协商的前置加载规则:必须先加载 motion-doctrine(含 cut-the-curve、有游标时加载 oversized-cursor、以及 seam-craft)和 captions-overlay。分工是明确的——本技能负责 changelog 专属流水线,doctrine 负责动效定律(motion law)。

核心准则只有一句话:visualize, don't list(用可视化代替列表)。每个主题都必须用「真实 UI 的动画 mock 或忠实类比」把这次变更“演”出来,绝不允许纯文字要点。所有主题在写脚本之前必须先经过可视化路由表 visualization-registry.md 的裁决:ui-recreate / ui-analog / terminal / checklist 四选一,文字 checklist 是最后手段,仅保留给真正无法可视化的条目(如可靠性修复清单)。

Step 0 · 从本技能资产启动项目——最常见的“跑偏”根源

SKILL.md 用整段警告强调:Step 0 必须在写任何 composition HTML 之前完成,跳过它必然产出「像上个项目、而不是这个品牌」的视频。原话很直接:“The skill's assets, fonts, and scaffold are the skill; the SKILL.md prompt is a router.”(技能的资产、字体和脚手架本身就是这个技能,SKILL.md 只是路由器。)

启动命令(原文完整继承):

mkdir -p project/assets/fonts
cp <SKILL_DIR>/assets/fonts/*.woff2 project/assets/fonts/
cp <SKILL_DIR>/assets/bgm.mp3 project/bgm.mp3
ffmpeg -y -stream_loop 15 -i <SKILL_DIR>/assets/bg-pattern.mp4 -t <TOTAL> \
  -vf "scale=1080:1080,fps=30,eq=saturation=0.72,drawbox=c=black@0.5:t=fill" \
  -an -c:v libx264 -crf 20 -pix_fmt yuv420p project/assets/bg-pattern-<TOTAL>s.mp4
cp <SKILL_DIR>/examples/master-skeleton.html project/index.html

对照技能目录,这些占位符全部有实体:assets/fonts/ 下确实打包了 5 个 woff2(TT Norms Pro 的 Normal/Medium/Bold 三级 + Mono + ABC Solar Display Bold),即 assets/fonts/ 下的 TT_Norms_Pro_Bold.woff2ABC Solar Display-Bold.woff2 等;house BGM 是 assets/bgm.mp3;循环背景源是 assets/bg-pattern.mp4

注意 ffmpeg 编码链里的品牌化处理:eq=saturation=0.72 把背景饱和度压到 0.72,drawbox=c=black@0.5:t=fill 再铺一层 50% 黑罩——两者叠加就是「animated brand background」的具体形态:有动效、但足够安静,不抢前景视觉。

启动之后必须通读(不是略读)references/build-spec.md。该文件定义品牌令牌:TT Norms Pro + ABC Solar Display + TT Norms Mono 三套字体、奶油底 #f5f6f4、限量绿 #5ef17c、绿色描边的玻璃卡片、kicker/sec-chip 胶囊形状、位于 top: 990 的 32px 字幕轨——所有场景都从脚手架继承这些令牌。

Step 0 还给出了一段自我检查:如果你发现自己想 cp 某个旧视频的 index.html、想自己手写 @font-face、或想设计 WebGL shader 背景而不用上面编码好的 bg-pattern MP4——停手,删掉当前 index.html 回到 master-skeleton 的拷贝步骤重来。理由:在正确的脚手架上重建场景内容,比把品牌往错误的脚手架上 retrofit 更便宜。脚手架 examples/master-skeleton.html 就是这套品牌的外壳(chrome、字体、色板、布局骨架),后续步骤 1–4 只是“规划往脚手架里放什么”,步骤 5 才填充占位符(<RANGE><TOTAL><CUT_N><DUR_N> 与场景主体),绝不重写脚手架的外壳

Step 1 · 解析 + 编辑性剪裁:预算 45–60 秒

从 changelog .md 中提取:周区间、头条数据(发布数、commits 数)、主题、条目。时间预算是硬性的:

  • 总长 45–60s
  • 片头标题 ≤2s,片尾 outro ≤3.5s,4 个主题各约 9–12s;
  • 每个主题保留 1 个 hero 可视化 + 最多 3 条口播条目,其余内容只以 outro 的 “full digest” 指针形式存在;
  • 「剪裁就是工作本身」:一份 30 条目的 changelog,最终口播 beat 数仍然 ≤14;
  • 主题按故事顺序排列:marquee 功能 → 产品界面 → 性能 → 可靠性(digest 通常本来就是这个读序)。

Step 2 · 可视化路由:路由表决定演什么

路由表 references/visualization-registry.md 按强度从高到低定义四个类别:

  1. ui-recreate——变更发生在一个可以忠实 mock 的界面上;
  2. ui-analog——没有精确界面,但存在一个诚实的 UI 隐喻(面板、仪表、管线),其行为本身就是这次变更;
  3. terminal——变更是一条 CLI 命令/参数:打出来,展示结果;
  4. checklist——不可视化内容(修复清单、依赖升级),最后手段。

有一条红线:绝不发明暗示了不存在的屏幕的 UI——类比必须描绘行为(速度、批处理、缓存),而不是伪造一个产品页面。

路由表中沉淀了 7 个已验证的 ui-recreate 表面(含 Studio 编辑器/时间线、Inspector 面板、画布 + 元素、变体渲染、故事板视图、终端、渲染面板),每个表面都有「mock 解剖」与「已验证编排」两列,例如渲染面板的“绿色进度条 + tabular-nums 帧计数器以 power2.in 跑(慢→快读起来像‘更快了’)”、终端的「字符逐字打出(stagger .02),结果行在 0.3–0.5s 后落定」。另有 8 个已验证类比:LUT/调色用滑杆行 + 同帧重滤镜、缓存用两行相同请求(第二行短路到 ✓ + cache 芯片)、并发上限用芯片队列过闸门、错误上浮用角落 toast 卡片等。

每个主题在路由之后要写一行:theme → surface → mock 执行的 2–4 个有序动作,每个动作绑定一句脚本。如果路由表没有合适表面、也不存在忠实类比,那就是 checklist 场景——不为无法诚实表达的东西编造假 UI。checklist 场景的规格:玻璃卡片、≤6 行 mono 行、绿色 ✓ 在每条的 VO 词上落下(back.out(1.5)、0.3s)+ 行亮度脉冲;超过 6 条的条目直接剪掉,它们活在 digest 链接里。

路由表还带自我生长约定:每当新的 UI 区域上线,第一次 mock 它时就把(解剖 + 编排)加一行进这张表,让下一期 changelog 直接复用而不是重新推导。

Step 3 · 两层脚本:口播层与显示层分离

脚本按 references/script-voice.mdtoken 行书写,同时是 VO 和字幕的唯一事实来源:VO 读 spoken 层,字幕渲染 display 层。这是一条硬质量门——字幕打出 "jay-sawn" 或 VO 念出 "juh-son"(把 JSON 逐字母念)都算构建失败。

语气要求:会话式而非 release-notes("The big one this week —" 优于 "Theme 1:")、允许缩写、可用第二人称、信息型而非推销型、每个 beat 一口气(句子 ≤ 约 14 词)、有信息量的数字保留("fifteen releases")但 commit hash、PR 号和版本细节绝不被念出、以“周 + marquee”开场、以 digest 指针收尾。还有一条特色规则:教会那条简单命令——如果某功能有一行式调用(斜杠命令、CLI 一行命令),脚本要原样念出来,mock 要展示它被敲进去——命令是结果的可见“因”。斜杠命令念作 "slash ",字幕作 /name

script-tokens.json 的 token 行格式(仓库自带 examples/script-tokens.json 示例):

{
  "lines": [
    {
      "id": "l1",
      "tokens": ["This", "week", "at", "HyperFrames,", { "display": "JSON", "spoken": "jay-sawn" }]
    }
  ]
}
  • 裸字符串 = display 与 spoken 相同;
  • 对象 = 两层分叉:display 保留标准拼写和字幕该显示的标点,spoken 是 TTS 读的形式;
  • 一行 = 一个字幕短语(display 文本 ≤ 约 40 字符),分行是写作期决定的;
  • vo-spoken.txt 由所有 token 的 spoken 形式以空格连接生成。

发音控制完全靠拼写、连字符和空格(HeyGen TTS 只吃纯文本、无 SSML),script-voice.md 给出 7 条规则:缩写字母逐个念(CLI → "C L I")、按词读的缩写重拼(JSON → "jay-sawn"GSAP → "jee-sap")、混合可发音复合词用连字符小写(ffmpeg → "ff-mpeg",绝不用空格大写,否则 TTS 会逐字母硬停)、版本号展开(v0.7.36 → "version zero point seven point thirty-six")、URL 用 "dot"(hyperframes.heygen.com → "hyperframes dot hey-jen dot com")、扩展名(.mp4 → "dot em pee four")、强调只靠逗号与 em-dash(绝不用大写,省略号在 TTS 中不可靠)。

共享词库是 references/lexicon.jsondisplay → spoken),当前包含约 60 个条目,例如 "JSON": "jay-sawn""ffmpeg": "ff-mpeg""HeyGen": "hey-jen""Linux": "linnucks""vite": "veet""MusicGen": "music-jen"。规则:每个技术词都查词库;缺失就停下问用户怎么念,然后加词条——绝不猜、绝不念没听过的。新词条要在生成的 VO 里听完那一行才被接受。

Step 4 · VO:Annie(HeyGen,锁定音色)

SKILL.md 给出的命令(原文完整继承):

# spoken-layer text only; words JSON = ground-truth timestamps of the SPOKEN text
# Repo-native path: the changelog-video skill runs from the hyperframes repo root,
# so it uses the tracked hyperframes-media TTS helper directly (no `npx hyperframes
# skills` install step). If you've copied the skill into another repo, swap in
# your own path to the media-use / hyperframes-media heygen-tts.mjs.
node skills/hyperframes-media/scripts/heygen-tts.mjs ./vo-spoken.txt \
  -o voiceover.mp3 --words vo-words.json \
  --voice 330290724a1b470fb63153f34d4c0183   # Annie — lifelike (do not substitute)

前置要求:heygen CLI ≥0.3.0 且已完成认证(heygen auth login --oauth)。从当前仓库结构看,技能引用的 heygen-tts.mjs 对应的 TTS 助手实现追踪在 skills/media-use/audio/scripts/heygen-tts.mjs(media-use 技能内含配套 tts 参考文档);技能注释也明确:如果把这个技能复制到别的仓库,换成你自己指向 media-use / hyperframes-media 的 heygen-tts.mjs 的路径即可。

词级时间戳是硬门槛。 进入 Step 5 之前必须验证 vo-words.json 非空且带 words: [...] 数组、每词有 start/end。如果它为空(0 字节)或缺数组——这是 TTS 服务商「返回音频但不返回时间戳 payload」的已知失败模式——不得继续。回退方案是用本地 whisper 对已生成音频做强制对齐:

uvx --from openai-whisper whisper voiceover.mp3 \
  --model base.en --language en --word_timestamps True \
  --output_format json --output_dir .
# then run align-captions.mjs with --words voiceover.json (same shape)

whisper 会误听 TTS 的发音("gee-sap" → "gsap"、"heyjen" → "hey Jen")——但字幕仍使用 script-tokens.json 里的 DISPLAY 拼写,whisper 只贡献时间戳。SKILL.md 把这个回退称作“有字幕构建”与“静默无字幕构建”之间的差别。

对齐由技能自带的 scripts/align-captions.mjs 完成:

node <SKILL_DIR>/scripts/align-captions.mjs \
  --tokens script-tokens.json --words vo-words.json --out captions.json

captions.json 是字幕轨的输入(display 拼写 + spoken 时间)。对齐器打印 MISMATCH 警告,构建前必须解决全部(通常是一个词库拼写被 TTS 渲染成了多个词)。音频就是时钟:所有 beat 时间都来自 vo-words.json,重新生成 VO 会重开每一个 seam。

从源码结构看,align-captions.mjs 的实现与文档描述完全一致:

  • 多词消耗consume() 从游标处贪心拼接流中词,直到其规范化拼接串与 token 的 spoken 形式匹配——一个 display token 可以覆盖多个 spoken 词("C L I" 是 3 个词),display 词的时间取其第一个 spoken 词的 start
  • 模糊匹配close() 用前缀匹配 + Levenshtein 距离(阈值 max(1, floor(min(len)/3)))吸收 TTS 时间戳的怪癖,如 "hey-jen" 可能作为一个词或多个词到达;
  • 失步再同步:游标未命中时,会在游标之后最多 4 个词范围内重试(for (let off = 0; off <= 4 ...)),再失败才打印 MISMATCH
  • 行尾时间:一行的 end = 下一行首词 start;最后一行 = 最后 spoken 词 end + tail(默认 0.6s,可通过 --tail 调整);
  • 退出码即门槛:存在 mismatch 时进程 exit(1) 并打印 N MISMATCH(ES) — resolve before building,意味着它可以直接作为 CI/流水线门使用。

Step 5 · 构建:品牌令牌 + 字幕轨不可省略

严格按 references/build-spec.md 执行:品牌令牌 + 字体(打包在 <SKILL_DIR>/assets/)、动画背景编码、场景脚手架、chrome、字幕轨、每场景一个限量绿瞬间#5ef17c)。然后是 doctrine 的顺序:ledger.json(所有普通 seam 按 cut-the-curve 切在 LEFT)→ seam-stamp → 内部 beat 落在 VO 词上 → seam-gate verify。

字幕不可选,SKILL.md 用了一整段加粗说明:master-skeleton 自带一个字幕轨 IIFE,读 LINES 数组——把这个数组留空是出厂 bug 而不是风格选择。必须在进入 Step 6 之前把 captions.json 的内容填进去(替换 IIFE 中的 const LINES = /* … */ []):

// paste in place of "const LINES = /* … */ []" in the caption-rail IIFE:
const LINES = /* contents of captions.json */ [
  { id: 0, end: 2.74, w: [["This", 0.0], ["week,", 0.30], …] },
  …
];

如果 align-captions.mjs 被跳过或 LINES[],Step 6 的帧检查会失败——不要通过从脚手架删掉 #cap-line 来掩盖它。

字幕轨的渲染规格(见 script-voice.md 的 Caption rail 一节):安静的 OVERLAY 而非预留条带;单行、底部居中(1080 方画幅上 top: 990px、高 52px)、TT Norms Pro 500 32px、墨色 .94、柔和深色 text-shadow、词在时间戳上淡入(0.12s)、短语整组切换;底部中心约 100px 内不放关键小字,其他内容可以从字幕轨下方穿过。

Step 6 · 门槛:全部变绿才能交付

SKILL.md 列出 5 道门槛(原文完整继承):

  1. lint 检查bun run --cwd packages/cli hyperframes check --caption-zone "x0=0;y0=.90;x1=1;y1=1;severity=error;seek=.02,.06,.10,.14,.18,.22,.26,.30,.34,.38,.42,.46,.50,.54,.58,.62,.66,.70,.74,.78,.82,.86,.90,.94,.98"(或仓库本地 skills/hyperframes-cli/ 技能里已安装的 hyperframes CLI)——0 error(对比度:变暗文本 ≥ .66 alpha;场景内容保持在字幕轨上方)。不要伸手去用 npx hyperframes@latest;追踪中的仓库本地 CLI 才是这个技能所产 composition 契约的事实来源。--caption-zone 声明的就是 top: 990 那一条字幕保护区(y0=.90 到 y1=1 的底部横带),以 25 个 seek 点采样检查。
  2. seam-gate.mjs verify——0 fail。
  3. 重启预览服务器(它缓存 bundle),在原始 comp 页上通过 __player.seek 抽查 3–4 个 beat。
  4. 未经用户要求不要渲染。用户要求渲染之后,从 MP4 验证帧(ffmpeg -ss <t> … -frames:v 1):字幕在、背景视频不黑、没有极小/冻结帧。
  5. 字幕在场门槛——硬失败。 在 VO 口播窗口内均匀采样 3–4 帧(例如 48s VO 取 t=3t=15t=30t=42),确认 top: 990 的字幕轨每帧都渲染出可见文字。任何口播区间内的帧缺字幕,构建即“无字幕出厂”——按红灯门槛处理,回到 Step 5 复查 LINES 填充。SKILL.md 特别点出这正是 Jul 13–20 v4 构建实际翻车的事故。

项目布局

SKILL.md 定义的标准产物目录(原文完整继承):

projects/active/weekly-changelog-<range>/
├── index.html            # single-doc master (scenes as slides, stamped seams)
├── ledger.json           # vector ledger (seam-stamp input)
├── script-tokens.json    # two-layer script (source of truth for VO + captions)
├── vo-spoken.txt         # generated: spoken layer, one line
├── voiceover.mp3 + vo-words.json + captions.json
├── bgm.mp3               # copy from <SKILL_DIR>/assets/bgm.mp3 (the house track) unless the user supplies one
└── assets/fonts/ + assets/bg-pattern-<dur>s.mp4

文件间的依赖链很清晰:script-tokens.json 是 VO 与字幕共同的事实来源 → 生成 vo-spoken.txt → TTS 产出 voiceover.mp3 + vo-words.jsonalign-captions.mjs 合成 captions.json → 粘贴进 index.htmlLINES 数组;ledger.json 与 stamped seams 则驱动 seam-stamp / seam-gate 校验。

反模式清单

SKILL.md 末尾的对照表(原文完整继承):

不要做 应该做
用要点幻灯片表现 UI 变更 让 mock 界面把这次变更“演”出来
为不可表达条目伪造 UI 诚实的 checklist 场景
TTS 文本里直接念 "JSON"/"CLI" 词库 spoken 形式;display 保持标准拼写
字幕里出现音译拼写 字幕永远渲染 display 层
猜未知词的发音 先问,再增长词库
念 changelog 的每一条 每主题 ≤3 条;digest 链接承载其余
绿色强调铺满全片 每场景一个绿瞬间(#5ef17c)
从某个旧视频的 index.html 起步 Step 0——永远把本技能的 examples/master-skeleton.html 拷入 project/index.html
手写 @font-face / WebGL shader / 自制 BGM Step 0——原样拷贝本技能的 assets/;技能资产即品牌
S3 替换后不做 CloudFront 失效就交付 替换后对精确路径执行 aws cloudfront create-invalidation(distribution E2BSLVSZ7FG3U0)——否则 CDN 会缓存旧文件
脚手架 LINES 数组留空就出厂 Step 4 必须产出填充的 captions.json;Step 5 必须粘贴进 IIFE;Step 6 门槛 5 必须确认渲染帧上有字幕。空 LINES = 无字幕出厂 = 重做
没有 vo-words.json 就跳过字幕出厂 回退到 whisper 强制对齐;字幕不可选

这套流水线的工程要点小结

通读 SKILL.md 与其引用文件,可以看到 changelog-video 技能把三条“不可协商”的纪律做成了可验证的门槛,而不只是口头要求:

  1. 品牌纪律——Step 0 的脚手架拷贝 + ffmpeg 背景编码把「品牌」固化成资产而非提示词,任何一步想绕开都有明确的 STOP 检查和反模式条目兜底;
  2. 发音纪律——display/spoken 双层 token + lexicon.json 词库 + “缺失必问不猜”规则,让 TTS 的纯文本限制被系统性绕过;
  3. 字幕纪律——词级时间戳硬门槛(含 whisper 强制对齐回退)+ align-captions.mjs 以退出码表达 mismatch + Step 6 门槛 5 的抽帧验证,形成「时间戳缺失 → 对齐失败 → 渲染帧缺字幕」三段式拦截,确保不存在“静默无字幕出厂”的通道。

对想复用这套思路的团队,技能目录本身就是一个可携带的最小样例:脚手架 master-skeleton.html、token 示例 script-tokens.json、路由表 visualization-registry.md 与发音契约 script-voice.md 都可以独立阅读并移植。

登录后查看全文
热门项目推荐
相关项目推荐