fix(ci)+test(packaging): 关掉「门禁关死而自检全绿」三条路:outputs 绑定、判据步真跑、自检步指向

r2 复审:r1 那 11 条 + M3 已闭合(复审自己重放 12/12 红),但它新造的 40 条变异仍有 11 条漏网,
其中三条是「1 行改绿」:把 `probe.outputs.available` 改指向 `reason`/字面量(门永久关、每次绿);
判据步的退出码在标记表之外被吞(`|| echo 先忽略` / `; :` / `| cat` / `if !` / `true # 原行`,
复审真 bash 实测 exit 0);自检步被指向另一个脚本(自检根本不跑)。

三条 P1:
- 新判据 9:`probe.outputs.{available,log,png}` 必须逐个等于 `${{ steps.detect.outputs.<同名> }}`;
- 新判据 5:把 `id: judge` 的 `run` 原文真跑两次 —— 正例必须 exit 0、CPU 档日志必须 exit≠0,
  且 `bash -e` 与**裸 bash** 各跑一遍(`; :` 只在后者现形,实测 `bash -e`=1 / 裸 bash=0)。
  这一条一次封掉整类「吞码」,不依赖标记表;
- 判据 2:`id: wiring` 的 `run` 必须含 `gpu_shot_ci_selftest.py`,且 `vectors`/`wiring` 必须是**单条命令**
  (不许换行/管道/`;`/`&`/反引号/`#`/`$(`)⇒ `|| echo 先忽略` 无从下手。

两条 P2:
- 新判据 10:`on:` 必须同时有 `push`(含 `main`)/`pull_request`/`workflow_call`;
- 「载体依赖」这条边界补进四处(自检 docstring / gpu-evidence.md §8 / README / workflow 头注释):
  整个 workflow 被删掉就没有东西会跑自检,`guards.yml` 399/400 行塞不进去。

止步线(写进 docstring 与 §8):封闭清单 10 类 / 33 条变异向量;明确不覆盖「改判据脚本」「删掉整个
workflow」「求值器与 GitHub 语义差」「判据 4 的合法调用替换」;本文档不宣称「不可能被改成总是绿」;
**自 2026-11-11 起停止加固,扩项须由人类新开 mission**。

新增 `packaging/tests/gpu_shot_ci_expr.py`(`if:` 小求值器,判据脚本仍守 ≤400 行:377/215/64)。
This commit is contained in:
AskaEth committed 2026-11-11 18:31:30 +08:00
1 parent 2e9702790d
commit 8722c5a0ab
7 files changed
+358 -139

No files matched your search

