Remotion flake 技能实战:在 Tracker Issue 中记录 CI 不稳定失败并自动恢复测试套件
Remotion 仓库为 Agent 提供了一组内部技能(internal skills),存放于 .agents/skills/ 目录下。其中 flake 技能 专门解决一类高频但棘手的工程问题:CI 检查"看起来"失败,实际是 flaky(不稳定/偶发)失败。读完本文,你将掌握一套完整的 flake 处理流程——如何用 gh CLI 定位失败的 PR、workflow run、job 与日志,如何判定一个失败是否真的是 flake,如何生成稳定的失败签名(signature),如何把失败登记进 tracker issue 的 Markdown 表格,以及如何选择正确的重跑(rerun)策略让受影响的测试套件恢复绿灯。
flake 技能的定位与适用场景
根据 skill-locations 技能 的约定,Remotion 把"仅内部 Agent 使用"的技能放在 .agents/skills/<skill-name>/SKILL.md,把可分发的公共技能放在 packages/skills/skills/ 下。flake 属于前者:它是维护者内部的工作流剧本(playbook),触发条件是"一个 Remotion CI 检查失败且看起来是 flaky",或用户显式请求执行 /flake。
技能 frontmatter 中声明的核心职责有三件事:
- 跟踪 Remotion 的 CI flake,统一登记在 tracker issue #8375;
- 对重复出现的失败签名(signature)累加计数;
- 在未给出 PR 时,主动发现失败的 PR 检查,并重跑 flaky 的 GitHub Actions job。
技能的总体目标可以概括为一句话:把 flaky 失败记录进 issue #8375,并让受影响的测试套件重新跑通。对每一个"已确认或疑似"的 flake,标准动作是三步:
- 定位 PR、workflow run、job、失败的 step,并提炼简洁的失败签名;
- 更新 tracker issue:签名已存在则累加其
Count,是新签名则追加一行(计数1、workflow/job、首次出现日期、证据 URL); - 重跑 flaky 的 job 或全部失败 job。
定位失败:gh 命令集
技能明确要求优先使用 gh CLI 而不是 GitHub App,原因是 GitHub App 可能无法暴露完整的 Actions 日志和写入权限。针对不同的输入上下文,给出了四组命令。
场景一:用户提供了 PR 编号
gh pr view <pr-number> --repo remotion-dev/remotion --json number,title,headRefName,statusCheckRollup
gh pr checks <pr-number> --repo remotion-dev/remotion --watch=false
第一条拿到 PR 的基础信息和完整的 check rollup(所有检查项的状态汇总),第二条直接列出各 check 的通过/失败明细,--watch=false 避免阻塞等待。
场景二:用户提供了 workflow run 或 job URL
gh run view <run-id> --repo remotion-dev/remotion --json databaseId,attempt,displayTitle,event,headBranch,headSha,status,conclusion,workflowName,createdAt,updatedAt,url,jobs
gh api repos/remotion-dev/remotion/actions/jobs/<job-id>/logs
注意 attempt 字段的含义——同一个 run 重跑后会形成多个 attempt,这直接决定了下一条命令的选择。第二条用 raw API 拉取指定 job 的完整日志,用于阅读失败 step 的实际输出。
场景三:没有 PR 上下文
当用户只说"CI 挂了"而没有给 PR 时,需要主动扫描最近的开放 PR 和最近的失败 run:
gh pr list --repo remotion-dev/remotion --state open --limit 30 --json number,title,headRefName,statusCheckRollup
gh run list --repo remotion-dev/remotion --workflow "Install and Test" --status failure --limit 20 --json databaseId,displayTitle,event,headBranch,headSha,conclusion,createdAt,url,workflowName
这里筛选的 workflow 名称 "Install and Test" 在仓库中是真实存在的:它定义在 .github/workflows/push.yml 中(name: Install and Test),由 push 到 main 和 pull_request 事件触发。该 workflow 的结构也解释了为什么它容易产生 flake:它先用一个 affected job 通过 bun .github/scripts/ci.ts plan 查询 Turborepo 任务图来裁剪出受影响的测试子集(lambda、nextjs、ssr、browser、webrenderer 等矩阵 job),再按输出条件执行对应的集成测试 job。测试矩阵被裁剪、并发取消(concurrency + cancel-in-progress)都会让超时类失败更具偶发性。
场景四:针对重跑过的 run,先查原始 attempt
一个常见陷阱:run 已经 rerun 过一次,直接看当前 attempt 的日志可能全是绿的,无法判断第一次失败是不是 flake。技能要求在判定前检查第一次 attempt:
gh api repos/remotion-dev/remotion/actions/runs/<run-id>/attempts/1/jobs --paginate
gh api repos/remotion-dev/remotion/actions/jobs/<failed-job-id>/logs
即:拉取 attempt 1 的 job 列表,再取其中失败 job 的日志。如果同一 run/job/step 在无代码改动的情况下重跑后成功了,这正是判定 flake 的第一条判据(见下一节)。
flake 判定标准
技能给出了明确的判定规则——满足以下至少一条即可视为 flake:
- 重跑验证:同一个 run、job 或失败 step 在没有代码改动的情况下重跑后成功了;
- 基础设施/网络类故障:例如
fetch failed、连接被重置(connection reset)、包管理器下载失败、缓存服务故障、runner 供给错误(provisioning error); - 与 PR 改动无关的偶发失败:超时、竞态(race)、浏览器断连、端口冲突、外部服务间歇性故障,且该 PR 的改动"不可能"触及出问题的区域。
同时给出了反例边界:不要把确定性的类型错误(type error)、lint 错误、snapshot 不匹配、或"由源码行为变更导致的测试断言失败"记入 tracker,除非有证据表明它们在重试后能通过。这条边界很重要——tracker 的价值在于把"噪声失败"与"真实回归"分开,误登记会把真实的 bug 淹没在 flake 表格里。
仓库中自带的 flake 缓解机制
判定标准不是凭空而来的。以 flake 文档示例中反复出现的 @remotion/it-tests#testssr 为例,packages/it-tests/package.json 里定义了 "testssr": "bun src/run-ssr-tests.ts"。而这个脚本本身就是一个精心设计的防 flake 包装器:packages/it-tests/src/run-ssr-tests.ts 对每个测试文件最多尝试 2 次,用 Bun.spawn 在独立子进程中运行 bun test <file> --timeout 40000,并设置了 90 秒看门狗——超时先发 SIGTERM,2 秒后再补 SIGKILL(非 Windows 下直接 kill 整个进程组);第一次超时会在干净进程中重试并打印 timed out. Retrying in a clean process.,第二次才超时则抛出 timed out twice。
从源码结构看,这正是 flake 文档里"timeout / race / browser disconnect"那类判据的现实来源:单个测试文件首次超时时,CI 未必会失败(脚本会自动重试一次),而"两次都超时"才会真正让 job 失败——这类失败在负载较高的 runner 上确实具有偶发性,是 tracker 表格的典型登记对象。同理,oven-sh/setup-bun@v2.1.2 failed with TypeError: fetch failed 这类签名对应的是 push.yml 中每个 job 都会执行的 setup-bun action 拉取失败(bun 版本固定为 1.3.3),属于典型的网络类基础设施故障,与 PR 代码无关。
签名(Signature)规则
签名是 tracker 表格中"把重复失败归为一组、又足够具体以便处理"的关键。技能的规则:
- 能给出 package 或 suite 名就给,例如
@remotion/it-tests#testssr; - 能给出测试名就给;
- 包含根因错误文本,但要剥离时间戳、文件路径、随机 ID、端口号、行号、ANSI 颜色码——这些是"同一次签名、不同次实例"的主要噪声来源;
- workflow/job 信息单独放在 tracker 行的对应列里,除非不放进签名就无法区分,否则不要混入签名。
文档给出的两个"好签名"示例:
`oven-sh/setup-bun@v2.1.2` failed with `TypeError: fetch failed``delayRender()` timeout while running `Render video with browser instance not open` in `@remotion/it-tests#testssr`
两个示例分别代表"基础设施类"和"测试超时类"两种典型 flake:前者签名锚定在失败的 action 版本上,后者锚定在"渲染模式 + 包#suite + 根因"上。第二个示例中的测试名 Render video with browser instance not open 确实存在于 it-tests 的 SSR 测试中(如 packages/it-tests/src/ssr/render-media.test.ts),说明签名规则是贴着真实测试套件写的。
更新 Tracker Issue
登记环节围绕 tracker issue #8375 的正文展开。先读取当前正文:
gh issue view 8375 --repo remotion-dev/remotion --json body --jq .body
操作时有两条硬性约束:
- 编辑完整正文、保留表格结构——不能只贴增量;
- 替换后的 Markdown 必须写入临时文件并用
--body-file提交,不要内联传递多行 Markdown(避免 shell 转义把表格弄坏)。
tracker 表格的格式:
| Count | Signature | Workflow / job | First seen | Evidence |
| ---: | --- | --- | --- | --- |
| 1 | `signature` | `Workflow` / `Job` | YYYY-MM-DD | <对应 Actions run 的 URL> |
对累加已有行的规则:
- 除非旧证据链接已失效、或新链接比旧链接更有用,否则只改
Count; First seen始终保持最早一次出现的日期。
对追加新行的规则:
- 证据列优先使用 GitHub Actions 的 job URL(比 run URL 更能直接定位失败步骤);
- 首次出现日期以日志中的时间戳为准,而不是本地机器日期——CI 日志时间戳与操作者本地时区可能不一致,用日志时间戳能保证跨维护者登记的一致性。
编辑完成后必须回读验证:
gh issue view 8375 --repo remotion-dev/remotion --json body --jq .body
这一步能捕获表格被破坏、行被重复追加等静默错误。
重跑策略
技能按三种情况给出了从轻到重的重跑手段:
只重跑失败 job(首选)——大多数 flake 场景用这一条就够了:
gh run rerun <run-id> --repo remotion-dev/remotion --failed
run 已结束、只有一个已知 flaky job 需要重跑时,用 raw API 直接重跑单个 job,避免整个 run 重来:
gh api --method POST repos/remotion-dev/remotion/actions/jobs/<job-id>/rerun
flaky 的 suite 仍在运行中、继续等待会阻塞当前任务时,先取消再重跑:
gh run cancel <run-id> --repo remotion-dev/remotion
gh run watch <run-id> --repo remotion-dev/remotion --exit-status
gh run rerun <run-id> --repo remotion-dev/remotion --failed
这里有一个容易踩坑的细节:取消会使 run 的最终状态变成 failure,因此 gh run watch 可能以非零状态退出——技能明确要求此时继续执行后面的 rerun 步骤,不要被非零退出码误导而中断流程。
收尾汇报
技能规定处理结束后向用户汇报三项内容:
- tracker issue 中新增或累加了哪一行;
- 采取的重跑动作,附 run 或 job URL;
- 还有哪些仍在运行的检查项是刻意没有动的(避免误杀正在进行中的正常 job)。
这一收尾设计保证了每次 flake 处理都是可审计的:登记动作、重跑动作、保留动作三件事都有据可查,tracker 表格随着时间累积后,本身就成为该仓库 CI 稳定性的观测数据——哪些签名 Count 高、集中在哪个 workflow/job,就是下一步修复(比如调整测试超时、加固缓存或升级 action 版本)的优先级依据。
小结
flake 技能的价值在于把"CI 挂了"这个模糊事件拆解成一条可复现的操作链:定位(四组 gh 命令覆盖有无 PR、有无重跑的不同上下文)→ 判定(三条阳性判据 + 一条排除边界)→ 签名(去噪后的稳定标识)→ 登记(body-file 全量编辑 + 回读验证)→ 重跑(failed-only、单 job API、cancel 后重跑三档策略)→ 汇报。配合仓库中 run-ssr-tests.ts 这类内置重试脚本和 push.yml 的任务图裁剪机制,它构成了 Remotion 这个大型 monorepo 处理偶发 CI 失败的完整闭环。
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 StartedRust0626
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