> 被引用的产物要有一个"任何一次运行都生成不出来"的文件名

Systems & Infrastructure Intermediate 2026-10-11 18:09 2026-10-11
#reproducibility #provenance #artifacts #git #scripts #failure-modes

README 里有九行延迟数据,取自一个提交进仓库的 benchmark JSON。

// TABLE_OF_CONTENTS

结论先行

README 里有九行延迟数据,取自一个提交进仓库的 benchmark JSON。

后来写了一个 reproduce.sh,它存在的全部目的就是把 README 里每个数字重新导出来。 那个脚本把 benchmark 结果写到 results/benchmark_$(hostname)_$(date +%Y%m%d).json。 那天,这个路径解析出来正好是 README 引用的那个文件。

核验那张表的脚本,覆盖了那张表引用的产物。 而且十天之内没有任何人可能发现。

三条修法合起来才管用:

  1. 那个 stage 改写到 /tmp/,根本到不了 results/;
  2. 恢复出来的产物重命名成 ..._20260830_readme.json—— 一个任何一次运行都生成不出来的名字($(date) 永远不会产出 _readme);
  3. 加 scripts/check_readme_table.py,断言 README 的表仍然和它命名的那个文件一致。

为什么

"产物被覆盖"的根因不是手滑,是生成路径和引用路径共用一个命名空间。 只要文件名是由 hostname/date/参数拼出来的,就一定存在某个参数组合撞上已发布的那份。 而撞上之后:

  • 数据是确定性的,所以多数时候逐字节重写,看不出任何异常;
  • 改一下 --max-rows 或策略,就静默替换了一个已发表的产物;
  • git status 是唯一的提示,而跑完脚本的人不一定看。

同一个项目里还有一条同源的:bench_series.sh 是追加,不清目录。 往同一个目录跑两次,两段序列被静默并在一起,回退门禁会把那个接缝读成一次阶跃。 reproduce.sh 在调用前 rm -rf 了自己的目录,而作为门禁基准真值的 local/ 目录没有这层保护。

所以这个项目后来把 40 次运行的基准目录明确写成数据,不是输出—— 在另一台硬件上重新生成它,会改变门禁眼里"正常"的样子,也改变"一次植入的 25% 回退"长什么样。

怎么用

扫一遍仓库里每一个被文档/论文引用的产物,逐个问:

  1. 有没有某次运行能写到这个路径? 用脚本里出现的所有 $(...) 展开组合去试。 能撞上,就把被引用的那份改名成一个模板生成不出来的名字(_readme、_published、_vN)。
  2. 这份产物是输入还是输出? 门禁的基准、论文的表、README 的数——都是输入。 输入放在脚本写不到的目录里。
  3. 故意重定向到 /tmp 的路径要写明原因,否则下一个人会把它"修"回 results/。 这个项目有三条,每条都带注释说明被覆盖过一次:
stage 写到 因为提交的那份是
tick-to-trade /tmp/titans_t2t.json 40 万行 + 饱和扫描;这个 stage 是 10 万行
subset selection /tmp/titans_subset.json 28 天那次;这个 stage 是一天
embargo sweep /tmp/titans_wf_sweep.json 提交的 5 点扫描
  1. 加一条断言,把文档和产物绑起来。 这是唯一能让"十天没人发现"变成"下一次 CI 就红"的东西。 追加型脚本同时要加 rm -rf 或拒绝写入非空目录。

出处