+4 -3
View File
@@ -70,9 +70,10 @@ Git Bash 下会静默落进 Docker VM(脚本内部用 `cygpath -m` 归一)
| `packaging/check_screenshot.py` | 截图非空白判定(CI 门禁 + 本地手动跑,零第三方依赖) | ≤400 |
| `packaging/check_gpu_shot.py` | GPU 截图证据判定(M249):`[shot] … renderer=wgpu` + `Selected: AdapterInfo` 的 `device_type ∉ {Cpu, Other}` + PNG 非空/尺寸对账/非空白,逐条点名;`--expect-refuse` 判拒绝降级出图;`--self-test` 跑 `tests/gpu_shot_vectors.json` 的 19 条向量(脚手架 `tests/gpu_shot_selftest.py`)。CI 接线见下一行(M253),口径与能证/不能证见 `docs/gpu-evidence.md` | ≤400 |
| `.github/workflows/gpu-evidence.yml` | GPU 证据门禁接线(M253):`selftest` **无条件**跑 `--self-test` + 三个退出码 0/1/2;`probe` 探测可用产物与硬件适配器;`evidence` 只在 `needs.probe.outputs.available == 'true'` 时跑真判据 —— **无 GPU / 无产物时它整块显示 skipped(不是绿色 ✓)**,原因写进 job summary 并打 `::warning::`(跳过 ≠ 通过)。新开文件而不是并进 `guards.yml`:那份已 399/400 行,`release-build.yml` 正好 400。**dev 线复用**(r1 P2-1):`dev-guards.yml` 里 `uses:` 同一份,dev 上只落 `selftest` 那一半 | ≤400 |
| `packaging/tests/gpu_shot_ci_selftest.py` | 上一条的**接线自检**(M253,r1 返工):6 条判据 —— ① 五步按 `id:` 必须在场且无条件(`vectors`/`exitcodes`/`wiring`/`detect`/`judge`,`exitcodes` 还得真断言 0/1/2);② **解析 `if:` 表达式求值**判「判定挂门」(`available` 取 `'true'`/`'false'`/`''` 必须真/假/假,认不出即红);③ 两种调用形态都在场且产物来自 `needs.probe.outputs.*`;④ 跳过路径**实跑 7 种输入**(`tests/gpu_shot_ci_probe.py`);⑤ `continue-on-error` 按 YAML 布尔语义判;⑥ 扫所有 `python`+`packaging/` 的 `run` 不许吞退出码。外加 **21 条变异向量**(`tests/gpu_shot_ci_vectors.json`,复审那 19 条 + 2 条)逐条必须当场红 | ≤400 |
| `packaging/tests/gpu_shot_ci_probe.py` | 上面判据 ④ 的**执行脚手架**:把 `probe` 步的 `run:` 原文落盘、用桩 env 起 bash,断言 `$GITHUB_OUTPUT` 的 `available` / job summary 文案 / `::warning::`,以及三条「点了名却找不到 ⇒ 必须红」的红线;找不到能跑的 bash 即判红(不降级) | ≤400 |
| `packaging/tests/gpu_shot_ci_vectors.json` | 接线自检的变异向量:21 条,按**解析后的结构**注入(`step_delete`/`job_delete`/`step_set`/`job_set`/`run_append`/`run_replace`),每条写明应红的判据名 | ≤400 |
| `packaging/tests/gpu_shot_ci_selftest.py` | 上一条的**接线自检**(M253,r1+r2 返工):10 条判据 —— ① 五步按 `id:` 必须在场且无条件(`vectors`/`exitcodes`/`wiring`/`detect`/`judge`,`exitcodes` 还得真断言 0/1/2);② 自测步必须**指向本脚本且是单条命令**(`|| echo 先忽略` 无从下手);③ **解析 `if:` 表达式求值**判「判定挂门」(`available` 取 `'true'`/`'false'`/`''` 必须真/假/假,认不出即红);④ 两种调用形态都在场且产物来自 `needs.probe.outputs.*`;⑤ **判据步真跑两次**(正例必须 0、CPU 档必须非 0,`bash -e` 与裸 bash 两种模式);⑥ 跳过路径**实跑 7 种输入**;⑦ `continue-on-error` 按 YAML 布尔语义判;⑧ 吞码标记表(兜底);⑨ `probe.outputs.*` 必须逐个绑到 `steps.detect.outputs.*`;⑩ `on:` 必须同时有 `push`(含 `main`)/`pull_request`/`workflow_call`。外加 **33 条变异向量**(`tests/gpu_shot_ci_vectors.json`:r1 复审表 20 条 + 1 条补充 + r2 复审 12 条)逐条必须当场红。**不覆盖**:不自守、载体依赖:整个 `gpu-evidence.yml` 被删掉/替换后,**没有任何东西会跑这个自检** —— 守门人住在被守对象自己的文件里(`guards.yml` 已经 399/400 行,塞不进去)。求值器与 GitHub 语义的差异、判据 4 的「合法调用替换」。**自 2026-11-11 起停止加固**,扩项须由人类新开 mission | ≤400 |
| `packaging/tests/gpu_shot_ci_expr.py` | 上面判据 ③ 的**小求值器**:给 `needs.probe.outputs.available` 取 `'true'`/`'false'`/`''`,门必须分别判真/假/假 —— 门还挂不挂由**语义**决定,不由子串决定。只认 `==`/`!=`/`&&`/`||`/括号/字符串/`needs.<job>.outputs.<名>`/`true|false|null`/四个无参函数;`!` 与其余写法一律抛错(调用方按红处理)。不是 GitHub 语义的替身,是它的保守子集 | ≤400 |
| `packaging/tests/gpu_shot_ci_probe.py` | 上面判据 ⑤⑥ 的**执行脚手架**:把 `detect` / `judge` 两步的 `run:` 原文落盘、用桩 env 起 bash。`detect` 跑 7 种输入,断言 `$GITHUB_OUTPUT` 的 `available` / summary 文案 / `::warning::` / 三条「点了名却找不到 ⇒ 必须红」;`judge` 跑两次(正例必须 0、CPU 档必须非 0,两种 bash 模式),一次封掉 `\|\| echo` / `; :` / 管道 / `if !` / `true # 原行` 整类「绿而没判」。找不到能跑的 bash 即判红(不降级) | ≤400 |
| `packaging/tests/gpu_shot_ci_vectors.json` | 接线自检的变异向量:**33 条**,按**解析后的结构**注入(`step_delete`/`job_delete`/`step_set`/`job_set`/`path_set`/`trigger_set`/`run_append`/`run_replace`,`also` 可叠加),每条写明应红的判据名;来源见文件里的 `_note` | ≤400 |
| `packaging/tests/gpu_shot_vectors.json` | `check_gpu_shot.py --self-test` 的向量:19 条(红 16 / 绿 3),每条给出**期望的失败判据清单**,数据与代码分开 | ≤400 |
| `packaging/tests/gpu_shot_selftest.py` | 上面那 19 条向量的执行器(按真实行格式手拼日志 + 用 `check_screenshot.encode_png` 现造 PNG):判据脚本自己守 ≤400 行,脚手架放这里 | ≤400 |
| `packaging/check_publish_slots.py` | 发布名单守卫(M164/M184):名单 ⇄ 产物两个方向 + `mode: ci` 槽位无产物的警告;`--self-test` 注入 11 条回归 | ≤400 |