目标
新增"测试篡改检测"门:PR 中删除测试文件、断言数量净下降、新增 skip/xfail/only/t.Skip、只改期望值不改实现 → gate 直接红(或强制升级为需 ADR)。#81 认为这是自动合并的第一必需门。
背景(源自 #81 §4.1)
无人 review 时,agent 最经济的通过策略是改判据而不是改代码。测试篡改是 agent 最经典的作弊路径:把红测试删掉、把断言改松、加 skip、把期望值改成现状值。现有 gate(hygiene/adr-required/gitleaks/zizmor)几乎不设防这一类。
涉及文件
CI-Workflows/.github/workflows/check.yml(新增 test-integrity job,作为 reusable workflow 供各仓复用,钉 hash 调用)
- 或新增
CI-Workflows/scripts/test-integrity.sh(检测逻辑主体,语言无关部分)
governance/policy/testing.yaml(检测规则与阈值声明)
- 各业务仓 caller workflow(把新 job 纳入 gate 的 needs)
执行步骤
- 实现检测脚本,输入为 PR diff(base...head),规则:
- 测试文件(路径含
test/、tests/、*_test.go、*.test.ts 等按 policy/testing.yaml 声明的模式)被删除或净减少 → 红;
- 测试文件内断言计数(
assert、expect(、require 等按语言声明的模式)净下降 → 红;
- 新增
skip/xfail/only/t.Skip/pytest.mark.skip 等抑制标记 → 红;
- 测试文件被修改但对应源码文件未修改,且修改内容为断言期望值改写 → 红或要求 ADR(由 policy 声明严格度)。
- 规则命中时的逃生门:PR 引用 scope 覆盖该路径的 ADR(复用 adr-required 机制)可放行,但计数入账。
- 接入各仓 gate 的 needs 链(aggregator 严格断言,P1-3 模式)。
- 语言无关的通用规则先进;语言特定解析(AST 级)作为后续增强,不在本卡范围。
验收标准
- 四类篡改注入 PR 全部变红;正常测试演进 PR 全绿。
- 检测逻辑在 CI-Workflows 中心仓维护,各仓钉 hash 复用(与 hygiene 同款模式)。
- 逃生门(ADR)路径可用且入账。
测试方法(预先指定)
在测试仓 Use-up-Plan(或其测试栈对应的仓)准备一组 fixture PR,逐一验证:
T1 删除测试文件(负向):PR 删除一个现有测试文件 → 断言 gate 红,错误信息指明被删文件。
T2 断言净下降(负向):PR 把某测试文件 5 个断言改为 3 个(其余不动)→ 断言 gate 红。
T3 新增抑制标记(负向):PR 给现有测试加 @pytest.mark.skip(或对应语言的 skip)→ 断言 gate 红。
T4 期望值改写(负向):PR 只改测试期望值不改实现 → 断言 gate 红或要求 ADR(按 policy 声明的严格度断言对应行为)。
T5 正常演进(正向):PR 新增源码 + 新增测试(断言数上升)→ 断言 gate 绿。
T6 纯重构(正向):PR 只改非测试源码、测试不动 → 断言 gate 绿。
T7 逃生门(正向):复刻 T2 但 PR 引用一个 scope 覆盖该测试路径的 ADR → 断言 gate 绿且计数被记录(日志可见)。
T8 计数正确性(单元级):对检测脚本喂构造的 diff fixture(本地 git diff 输出文件),断言:断言计数、skip 计数、文件删除判定三项输出与人工预标注完全一致。fixture 至少 8 个 case,覆盖四类篡改 + 四类正常。
依赖
- P1-3(aggregator 严格断言模式已确立,新门按同款接入)
- P3-2 之前可先用"引用 ADR"形式校验做逃生门;ADR scope 实质校验落地后自动升级(不阻塞本卡)
目标
新增"测试篡改检测"门:PR 中删除测试文件、断言数量净下降、新增 skip/xfail/only/t.Skip、只改期望值不改实现 → gate 直接红(或强制升级为需 ADR)。#81 认为这是自动合并的第一必需门。
背景(源自 #81 §4.1)
无人 review 时,agent 最经济的通过策略是改判据而不是改代码。测试篡改是 agent 最经典的作弊路径:把红测试删掉、把断言改松、加 skip、把期望值改成现状值。现有 gate(hygiene/adr-required/gitleaks/zizmor)几乎不设防这一类。
涉及文件
CI-Workflows/.github/workflows/check.yml(新增 test-integrity job,作为 reusable workflow 供各仓复用,钉 hash 调用)CI-Workflows/scripts/test-integrity.sh(检测逻辑主体,语言无关部分)governance/policy/testing.yaml(检测规则与阈值声明)执行步骤
test/、tests/、*_test.go、*.test.ts等按 policy/testing.yaml 声明的模式)被删除或净减少 → 红;assert、expect(、require等按语言声明的模式)净下降 → 红;skip/xfail/only/t.Skip/pytest.mark.skip等抑制标记 → 红;验收标准
测试方法(预先指定)
在测试仓
Use-up-Plan(或其测试栈对应的仓)准备一组 fixture PR,逐一验证:T1 删除测试文件(负向):PR 删除一个现有测试文件 → 断言 gate 红,错误信息指明被删文件。
T2 断言净下降(负向):PR 把某测试文件 5 个断言改为 3 个(其余不动)→ 断言 gate 红。
T3 新增抑制标记(负向):PR 给现有测试加
@pytest.mark.skip(或对应语言的 skip)→ 断言 gate 红。T4 期望值改写(负向):PR 只改测试期望值不改实现 → 断言 gate 红或要求 ADR(按 policy 声明的严格度断言对应行为)。
T5 正常演进(正向):PR 新增源码 + 新增测试(断言数上升)→ 断言 gate 绿。
T6 纯重构(正向):PR 只改非测试源码、测试不动 → 断言 gate 绿。
T7 逃生门(正向):复刻 T2 但 PR 引用一个 scope 覆盖该测试路径的 ADR → 断言 gate 绿且计数被记录(日志可见)。
T8 计数正确性(单元级):对检测脚本喂构造的 diff fixture(本地
git diff输出文件),断言:断言计数、skip 计数、文件删除判定三项输出与人工预标注完全一致。fixture 至少 8 个 case,覆盖四类篡改 + 四类正常。依赖