Communitygithub.com

genekogan/proof

录制一个T2及以上变更所需的证明视频片段——一段未经剪辑、实时速度的 Playwright 录制,展示在本地模拟世界上的用户旅程变化,包含进度条和时间码叠加,桌面端和(布局变动时)手机端各一份,然后将其附加到 Pull Request 中,且不将视频提交到仓库。

proof 是什么?

该技能的核心是为 T2 及以上级别的变更提供高质量的视觉证据。它要求录制一个完整、未经剪辑、实时速度的用户旅程视频,从世界页面加载完成(或指定开始点)开始,到变更结束。视频必须包含进度条和帧时间码,并且只能在本地模拟世界(fake-mind)上录制,使用空数据目录,以确保数据隔离和可复现性。 技能覆盖多个平台(桌面端和手机端),并自动处理视频录制、元数据记录(侧边栏 JSON)、以及通过 GitHub Releases 将视频上传并附加到 PR 的过程,同时生成预览 GIF 以便在 PR 中内嵌展示。它严格遵循 `PROOF-POLICY.yml` 中定义的 Tier 要求,并包含多项检查(如本地烟雾测试、上传前文本泄露检测)来保证证据的完整性和安全性。 工作流程包括:启动一个模拟世界服务器、编写录制步骤脚本、执行录制、检查录制内容,最后通过 `upload.mjs` 将视频作为 GitHub Release 资产上传并生成可内嵌的预览链接。整个过程无需将视频文件提交到仓库,符合最佳实践。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/genekogan/souls/tree/HEAD/.claude/skills/proof

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

预览

来自技能 README

desktop.mp4

文档

proof — 一段未经剪辑的变更旅程视频

审阅者应该能够以正常速度,从头到尾观看用户所看到的内容。视频就是证据;截图、联系表或快进录屏都不算。

关于不同 Tier 需要什么的权威来源:docs/process/PROOF-POLICY.yml(T0 无;T1 聚焦的测试 + 类型检查;T2 在模拟世界上添加一段桌面端变更旅程的视频,如果布局改变了还要加手机端;T3/T4 增加更多)。通过 node tools/proof/tier.mjs --base origin/main 获取当前差异的 Tier。

规则

  • 未经剪辑且实时速度。 一个浏览器上下文,一次录制。绝不要修剪、拼接、加速或减速。如果旅程时长超过策略中的 max_seconds,请录制一个仍能展示变更但更短的旅程,而不是一个更快的旅程。
  • 录制从到达(arrival)开始(默认):录制在世界显示在屏幕上且到达卡片消失后开始,因此视频片段从世界画面开始,而不是加载画面。从那里开始,是一次未经剪辑、实时速度的旅程录制,这正是策略所要求的。当加载或到达本身就是变更的内容(例如到达卡片、加载屏幕、首屏绘制),或者旅程不在世界页面上(世界列表、登录页)时,请传入 --from-load:此时录制从页面创建开始。侧边栏中的 start(arrival 或 load)说明了使用的是哪种方式,并且 upload.mjs --proof 会将其复制到 proof.json 的 media[] 中。
  • 白天时间。 在本地世界 URL 上,录制器会在页面打开前将世界时间设置为正午并暂停时钟(--time-of-day 12,默认值;作为开发所有者使用的 god 环境路由)。开发世界在 06:00 启动,并以每小时一个世界日的速度运行,因此没有这个设置的话,一个运行了 40 分钟的世界会是夜晚。此设置会在世界的 god 历史中添加一个 world 条目,之后时钟保持暂停。如果时钟、灯光或 god 历史本身就是变更的内容,可以传入另一个小时数或 --time-of-day keep。侧边栏的 worldTime 会说明设置的值。
  • 真实输入。 使用 page.mouse、page.keyboard、page.touchscreen 或定位器点击来驱动,像用户一样操作。不要在摄像头上通过 page.evaluate 调用应用内部方法来推动事件。
  • 模拟大脑,隔离数据。 仅限本地世界,BRAIN_MODE=fake,使用全新的数据目录。绝不要对着生产世界录制,除非维护者要求,也绝不要使用已登录的个人浏览器配置文件。
  • 绝不要将视频(或帧)提交到仓库中。在 .e2e/(已被 gitignore 忽略)下工作。
  • 视频片段必须展示变更内容。在附加它之前,请观看它(或至少提取帧,见下文)。

1. 启动一个模拟大脑世界

需要一个构建好的 Web 客户端(pnpm install --frozen-lockfile && pnpm build;如果 apps/web/dist/index.html 已存在且对你的变更为最新,可跳过)。在新的克隆中,构建的预检检查会失败,直到 Web 媒体资源就位:node tools/assets/pull.mjs --only 'apps/web/**'(几个文件)就足够了。

