DSH / Atlas
2026-07-26implementedprocessarchived 2026-07-27

GUI pull request GIF evidence and assets-branch publication

GUI PR 的 GIF 证据与 assets 分支发布

A pull request that changes what a product user sees in the GUI is otherwise reviewed through prose and test names, neither of which shows the rendered result. The [browser-demo GIF recording](../../../skills/record-browser-gif/SKILL.md) skill produces truthful local GIFs but deliberately stopped at the local artifact, so each pull request that wanted to show one re-derived publication on its own — and committing the

English

Problem

A pull request that changes what a product user sees in the GUI is otherwise reviewed through prose and test names, neither of which shows the rendered result. The browser-demo GIF recording skill produces truthful local GIFs but deliberately stopped at the local artifact, so each pull request that wanted to show one re-derived publication on its own — and committing the GIF to the pull request branch is never acceptable, because binary media in history bloats every future clone permanently.

The recording procedure itself also kept being re-learned failure by failure: screenshots written outside the browser tool's allowed roots or into missing directories fail at capture time, transient UI states polled across separate tool calls are lost because the turn settles between calls, substring completion predicates match the echo of the user's own prompt, and an inline environment-variable assignment on the encoder command expands too late to take effect.

Decision

Every pull request that changes product-user-visible GUI behavior includes a demonstration GIF recorded with the record-browser-gif skill, with real provenance — a real server booted from that pull request's own branch tree, a real API key, and real model rounds — stated next to the embed. Fixture provenance is acceptable only when the user explicitly asked for it.

