Ruff ty 生态系统报告的证据冻结:如何精确钉住并验证 PR 生态分析的 GitHub Actions 证据
本文基于 Ruff 仓库中 summarise-ecosystem-results 技能包的参考文档 evidence-acquisition.md,系统讲解 ty 生态分析(ty ecosystem-analyzer)结果汇总流程中的第一步——“证据冻结”:如何优先保存用户显式提供的报告与 PR 评论、如何精确定位对应的 Actions run 与 attempt、如何验证并下载不可变的构建产物(artifact),以及为什么必须优先使用所选 attempt 的 diff.json 结构化差异。读完本文,你将掌握一套可复制的取证操作序列(含完整 bash 脚本),能独立复现一次历史生态分析的完整证据链,并理解其背后的产物替换机制与验证原则。
背景:为什么需要“冻结”生态系统证据
Ruff 项目对每次涉及 crates/ty*/** 等路径变更的 PR 都会触发 ty-ecosystem-analyzer.yaml 工作流:分别构建 merge base 与 PR 两个 revision 的 ty profiling 二进制,然后在数百个真实开源项目上运行两者并比对诊断输出,最终生成结构化的 diff.json 与人类可读的 diff.html 报告,写入 full-report artifact,并把摘要评论(ecosystem-results comment)回贴到 PR。周期性全量报告则由 ty-ecosystem-report.yaml 每周三生成并上传 full-report artifact(内含 dist/index.html)。
问题在于:PR 上“当前可见的报告”并不等于“你要分析的那一次运行产生的报告”。重新运行(rerun)会创建新的 attempt,新的 attempt 可以替换旧 attempt 的产物,而 Ruff 的 base/PR revision 甚至可能保持不变;PR 评论也可能被编辑或覆盖。因此,SKILL.md 定义的工作流把“Freeze the evidence(冻结证据)”列为第一步,而本文档 evidence-acquisition.md 正是这一步的操作细则:把所有证据钉死到唯一的 run、attempt、artifact 上,后续的复现、最小化与报告撰写(report-template.md)全部基于冻结下来的快照,而不是任何“实时”数据。
第一步:报告来源的优先级与 run/attempt 定位
文档开篇给出了三条来源裁决规则:
- 用户显式提供的报告/评论最高优先:在汇总请求开始时,立即保存用户在请求中明确给出的详细报告或 ecosystem-results 评论。只有当用户没有提供任何报告或评论时,才去查找 PR 当前的评论。
- 绝不替换:一旦选定某份已部署的报告,就要识别与其匹配的 PR、Actions run 与 attempt;永远不要用更新的评论、更新的报告、更新的 PR revision、更新的工作流运行或更新的 report 尝试来替换它。
- 不确定就如实报告:如果匹配用户显式报告的评论已不存在,继续使用该报告本身,并记录“匹配评论不可用”;如果无法唯一确定对应的 Actions run 或 attempt,应报告这一不确定性,而不是退回“当前 PR 报告”或从恰好匹配的 Ruff revision 进行猜测。
这套规则与 SKILL.md 中“忽略后续评论编辑、PR 更新与工作流运行”的要求一致:证据一经选定,后续分析对一切“更晚出现的同类证据”免疫。
第二步:创建唯一快照目录并保存元数据
定位到 run 与 attempt 后,文档给出一段可直接执行的 bash 序列,建立唯一的临时快照目录,并把三份关键元数据落盘:
set -euo pipefail
snapshot_dir="$(mktemp -d "${TMPDIR:-/tmp}/ty-ecosystem-report.XXXXXX")"
ecosystem_comment_id="<matching-comment-id-or-empty>"
if [[ -n "$ecosystem_comment_id" ]]; then
GH_TELEMETRY=false gh api "repos/astral-sh/ruff/issues/comments/$ecosystem_comment_id" > "$snapshot_dir/comment.json"
fi
GH_TELEMETRY=false gh run view <actions-run> --repo astral-sh/ruff --attempt <actions-attempt> \
--json attempt,headSha,jobs,startedAt,updatedAt,url > "$snapshot_dir/run.json"
GH_TELEMETRY=false gh api --paginate --slurp \
"repos/astral-sh/ruff/actions/runs/<actions-run>/artifacts?per_page=100" |
jq '{artifacts: [.[].artifacts[]]}' > "$snapshot_dir/artifacts.json"
printf 'TY_ECOSYSTEM_SNAPSHOT_DIR=%s\n' "$snapshot_dir"
逐段说明:
mktemp -d保证每次取证都有唯一的快照目录,避免多次分析互相污染;目录路径通过TY_ECOSYSTEM_SNAPSHOT_DIR环境变量对外发布,供后续子代理(见 subagent-handoff.md)以只读方式共享。- 若存在匹配的 ecosystem-results 评论,用
gh api按评论 ID 直接拉取 JSON 并保存为comment.json。脚本中所有gh调用都带GH_TELEMETRY=false前缀——这是该技能包的硬性规定(SKILL.md 要求包括子代理在内的一切直接或间接触发gh的调用都必须加此前缀,且因为不同工具调用可能各自开 shell,export不会自动继承)。 gh run view <actions-run> --attempt <actions-attempt> --json ...固化该次运行在该 attempt 下的attempt、headSha、jobs(有效作业图)、startedAt、updatedAt与url。注意这里显式带--attempt:不带该参数的gh run view默认取最新 attempt,正是需要规避的“取到新证据”陷阱。gh api --paginate --slurp分页拉全该 run 的全部 artifact 元数据(含不可变的id与创建时间createdAt),供下一步做产物—作业的匹配验证。- 注意脚本中的 API 仓库名
astral-sh/ruff是上游仓库地址;本地工作副本的绝对路径、二进制路径等由后续流程另行发布,快照本身只记录这些“指向性”元数据。
核心原则:下载前必须完成的两类产物验证
文档特别强调:gh run download 无法按 attempt 选择,而一次较新的 rerun 可以在 Ruff revision 完全不变的情况下,替换掉旧 attempt 的 artifact。因此,在真正下载任何 artifact 之前,必须基于**有效作业图(effective job graph)**完成验证,具体是:
full-report必须创建于所选 attempt 的 report 生成作业期间;- 每一个 diagnostics shard 必须创建于其对应的、成功的 shard 作业期间。
这里有两个容易踩错的判断细节:
- 以有效作业图为准,而不是 attempt 的启动时间。部分重跑(partial rerun)会合法地继承早期 attempt 中已成功的作业及其 artifact——某个 shard 的
createdAt早于所选 attempt 的startedAt并不意味着它不属于这次运行,必须看它是由作业图里哪个作业产出的。 - 保留每个已验证 artifact 的不可变 ID,下载时永不按可变的 artifact 名称重新解析。同名 artifact 在 rerun 后可能指向完全不同的 zip 包,只有
actions/artifacts/<id>/zip这种按 ID 取件的方式才能保证拿到冻结时验证过的那一份。
第三步:按 artifact ID 下载已验证的产物
验证通过后,文档给出一个通用下载函数,直接以 artifact ID 拉取 zip 并解包:
download_validated_artifact() {
local artifact_id="$1"
local destination="$2"
mkdir -p "$destination"
GH_TELEMETRY=false gh api "repos/astral-sh/ruff/actions/artifacts/$artifact_id/zip" \
> "$snapshot_dir/artifact-$artifact_id.zip"
unzip -q "$snapshot_dir/artifact-$artifact_id.zip" -d "$destination"
}
download_validated_artifact <validated-full-report-id> "$snapshot_dir/full-report"
download_validated_artifact <validated-shard-id> \
"$snapshot_dir/shards/diagnostics-shard-<number>"
full-report 解到 $snapshot_dir/full-report,每个 diagnostics shard 解到 $snapshot_dir/shards/diagnostics-shard-<number>。这些路径随后就是整个调查过程中唯一被信任的本地证据来源。
第四步:HTML 字节比对与精确运行元数据的收集
选定并下载的产物还要通过一道交叉校验:如果当时选定的已部署 HTML 报告是可得的话,在信任其相邻 JSON 之前,先把它与 artifact 里的 diff.html 做逐字节比较。只有两者一致,才能确认这份 artifact 就是你分析对象的那次运行生成的。
接下来调用仓库自带的元数据收集脚本 scripts/collect_ty_ecosystem_run_metadata.py:
- 该脚本通过
gh run view拉取指定 run(可用--attempt <actions-attempt>指定 attempt),并且有严格的前置校验:工作流名必须是ty ecosystem-analyzer、运行状态必须为completed,否则直接以MetadataError失败退出(见 collect_ty_ecosystem_run_metadata.py 中parse_run_reference与collect_metadata的校验逻辑); - 它从该 attempt 的
Build ty (base)作业日志中解析Merge base:与PR commit:两个 SHA,从 shard 作业日志中解析EXCLUDE_NEWER时间戳与 ecosystem-analyzer revision,并交叉核对 build 与 shard 日志中的 merge base 是否一致(不一致即报错,见 collect_ty_ecosystem_run_metadata.py); - 按文档要求,
--attempt <actions-attempt>必须传入,且对所有需要复现的项目只运行一次——产出的不可变 manifest 随后作为共享只读输入分发给所有子代理(这是 subagent-handoff.md 中“主代理负责发布TY_ECOSYSTEM_RUN_METADATA等不可变路径”约定的数据来源)。
最后一步一致性检查:验证冻结的 HTML 报告中记录的 Ruff base revision 与 PR revision,和上述不可变 manifest 中的值一致;此后,整个调查过程只使用保存下来的报告、shards、run、attempt(以及可得时匹配的评论),不再接触任何“实时”证据。
优先使用精确 attempt 的结构化 diff
文档的第二个主题,是解释 diff.json 与 diff.html 的权威性与出处(provenance)规则:
- 首选来源:当已验证的
full-reportartifact 内含diff.json时,$snapshot_dir/full-report/diff.json就是权威的结构化变更清单(authoritative structured change inventory),而相邻的diff.html是它的人类可读对应物。 - 内容本身不携带出处:
diff.json里不包含任何 Ruff revision、Actions run ID 或 attempt 编号——它的可信出处来自“已验证的 artifact + 与其匹配的 HTML 报告”这一组合,而不是来自 JSON 内容,更不是来自恰好一致的 PR revision。 - 已部署的相邻
diff.json只能有条件使用:部署站点也会在详细 HTML 报告旁边暴露一个diff.json。只有当这个已部署 JSON 与所选 HTML 报告冻结自同一个不可变部署,且该部署能绑定到你选定的 Actions run 与 attempt 时,才能使用它;“当前 PR 的部署”“更晚的 artifact”或“Ruff commit 恰好匹配”都不足以建立出处。 - 不要假设字段稳定:必须在你所锁定的 ecosystem-analyzer 精确 revision 上检查结构化报告以及 schema/diff 生成代码;不同 revision 之间 JSON 字段名与分类方式可能变化。
- 记录分析模式:记录每个受影响项目是 strict 还是 non-strict 分析模式,在适用的比较方法描述中同时给出两个 strict 分析标志。
- 降级路径:如果所选 attempt 的 JSON 报告或 artifact 不可用、已被替换,或 artifact 中的 HTML 与冻结的已部署报告不一致,则回退使用冻结的 HTML 报告,明确披露不可用的 shard 及由此带来的验证局限,并且永远不用其他 attempt 的 artifact 顶替。
与仓库内工作流的对应关系
这套取证流程的每个环节都能在仓库中找到产出端对应物。从 ty-ecosystem-analyzer.yaml 可以看到:build-ty 作业以 revision: [base, pr] 矩阵分别构建两个 revision 的 --profile profiling 二进制(base 侧的 merge base 取 GITHUB_SHA^1,即“第一父提交”,以兼容堆叠 PR),并上传 ty-build-base(含 ty-base、merge-base.txt)与 ty-build-pr(含 ty-pr、projects_flaky.txt、ty-ecosystem.toml)artifact;后续的 shard 作业与 report 生成作业则产出各 diagnostics shard 与 full-report。ty-ecosystem-report.yaml 中的全量报告同样以 name: full-report 上传 dist/ 目录——这也是文档中“验证 full-report 由 report 生成作业创建”所针对的产物。正因为 artifact 名称是可变的(rerun 可复用同名),而 collect_ty_ecosystem_run_metadata.py 这类脚本又能独立地从作业日志中恢复出 merge base、PR revision、EXCLUDE_NEWER 等地面真值,才有了文档中“按不可变 ID 取件 + 用 manifest 交叉验证”的完整闭环。
小结:一条可审计的证据链
把 evidence-acquisition.md 的规程串起来,得到一条每一步都可独立核验的证据链:
- 以用户显式提供的报告为锚点,唯一确定 PR、Actions run 与 attempt,不确定时如实上报而非猜测;
- 用唯一
mktemp快照目录保存评论 JSON、指定 attempt 的运行 JSON 与全量 artifact 元数据; - 以有效作业图(而非时间戳)验证
full-report与各 shard 的作业归属,锁定不可变 artifact ID; - 仅按 ID 下载,随后对 HTML 做逐字节比对,并用
collect_ty_ecosystem_run_metadata.py --attempt生成唯一一份不可变 manifest,确认 base/PR revision 一致; - 以该 attempt 的
full-report/diff.json为权威结构化清单,diff.html为可读对照;JSON 缺失或被替换时降级到冻结的 HTML 并披露局限。
这套做法的核心思想是:生态分析汇总的结论必须能够追溯到某一次运行、某一个 attempt、某一份按 ID 固定的产物,而不是“PR 上现在显示的东西”——这正是历史 PR 行为变化调查、复现与最小化工作能够并行且互不干扰的前提。
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 StartedRust0624
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