node tools/god-skill/dev-world.mjs proof 4710 --genesis worlds/templates/hello-hamlet.json --fresh --data .local/proof
# 输出 http://127.0.0.1:4710/w/proof?dev=1 (pid N, log .lane/proof.log); 服务器保持后台运行

首页(世界列表,新建世界)由世界启动器提供服务,而不是由单个世界。对于从那里开始的旅程,请运行一个启动器(其世界总是获得模拟大脑)并通过其 API 创建一个世界以显示在列表中:

node tools/world-launcher/launcher.mjs --port 4720 --range 4721-4723 --data "$PWD/.local/proof-launcher" --eager 0 &
curl -s -X POST -H 'content-type: application/json' -H 'authorization: Bearer dev:proof' \
  -d '{"name":"Proof Garden","template":"hello-hamlet"}' http://127.0.0.1:4720/__sim/worlds
# 对着 http://127.0.0.1:4720/ 录制;通过 PID 停止启动器(它会停止其所有世界)

选择包含变更界面的模板(worlds/templates/:hello-hamlet 小而快;eden-2026、mars-two 等更大)。使用空闲端口;e2e/config.mjs 列出了为其他测试工具预留的端口。完成后使用 kill <pid> 停止服务器。

快速验证应用是否正常运行:本地烟雾测试

在录制之前,或者作为 T1 变更(涉及服务器或 Web 应用)的整个证明,请运行本地烟雾测试。它会启动自己的隔离启动器(模拟大脑、开发登录、数据在运行目录下)在一个空闲端口上,打开世界列表和一个世界,检查一个人物是否被绘制且没有控制台错误和失败的请求,然后停止它启动的一切:

