HyperFrames Seam Gate 解析:用 ledger.json + 双脚本为多场景视频切点做"生成—验证"闭环
HyperFrames 的 motion-doctrine 技能用一套零依赖的 Node 脚本(seam-stamp.mjs 生成 + seam-gate.mjs 验证)把"切点处进出向量必须一致"这条运动法则从口头规范变成了可执行断言:以项目根目录的 ledger.json 作为向量账本(vector ledger),先由 stamp 脚本从账本生成主时间轴上的接缝(seam)代码,再由 gate 脚本驱动无头 Chrome 逐帧测量、判定 exit 0/1。读完本文,你能完整掌握 ledger.json 的 schema 与每个字段的取值约定、stamp/gate 两条命令的全部参数与默认值,以及 gate 报告中每一行检查(ledger、zero-overlap、z-sign-scan、carrier-* 等)背后的判定阈值与测量原理。
Seam Gate 在整个 motion-doctrine 中的位置
motion-doctrine 是 HyperFrames 动画技能体系中的"总纲":它要求整片只选一个主导运动方向("the current",默认向左),并且每个切点(seam)处,前一场景如何退出决定后一场景如何进入——同轴、同向、速度匹配、两侧都切在运动中途(向量法则,见 SKILL.md 的 Part 1)。
但法则必须落到构建门禁(build gate)上才有约束力,这就是 Seam Gate 的角色。官方给出的制作顺序是:
vector ledger (ledger.json)
→ STAMP 主时间轴接缝 (scripts/seam-stamp.mjs)
→ 每个阶段选一条持续运动路线
→ 构建 comps
→ VERIFY (scripts/seam-gate.mjs)
其中手写代码只用于 Tier-A 的 morph / match-cut;由 stamp 生成的接缝"天然通过 gate"(pass by construction),因为它们是同一份账本的确定性投影。
两个脚本与运行前提
Seam Gate 是一对"生成 + 验证"脚本,位于 .claude/skills/motion-doctrine/scripts/ 目录:
- seam-stamp.mjs(143 行)— 从
ledger.json生成主时间轴的接缝代码块; - seam-gate.mjs(600 行)— 数值验证器,提供
verify与probe两个子命令。
运行前提(源自 seam-gate.md 与 seam-gate.mjs 头部注释):
- 零 npm 依赖:只需 Node ≥ 22(gate 直接使用 Node 22 内置的
WebSocket/fetch,走原始 CDP 协议)+ 一个本地 Chrome; - Chrome 自动发现顺序(见 findChrome):环境变量
CHROME_PATH→~/.cache/puppeteer/chrome-headless-shell→~/.cache/puppeteer/chrome→ macOS 系统 Chrome;找不到则报错提示设置CHROME_PATH; - 页面以 1920×1080 视口加载(
Emulation.setDeviceMetricsOverride),等待 HyperFrames 运行时就绪信号window.__playerReady && window.__renderReady && window.__player(见 openPage),该运行时契约与 packages/core 中注入到页面的全局对象一致。
核心命令三件套(<SKILL_DIR> 指 .claude/skills/motion-doctrine):
# STAMP: 从 ledger 写主接缝块(base sets + 全部 wrapper tweens)
node <SKILL_DIR>/scripts/seam-stamp.mjs --ledger ledger.json --write index.html
# verify: 验证 ledger 中的每一个 seam(exit 0 = 门禁通过)
node <SKILL_DIR>/scripts/seam-gate.mjs verify --ledger ledger.json --project <project-dir>
# 复用已在运行的 preview server(改完 comp 后要重启它——bundle 会过期!)
node <SKILL_DIR>/scripts/seam-gate.mjs verify --ledger ledger.json --url http://localhost:5244
# probe: 发现某切点时刻附近的所有运动元素(用于编写/修正 ledger 行)
node <SKILL_DIR>/scripts/seam-gate.mjs probe --t 44.8 --project <project-dir>
两种服务模式的取舍(源码见 ensureServer):
| 模式 | 行为 | 适用场景 |
|---|---|---|
--project <dir>(推荐) |
随机取 5380–5399 端口,npx --yes hyperframes preview --foreground --no-open --port <port> 起一个全新 preview server,等待 /api/projects 就绪(120s 超时),结束后自动 kill |
避免旧 bundle 假象;且启动时显式删除环境变量 HYPERFRAME_RUNTIME_URL——源码注释指出:该变量取值错误时会让请求"静默失败成 200 HTML",必须保证采样到的是真实构建产物 |
--url <preview-url> |
复用你正在运行的 server | 快速迭代时复用 IDE 预览;comp 有编辑后必须重启 server,否则你验证的是过期构建 |
server 启动后,gate 会请求 /api/projects 解析第一个项目 id,最终导航到 /api/projects/<id>/preview/comp/index.html 这个 comp 预览页。验证完成后,退出码语义为:任一检查 FAIL → exit 1;全部 PASS/WARN → exit 0;参数/环境错误 → exit 2(见 main),因此可以直接串进 CI。--json 输出机器可读的完整结果数组,--fps 默认 30(ledger.json 顶层的 fps 优先)。
STAMP:把账本行编译成主时间轴代码
node <SKILL_DIR>/scripts/seam-stamp.mjs --ledger ledger.json --write index.html
stamp 的输出是替换/插入 index.html 中 // <seams:auto> … // </seams:auto> 标记之间的代码块(带"do not hand-edit"头注释和再生成命令)。若标记不存在,它会定位 window.__timelines["main"] = tl; 注册行并插在其后——定位不到两者之一则直接抛错(见 写入逻辑)。这正是 changelog-video 技能 模板骨架中预留接缝块的原因:该技能的 build spec 明确要求主时间轴必须命名为 tl,因为 stamp 生成的都是 tl.to / tl.set 调用。
账本里每条 seam 的可选 stamp 参数
| 参数 | 含义 | 默认值(源码核对) |
|---|---|---|
exit.dur / entry.dur |
覆盖出/入时长 | X/Y seam:exit 0.34s / entry 0.42s;Z seam:exit 0.21s / entry 0.5s(seam-stamp.mjs#L92-L120) |
entry.travel |
入场起始偏移量(xPercent/yPercent) | 10;想要"软入场"观感用 8 |
blur |
Z seam 的模糊峰值(px) | 18(整帧 zoom);文字缩放场景用 10 |
生成代码的两类形态
stamp 会先做静态账本自检:同一行的 exit.axis/dir 与 entry.axis/dir 不一致时直接抛错 mismatched … fix the PLAN, not the stamp——与 SKILL 中"修计划,别修缓动"的口号对应(seam-stamp.mjs#L85-L90)。
X/Y seam(power3.in 出 + power4.out 入的镜像缓动对):
tl.to("#el-hook", { xPercent: -12, autoAlpha: 0, duration: 0.34, ease: "power3.in" }, cut - 0.34);
tl.set("#el-hook", { autoAlpha: 0 }, cut); // 硬切:切点帧直接归零,杜绝叠化
tl.fromTo("#el-claim", { xPercent: 10, autoAlpha: 0.35 },
{ xPercent: 0, autoAlpha: 1, duration: 0.42, ease: "power4.out", immediateRender: false }, cut);
注意三处设计:出方在 cut - dur 就启动、切点帧仍在运动中("cut mid-motion");tl.set 在切点把出方强制隐藏(保证 gate 的 zero-overlap 检查);入方 fromTo 起点带 0.35 透明度且 immediateRender: false,避免时间轴注册瞬间就把未轮到的场景画出来。
Z seam(scale = "Z 轴",blur 作为景深伴随效果):出方向 dir=-1(pull)收到 scale 0.8、dir=+1(push)推到 scale 1.18,均配 power3.in + blur(18px) + autoAlpha→0(none 缓动);入方 fromTo 从超规格状态起飞——pull 从 scale 1.25("以更大的规模抵达")、push 从 scale 0.78,落点 scale 1.0、expo.out、透明度 0.15→1、blur 归零。这些预置状态同时写进文件开头的 base gsap.set 清单(见 场景清单与 Z 预置):第一个场景可见,其余 autoAlpha: 0,Z 入场元素额外携带起始 scale 与 blur。
match-cut / morph 行只生成两个可见性 set(切点帧出方 autoAlpha: 0、入方 autoAlpha: 1)加一行注释:载体交接(carrier handoff)是 Tier-A 工作,必须手写,但载体行必须保留在 ledger.json 里供 gate 检查位置连续性。
ledger.json:向量账本的数据格式
ledger.json 放在项目根目录,一个 seam 一行,"向量账本即数据"(the vector ledger as data)。完整示例(原文档示例,可直接作为起步模板):
{
"fps": 30,
"seams": [
{
"id": "hook→claim",
"cut": 4.2,
"technique": "cut-the-curve LEFT",
"exit": { "selector": "#el-hook", "axis": "x", "dir": -1 },
"entry": { "selector": "#el-claim", "axis": "x", "dir": -1 }
},
{
"id": "claim→payoff",
"cut": 10.2,
"technique": "inverse zoom-through",
"exit": { "selector": "#el-claim", "axis": "z", "dir": -1 },
"entry": { "selector": "#el-payoff", "axis": "z", "dir": -1, "scanRoot": "#el-payoff" }
},
{
"id": "ui→player (match cut)",
"cut": 60.6,
"type": "match-cut",
"carrier": { "out": "#resting-card", "in": "#product-video" }
}
]
}
字段约定(与源码解析逻辑逐一对应):
cut— 主时间轴(master clock)上的秒数,即"入方点火"的那一帧;type—"cut"(默认,做完整向量检查)、"match-cut"/"morph"(只查载体连续性 + overlap;运动允许恰好在边界开始);axis—"x"、"y"或"z"(z 即 scale)。dir是运动符号:x 轴 −1 = 向左;y 轴 −1 = 向上;z 轴 +1 = push(变大)、−1 = pull(变小)。gate 与 stamp 都按此约定把axis映射到xPercent/yPercent/scale;selector— 真正承载接缝运动的元素。主时间轴动的是 wrapper 时用 wrapper 选择器;接缝运动写在 sub-comp 内部时用 comp 内 hero(id 或[data-hf-id=…])。probe子命令会告诉你该用哪个;entry.scanRoot(Z seam)— 用于扫描"入场元素内部自带入场动效是否在和 Z 符号打架"的子树根,默认取 entry selector;carrier— 普通"cut"行可选;match-cut/morph 行必填。out/in两个 rect 必须在切点 ±1 帧处匹配(中心 12px / 尺寸 5% 容差,且祖先 transform 自动计入——因为测量走getBoundingClientRect)。
technique 是给人看的标注(如 cut-the-curve LEFT、inverse zoom-through),不参与判定。
VERIFY:每条检查行到底在断言什么
verify 对每个 seam 在四个时刻采样:cut-0.1s、cut-1帧、cut+1帧、cut+0.1s(dt = 1/fps,fps 取自 ledger 顶层,缺省 30)。页内 harness(__seamGate)的测量原语(HARNESS)值得单独说明,因为它决定了所有判定口径:
- 累计不透明度
cumOp:沿祖先链连乘opacity,途中遇到display:none或visibility:hidden直接归 0; - rect 测量:
getBoundingClientRect()取中心点(x/y 速度)与宽度比es = rect.width / layoutWidth(z 速度)。由于getBoundingClientRect天然包含祖先 wrapper 的 transform,外层 scale 无需单独补偿——这正是原文档最后一句"Velocities are measured ongetBoundingClientRect(x/y 用中心,z 用 width-ratio),祖先 transform 自动计入"的实现依据; - 可见性判定:累计 opacity >
0.04且面积 > 16px² 且在 1920×1080 屏内。
判定阈值常量(seam-gate.mjs#L39-L44):
const VIS = 0.04; // 累计不透明度低于此值 = 不可见
const EPS_XY = 15; // px/s —— 慢于此视为"静止"(exit 没在动 / entry 从静止起)
const EPS_Z = 0.04; // effective-scale 单位/s
const SPEED_RATIO = 3; // 入/出速度比超过 3:1(或 <1:3)→ WARN
const CARRIER_POS_TOL = 12; // 载体中心偏移容差 px
const CARRIER_SIZE_TOL = 0.05; // 载体尺寸差容差 5%
原文档给出的"报告行 ↔ 规则"对照表,结合源码的完整解读如下:
| 报告行 | 对应法则 | 源码中的判定逻辑 |
|---|---|---|
ledger |
计划内一致性:exit/entry 向量(axis + dir)必须匹配 | 纯静态比较,运行采样前执行;不匹配即 FAIL 并报"mirrored/mixed vector in the PLAN"(verify) |
exit-moving / entry-moving |
规则 1/3——不许"已静止再切",不许"从静止起入" | 双侧速度绝对值须 ≥ EPS(15 px/s 或 0.04 es/s),否则报 "exit settled before the boundary" / "entry starts from rest" |
exit-visible / entry-visible |
采样窗口内该侧必须真实可见 | 出方在 cut-0.1s 可见、入方在 cut+0.1s 可见,否则报具体 op 值 |
exit-direction / entry-direction |
规则 3——实测符号必须等于账本符号 | sgn(velocity) !== dir 即 FAIL;z 轴会额外标注 "(mirrored zoom)" |
speed-match(WARN) |
向量法则第 3 条——入场初速 ≈ 出场末速 | 速度比 >3 或 <1/3 才 WARN("want ~1"),不阻断门禁 |
zero-overlap |
规则 6——每一帧只允许一侧可见,切点不是叠化 | 入方在 cut-1帧 或出方在 cut+1帧 仍可见即 FAIL,报文中点明"reads as a dissolve" |
z-sign-scan |
规则 7——新场景自带的入场不得与接缝 Z 符号相斗 | 对 scanRoot 子树(上限 900 元素、忽略 <32px 与 op≤0.1 的元素)在 cut+1帧 → cut+0.1s 间测 scale 速度,符号与 entry.dir 相反的逐个列出(最多前 5 个) |
carrier-position / carrier-size / carrier |
规则 3/4——载体 rect 连续性,祖先 scale 计入 | 切点两侧载体中心差 ≤12px 且宽度差 ≤5% 才 PASS,报实测 Δpos/Δsize;match-cut/morph 无 carrier 行会 WARN "nothing to verify" |
输出形态:人读模式下每个 seam 一块(■ id (cut @Xs, type) ✓/✗ N FAIL + 逐行 PASS/WARN/FAIL check detail),末尾汇总 SEAM GATE: PASSED/FAILED — N fail, M warn across K seams 并据此定退出码;--json 则直接 dump 全部结果数组。
一个典型失败案例的排障链路(结合 SKILL 中的经验):zero-overlap FAIL 的常见根因是 clip-gating——data-start 早于入场 tween 的 clip 会在初始不透明度下被提前 un-hide。修复口径:初始 autoAlpha: 0 且 data-start = 切点时间,不要更早。此外记住"编辑重开接缝":任何对场景首/尾约 1 秒的改动(包括按新配音重排时间)都使该边界审计失效,必须重跑 verifier。
PROBE:切点附近的"运动元素探测器"
编写账本行时最常遇到的问题是:这个切点真正在动的是哪个元素、符号是什么?probe 就是为此设计的:
node <SKILL_DIR>/scripts/seam-gate.mjs probe --t 44.8 --project <project-dir>
工作原理(probe):在 #root 下做四次全树扫描(t-±window 与 t±1帧,--window 默认 0.1s),两两配对求 vx(中心 x 速度)、vy(中心 y)、vscale(宽度比速度)与透明度变化,过滤掉静止元素(阈值同 EPS),按速度量级降序取前 14 个输出,并附上人类语义提示:
PROBE @ 44.8s (window ±0.1s, 1f = 0.033s)
— OUTGOING side (44.70 → 44.77) — movers:
#el-claim vx -820 vy 0 vscale 0.000 op 1.00→0.41 (640×280)
...
Use these selectors + signs to write the ledger row (x-: left, y-: up, scale+: push, scale-: pull).
拿到 selector 与符号后照抄进 ledger.json 即可——这条"probe 发现 → ledger 落行 → stamp 生成 → verify 关闭"的循环,就是 Seam Gate 的日常使用闭环。
小结:门禁、账本与"修计划不修缓动"
把 seam-gate.md 放回上下文,Seam Gate 的工程价值在于三点:其一,把运动法则中"人眼可辨但难以复核"的要求(同轴同向、切在运动中、零叠化、Z 符号一致、载体连续)翻译成带明确阈值(15 px/s、0.04 es/s、12px/5%、3:1)的数值断言,可进 CI;其二,"账本 → 生成"让普通接缝零手写——stamp 与 gate 共享同一份 ledger.json,生成物按构造通过验证,人力集中到 Tier-A 的 carrier 交接上;其三,probe 把"找运动载体"这一最费时的定位工作自动化,保证账本里的 selector 与真实 DOM 运动一一对应。
脚本查不到的仍归作者:编辑重开接缝、音频即时钟(按 VO 真实词时间戳重排场景)、clip-gating 陷阱——这三条在 motion-doctrine/SKILL.md 的 Seam Gate 一节有完整表述。验证通过(exit 0)之后,一个 seam 才算"完成"。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00