The GIF is published to a dedicated orphan assets branch — no parent commit, media only — never to the pull request branch; one assets branch serves a whole pull request series (existing branches: code-mode-ui-assets, pr-613-assets). Publication works in a shallow single-branch scratch clone, commits as assets: <what it shows> gif (#<pr>), and the pull request body embeds the blob URL with the required ?raw=true suffix. Assets branches are append-only: merged pull request bodies reference their URLs forever, so an assets branch is never rewritten or deleted.

Recording itself stays side-effect-free; publication is a bounded final step the skill performs only when the task includes attaching the GIF to a pull request. The record-browser-gif skill remains the current contract for the recording half.

The skill folds in the operational lessons recording earned: frames go under .playwright-mcp/, ignored by the repository .gitignore and created before capture, because the browser tool writes only under its allowed roots and resolves relative names against the repository root; each pull request stages its own built tree with a fresh scratch workspace and a new session per scenario, and servers are stopped by PID rather than a broad process-name pattern; transient states are captured by driving a slow foreground operation and polling a concrete DOM marker inside one browser-script call; completion predicates match an exact-text element rather than a substring; and the encoder runs with GIF_SKILL_DIR exported on its own line, per-frame durations holding the settled state longest, and both a JSON-summary check and a visual read of the encoded GIF.

Alternatives considered

Commit the GIF to the pull request branch. Binary media merged into the default branch stays in history for every future clone and fetch; a demo GIF's value ends at review while its cost never does.

Attach the GIF as a GitHub upload. Drag-and-drop user-attachments uploads are not available to a command-line workflow, cannot be re-created or audited from the repository, and leave the media's lifecycle outside repository control.

Store GIFs with Git LFS. LFS still couples media to the code branch's history, adds an infrastructure dependency to every clone and CI fetch, and buys nothing over an isolated branch that ordinary git already supports.

One assets branch per pull request. A branch per pull request sprawls the ref namespace and multiplies scratch clones during a series; one branch per series keeps publication a single push while staying isolated from code history.

Keep publication out of the recording skill. That was the prior state; it preserved a clean boundary but made every pull request re-derive the same procedure. The boundary survives as an explicit gate — publication runs only when the task includes attaching the GIF to a pull request — instead of as omission.

Leave the GIF optional per pull request. Optional evidence disappears under schedule pressure exactly where it matters most; a GUI change reviewed without a recording asks reviewers to imagine the rendered result or rebuild the branch themselves.

Consequences

Every GUI pull request carries visual evidence with stated provenance, and reviewers see the change without rebuilding the branch. Repository history stays free of media; the cost moves to append-only assets branches that grow forever, stay cheap to clone shallowly, and can never be deleted. Mandatory real-provenance recording adds a real-key, real-model round to every GUI pull request's workflow — deliberate, because that run is the evidence. The recording half remains locally reversible, and a GIF request whose task does not include attaching it to a pull request still ends at the verified local artifact.

中文

问题

改变产品用户在 GUI 中所见行为的 PR(Pull Request),此前只能通过文字描述和测试名称接受评审,两者都无法展示渲染结果。浏览器演示 GIF 录制对应的 skill(技能)能生成真实可信的本地 GIF,但刻意止步于本地产物,于是每个想展示 GIF 的 PR 都得各自重新摸索发布方式;而把 GIF 提交到 PR 分支从来不可接受:进入历史的二进制媒体会永久增大之后每一次克隆的体积。

录制流程本身也在靠一次次失败反复重新学习:截图写到浏览器工具允许的根目录之外或写入不存在的目录,会在截取时直接失败;跨多次工具调用轮询的瞬态 UI 状态会丢失,因为调用之间轮次已经结算;用子串匹配做完成判定会命中用户自己提示词的回显;在编码器命令上内联赋值环境变量则因参数先于赋值展开而不生效。

决策

每个改变产品用户可见 GUI 行为的 PR 都包含一个用 record-browser-gif skill 录制的演示 GIF,其来源必须真实:从该 PR 自身分支树启动的真实服务器、真实 API 密钥、真实的模型轮次,并在嵌入处注明来源。只有当用户明确要求 fixture(测试前置数据)来源时才可使用 fixture。

GIF 发布到专用的孤儿(orphan)assets 分支上:该分支没有父提交、只含媒体,GIF 绝不进入 PR 自己的分支;一个 assets 分支服务整个 PR 系列(现有分支:code-mode-ui-assetspr-613-assets)。发布在浅层单分支的临时克隆中进行,提交信息形如 assets: <what it shows> gif (#<pr>),PR 正文用带必需 ?raw=true 后缀的 blob URL 嵌入。assets 分支只允许追加:已合并的 PR 正文会永远引用其 URL,因此 assets 分支绝不重写或删除。

录制本身保持无副作用;发布是一个有边界的收尾步骤,仅当任务包含把 GIF 附到 PR 时才由该 skill 执行。record-browser-gif skill 仍是录制部分的现行契约。

该 skill 还吸收了录制实践换来的操作经验:帧文件放在仓库 .gitignore 忽略的 .playwright-mcp/ 目录下并在截取前先创建,因为浏览器工具只能写入其允许的根目录,相对文件名也相对仓库根目录解析;每个 PR 从自己构建的分支树启动服务,配以全新的临时工作区目录,每个录制场景新开会话,停止服务器时按 PID 精确匹配而不是用宽泛的进程名模式;瞬态状态靠驱动一个缓慢的前台操作、并在同一次浏览器脚本调用内轮询具体的 DOM 标记来截取;完成判定匹配精确文本元素而非子串;编码器在单独一行 export GIF_SKILL_DIR 之后运行,逐帧时长让最终稳定状态停留最久,并同时核对 JSON 摘要与目视检查编码后的 GIF。

曾考虑的替代方案

**把 GIF 提交到 PR 分支。**合入默认分支的二进制媒体会留在历史中,影响之后的每一次克隆和拉取;演示 GIF 的价值止于评审,代价却永不消失。

**作为 GitHub 附件上传。**拖拽产生的 user-attachments 上传对命令行工作流不可用,无法从仓库重建或审计,媒体的生命周期也脱离仓库的控制。

**用 Git LFS 存储 GIF。**LFS 仍把媒体耦合进代码分支的历史,给每次克隆和 CI 拉取增加一项基础设施依赖,相比普通 git 即可支持的隔离分支没有任何额外收益。

**每个 PR 一个 assets 分支。**按 PR 建分支会让 ref 命名空间蔓延,并在一个系列内成倍增加临时克隆;每个系列一个分支让发布只需一次推送,同时仍与代码历史隔离。

**把发布留在录制 skill 之外。**这是此前的状态;它保住了干净的边界,却让每个 PR 重新摸索同一套流程。这个边界如今以显式条件的形式保留:仅当任务包含把 GIF 附到 PR 时才执行发布,而不是靠省略来体现。

**让 GIF 在每个 PR 中保持可选。**可选的证据恰恰会在最需要它的进度压力下消失;没有录制的 GUI 变更评审,等于要求评审人自行想象渲染结果或重新构建分支。

后果

每个 GUI PR 都携带注明来源的可视证据,评审人无需重新构建分支即可看到变更。仓库历史保持不含媒体;代价转移到只追加的 assets 分支上:它们会持续增长、可以低成本地浅克隆、且永远不能删除。强制的真实来源录制给每个 GUI PR 的工作流增加一次真实密钥、真实模型轮次的运行,这是有意为之,因为这次运行本身就是证据。录制部分仍然在本地可撤销;任务不包含附到 PR 的 GIF 请求,仍以已验证的本地产物结束。