node e2e/local-smoke.mjs --port 9950 --clip      # 在新克隆上先 --build;省略 --port 以使用任意空闲端口
# → .local/e2e/<timestamp>/summary.json, shots/1-world-list.png 2-world.png 3-person.png, clip.mp4
node tools/proof/upload.mjs --pr $N .local/e2e/<timestamp>/summary.json .local/e2e/<timestamp>/shots/*.png

summary.json 中的 status: "pass" 就是证据;其视频片段是一段未经剪辑、实时速度的烟雾测试旅程。它不是你变更的视频片段:T2 变更仍然需要录制自己的旅程,见下文。详细信息:docs/testing/e2e.md#local-smoke。

2. 编写步骤

将 .claude/skills/proof/steps.example.mjs 复制到 .e2e/proof/<slug>/steps.mjs 并编写变更旅程的脚本:执行操作,展示结果。在默认的到达(arrival)启动模式下,步骤从世界到达后开始(arrived 标记为 0:00);使用 --from-load 时,步骤在导航后立即开始。mark('label') 会将章节时间戳写入侧边栏,这样 PR 上就可以说“面板在 0:07 打开”。请只编写变更所影响的旅程。

3. 录制

node .claude/skills/proof/record.mjs --url 'http://127.0.0.1:4710/w/proof?dev=1' \
  --steps .e2e/proof/<slug>/steps.mjs --out .e2e/proof/<slug>/desktop.mp4 --viewport desktop
# 布局或触摸有变化?也录制:
node .claude/skills/proof/record.mjs --url '...' --steps .e2e/proof/<slug>/steps.mjs \
  --out .e2e/proof/<slug>/phone.mp4 --viewport phone
  • 浏览器:使用 PLAYWRIGHT_BROWSERS_PATH,如果存在则使用 /opt/pw-browsers。如果 Chromium 缺失: PLAYWRIGHT_BROWSERS_PATH="$PWD/.e2e/browsers" pnpm --filter mars-campus-viewer exec playwright install chromium。
  • Playwright 的解析方式与 playtest 测试工具相同(tools/playtest/lib/platform.mjs:全局的 playwright,否则是 mars/viewer 中的 @playwright/test)。不需要 GPU;WebGL 在 SwiftShader 上运行。
  • --max-seconds 默认为策略中的 max_seconds。退出码 3 表示旅程运行超时:请缩短它。
  • 使用 ffmpeg:会生成一个 mp4 文件,底部有进度条和 hh:mm:ss / total 时间码(时间码需要带有 drawtext 的 ffmpeg;侧边栏的 timecode 会说明时间码是否存在)。没有 ffmpeg:生成原始的 .webm 文件;在 PR 中说明(播放器仍然显示自己的进度条)。
  • 每个视频片段都会产生一个 <clip>.json 侧边栏:包含 URL、视口、时长、start、startedAt、标记(包括 arrived)、worldTime、控制台错误、提交和脏标记。如果 Playwright 没有 page.screencast,即使没有 --from-load,也会从页面创建开始录制;侧边栏会在 startFallback 中说明。
  • 页面有 --arrival-seconds(默认 120)在录制开始前等待世界到达;从未显示世界的页面会失败(退出码 1),并提示使用 --from-load,同时生成一个静止图像(<clip>.open-failed.png)和一个侧边栏。侧边栏中的控制台错误是需要分析发现的问题:请解释或修复它们。

录制运行时间远低于 10 分钟的前台限制。如果需要一次录制多个视频,可以在后台运行它们(nohup setsid node ... > .e2e/proof/<slug>/rec.log 2>&1 &)并轮询日志。

4. 亲自检查

for t in 1 4 8; do ffmpeg -loglevel error -y -ss $t -i .e2e/proof/<slug>/desktop.mp4 -frames:v 1 .e2e/proof/<slug>/t$t.png; done

查看这些帧(阅读 PNG 文件)。变更的状态必须可见,世界必须已加载(不是空白或加载中的画布),并且帧内不应包含任何私人信息。如果视频没有展示变更,请修复步骤并重新录制;不要附加一个不能证明所声称内容的视频。

5. 附加到 PR(绝不提交到仓库)

在发布前,使用 node tools/proof/publication.mjs <payload files> 检查每个外发的文本/JSON 负载。单独检查媒体像素、音频、文件名和二进制元数据。设备路径、主机名、设备/私有 IP、本地 URL 和操作标识符只属于私有记录。使用相对路径和中性的捕获标签。上传器在上传批次前会阻止检测到的文本泄露;这不能保证媒体隐私。

node tools/proof/upload.mjs --pr $N --proof .e2e/journal/$ISSUE/proof.json .e2e/proof/$SLUG/desktop.mp4 .e2e/proof/$SLUG/phone.mp4
  • 默认目标是 PR 自己的预发布版本 proofs-<n>(在默认分支上的标签,首次使用时创建);每个 PR 一个发布版本可以保持每个发布版本在 GitHub 的 1000 个资产限制之内,而旧的共享 proofs 发布版本已经达到了这个限制。--release <tag>(或 SIM_PROOF_RELEASE)可以指定另一个发布版本。资产被命名为 pr-<n>.<file>,重新录制的版本会得到 -2、-3 后缀,这样早期的录制版本(包括失败的)仍然可以链接。SIM_PROOF_UPLOAD_URL(+ SIM_PROOF_UPLOAD_TOKEN)会切换到另一个端点,例如 CDN。
  • 使用 --proof 时,视频片段和图片链接会被写入 proof.json 的 media[],其他文件(日志、JSON)写入 files[],并且 proof.json 最后上传。将它的链接和视频片段链接粘贴到 PR 的 Proof 部分。journal 技能涵盖了 proof.json。
  • 内联预览。 GitHub 将发布版本资产作为下载提供,因此 mp4 链接永远不会在 PR 中播放。对于每个视频,upload.mjs 会使用 ffmpeg 制作一个小的动画 GIF(<clip>.preview.gif:从视频片段的 arrived 标记处,或对于 --from-load 录制从 0 开始,以实时速度播放 6 秒;最大 5 MB),将其与视频片段并排上传,记录为 media[].preview,并输出类似于 [![desktop.mp4](…pr-412.desktop.preview.gif)](…pr-412.desktop.mp4) 的行。粘贴该行:预览图会在 PR 中播放,点击它会打开完整的视频。当变更在视频后续部分才展示时,可以使用 --preview-start <s>(和 --preview-seconds <s>)选择窗口。预览是指针,不是证据:未经剪辑的 mp4 才是。在每次预览前,始终放置一个明确的全分辨率未经剪辑视频链接,说明实际编码的尺寸和时长。保持捕获分辨率并框定动作,以便可以审查接触点。不要放大捕获画面或使用缩小的 GIF 来声称运动证据。
  • 退出码 3:上传被阻止(没有 gh,未登录,没有权限,或者没有网络,许多云会话中常见)。告知用户哪些视频在等待以及它们的位置。发布一个 PR 评论,包含每个视频的时长、视口、经过清理的侧边栏摘要(标记、控制台错误、提交),以及一个仓库相对路径的 record.mjs 命令和步骤文件,以便任何人都可以重新录制它。在 PR 正文中将证明标记为“pending upload”。绝不要将视频作为 base64 粘贴,绝不要提交它,也绝不要将其推送到另一个分支。

gh pr 在云会话中可能不起作用;请使用 gh api repos/$REPO/issues/$N/comments -F [email protected]/proof/$SLUG/comment.md 发表评论。

在视频旁边写什么

每个视频一行:是什么旅程,哪个世界模板,视口,时长,提交,以及变更可见的时刻(“0:07 座位高亮显示,Soul 坐下”)。加上任何控制台错误以及它们为什么是可以接受的。

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

相关技能