> 提交进仓库的二进制会和源码分叉——用导出符号 diff 发现,用 ABI 常量让它自己报
一个 WebAssembly 演示页把编译产物 docs/fractals.wasm 直接提交进仓库 (因为 GitHub Pages 的发布根就是 docs/)。
结论先行¶
一个 WebAssembly 演示页把编译产物 docs/fractals.wasm 直接提交进仓库
(因为 GitHub Pages 的发布根就是 docs/)。
| 事实 | 值 |
|---|---|
最后改动 docs/fractals.wasm 的提交 |
a729ed4,2025-11-12 |
最后改动 wasm/src/fractals_wasm.cpp 的提交 |
27be813,2026-09-12 |
十个月。而且不是"旧一点"——是不兼容。实测导出符号:
computeFractalBatch 二进制里有,源码里已经没有
tricornIterations 源码有,二进制没有
phoenixIterations 源码有,二进制没有
newtonRoot 源码有,二进制没有
fractalCoreAbiVersion 源码有,二进制没有
页面因此一直在跑它的 JavaScript 回退路径,至少从 2026 年 3 月起。 没有任何报错——回退是静默的,而页面确实画出了图。
为什么¶
这是一个有三层的问题,每层都要单独处理。
一、把构建产物提交进仓库,就是放弃了"源码改了产物会跟着改"这个保证。
而 docs/ 同时是发布根,所以这个决定本身是被工具链逼出来的,不能简单地说"不要提交产物"。
二、分叉是静默的,因为回退是设计好的。 一个写得好的加载器会在 WASM 不可用时退到 JS——这是对的。 但"不可用"和"版本不对"被归成了同一类,于是优雅降级掩盖了版本错配。
三、能发现它的检查不是时间戳,是符号表。 两边的 mtime/commit date 只能告诉你"可能旧了"; 导出符号的集合差告诉你"旧到不能用了"。这是一个可以写成脚本的检查。
修法是在源码里导出一个 ABI 版本号,页面侧断言它:
static REQUIRED_ABI = 2;
...
if (abi !== CompleteFractalRenderer.REQUIRED_ABI) {
// 在状态栏里报出来,而不是去调一个不匹配的导出
`fractals.wasm ABI ${abi}, page needs ${REQUIRED_ABI}`
}
关键在于它报告而不是回退。页面现在会说出"你看到的是回退路径"。
怎么用¶
任何把编译产物提交进仓库的项目(wasm、预编译扩展、生成的 parser、打包的前端 bundle):
- 加一个 CI 检查:产物的提交时间不得早于它的源码。
一行
git log -1 --format=%ct -- <artifact>和-- <src>比一下就够。 - 更强的那条:在 CI 里重建产物并 diff。 不需要提交重建的结果,只需要它和已提交的那份一致。
- 让产物导出一个版本号,让消费方断言它。 版本号要跟着 ABI 变(改了导出签名就 +1),不要跟着语义版本。 见 版本控制的边界要跟着"什么在变"走,不是跟着目录大小走。
- 回退路径必须可见。 任何"失败就降级"的逻辑都要在界面或日志里说出它降级了, 否则它会掩盖掉本该修的东西——这和"metric 工具静默回退"是同一个毛病, 见 指标的默认回退是静默的——而且 p 值不是效应量、最危险的工具故障是"静默少做"——技能在、后端不在。
出处¶
~/Engineering/Mandelbrot-Renderer,2026-10-11 实测:git log -1 -- docs/fractals.wasm→a729ed42025-11-12;git log -1 -- wasm/src/fractals_wasm.cpp→27be8132026-09-12;strings docs/fractals.wasm与源码逐符号比对(上表五个符号);docs/wasm-fractal.js:13的static REQUIRED_ABI = 2与:130的断言- 同项目
PROJECT_ENGINEERING_GUIDE.md§0.4