dsh-protect-tests:在应用前拦截削弱测试的智能体编辑

前言

在 DeepSeek Harness(DSH)里让智能体「把测试跑绿」,有时它会用你不希望的方式达成目标:给失败用例加 .skip、注释掉断言、把 expect(total).toBe(42) 改成 expect(total).toBeDefined()。测试报告变绿,缺陷仍在。人工每次 diff 都盯一遍成本高,单靠提示词也不稳定。

dsh-protect-tests 是社区维护的 DSH 插件(目录分类为 admin-security),在每次对测试文件的编辑落地前对比新旧文本;若变更让测试「检查得更少」,则拒绝该编辑并向模型说明原因。下面介绍它的定位、能力与安装配置方式。

这是什么

  • 名称:dsh-protect-tests(GitHub:acus9345/dsh-protect-tests)
  • 维护者:acus9345
  • 许可证:MIT(据仓库 README)
  • 一句话:DeepSeek Harness 插件,拦截削弱测试的 agent 编辑,例如 .skip、删除或稀释断言。

插件以 DSH 的 Cordis 补丁形式挂载,安装后对匹配规则的测试文件编辑生效;正常增删用例、加断言、重命名、整理 setup 等不受影响。

核心功能

拦截时机与反馈

编辑测试文件时,插件在变更应用前比较旧文与新文。若判定为削弱测试,会拒绝并返回说明,例如:

dsh-protect-tests blocked this change to src/sum.test.ts because it makes the tests check less:
- replaces specific assertions with existence/truthiness checks (0 → 1): `expect(sum(1, 2)).toBeDefined()`
Tests should keep failing until the code is fixed. Fix the implementation instead, or make
the test more accurate without weakening it. If the test itself is wrong (the requirement
changed, or it tested a bug), tell the user which test you are changing and why, then
repeat the identical call once to confirm.

模型应改实现而非弱化测试;若测试本身有误,需向用户说明要改哪条测试及原因,再重复一次相同调用以确认,第二次可通过(allowIdenticalRetry 默认为 true)。

能识别的模式(据 README)

  1. 新增 skip:it.skip、test.only、xit、xdescribe、@pytest.mark.skip / xfail、@unittest.skip、t.Skip()、@Disabled、#[ignore],以及 xUnit、RSpec、PHPUnit、XCTest 等等价写法。
  2. 测试或断言变少:删除用例,或单次编辑中删除不少于阈值的断言(默认 minAssertionsRemoved: 2、minTestsRemoved: 1,可配置)。
  3. 无法失败的断言:如 expect(true).toBe(true)、assert True、assertEquals(x, x)、assert!(true)。
  4. 注释掉的断言。
  5. 吞掉失败:空 catch {} 或 except AssertionError: pass 包住断言。
  6. 弱化断言:具体断言换成 toBeDefined()、toBeTruthy()、assert.ok(x)、is not None 等。
  7. 删除测试文件:通过 shell 对测试文件或目录执行 rm、git rm、unlink;Windows 上 PowerShell 的 Remove-Item、del、rd 亦在监视范围内(guardDeletes)。
  8. 无法失败的 test 脚本:修改 package.json 的 "test" 为 echo、exit 0、... || true,或加入 --passWithNoTests(guardTestScript)。

测试文件如何被识别

默认按文件名与路径匹配,例如 *.test.*、*.spec.*、*_test.*、test_*.py、*Test.java、conftest.py,以及 tests/、__tests__/、spec/、e2e/ 等目录下的文件。列表可通过 testFiles 覆盖,支持排除(如 !tests/fixtures/**)。

README 说明:合并多个近似用例为 test.each / @pytest.mark.parametrize / t.Run,或将多个 toBe 合并为 toEqual({...}) 等 refactor,虽在纸面上像「变少」,一般会放行。

刻意不覆盖的情况

插件基于模式而非语义。把 toBe(4) 改成 toBe(5) 以迎合错误实现、或重写测试逻辑使其不再触及缺陷,通常不会被拦。它主要针对常见偷懒手段,不能替代人工阅读 diff。

安装与启用

DSH 按 profile 维护插件列表;web 对应 dsh web 的浏览器 UI,headless 用于脚本与 CI,acp 用于编辑器集成。你实际使用的每个 profile 都要单独安装。

在 web profile 下安装:

dsh plugin --profile web add dsh-protect-tests

安装后即生效,无需额外开关。卸载:

dsh plugin --profile web remove dsh-protect-tests

若你通过 headless 或 acp 运行 DSH,将 --profile web 换成 headless 或 acp 即可。

社区插件目录页:https://www.skillhub.cn/plugins/acus9345/dsh-protect-tests(独立站点,与 DeepSeek / 幻方无官方从属关系)。仓库:https://github.com/acus9345/dsh-protect-tests。

配置

可在 DSH 侧栏 Plugins 中点选 dsh-protect-tests,修改后 Save,下一次工具调用即生效,无需重启。

或在 ~/.dsh/profiles/<profile>/cordis.patch.yml(仅当前 profile)或 ~/.dsh/cordis.patch.yml(全部 profile)中增加片段;未写的键保持默认:

- id: protect-tests
  config:
    mode: deny                  # 或 ask:弹出审批
    testFiles: ['*.test.*', '**/tests/**', '!tests/fixtures/**']
    minAssertionsRemoved: 2
    minTestsRemoved: 1
    disabledSignals: []         # skip, fewer-assertions, fewer-tests, trivial-assert,
                                # commented-assert, swallowed-failure, weaker-assert
    guardDeletes: true
    guardTestScript: true
    allowIdenticalRetry: true   # false 时拒绝为最终决定
    shellTools: [bash, pwsh]   # Windows 上 DSH 使用 pwsh

mode: ask 时,削弱类变更可走审批而非一律拒绝。

适用场景与注意

适合:在 DSH 中让智能体修代码、跑测试的团队或个人,希望默认阻止「skip / 删断言 / 换弱断言」类 reward hacking,同时保留在说明理由后二次确认改测试的路径。

权限与安全:插件随当前 DSH 进程权限运行,可拦截编辑并监视配置中的 shell 工具对测试路径的删除。安装前请阅读仓库源码与 MIT 许可证,确认符合你的安全策略;勿在未审阅的情况下在敏感仓库盲目添加第三方插件。

局限:无法防止语义层面的测试退化;关键变更仍应人工 review。

结尾

dsh-protect-tests 在测试文件编辑链路上加了一层模式级护栏:测试应持续失败直到实现修好,而不是被悄悄改绿。需要时可在目录页查看条目,或在 GitHub 提 issue。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