
agent-roast 推出工具检测 AI 编程代理削弱测试以通过 CI
来源标题:Show HN: Agent-roast – Catches coding agents that weaken tests to get CI green
agent-roast 能识别编程代理通过削弱测试而非修复代码来让构建通过的行为,比如放宽断言、跳过或删除测试、降低覆盖率阈值、允许 CI 步骤失败。该工具以确定性离线方式运行,不依赖模型或网络,可作为 agent hook、Git hook 和 PR 检查使用,并支持 Claude Code、Codex 和 Cursor。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
你的 AI agent 让测试通过了。 它修的是代码,还是测试?
agent-roast 能抓住那些靠削弱检查、而非修代码来把红构建变绿的改动:放宽的断言、被跳过或删除的测试、降低的覆盖率阈值、被允许失败的 CI 步骤。linter 看不出这些,因为每一行新代码单看都没问题。agent-roast 以 agent hook、Git hook 和 pull request 检查的形式,对比每处改动的前后状态。它是确定性的、离线的:不用模型、不消耗 token、不联网。
以下是 npx agent-roast demo 的真实输出,有删节。场景是脚本预设的,审查不是。
npx agent-roast demo # 十秒钟:在临时仓库里跑一个脚本化示例,不会读取你的任何东西
npx agent-roast # 审查你尚未提交的内容(工作区干净时:重放你最近的提交)
阻止你的编码 agent 这么做
npx agent-roast hook install # Claude Code、Codex 和 Cursor
Claude Code 还有插件可用:/plugin install agent-roast --marketplace Nari-Ab/agent-roast。任何支持 MCP 的 agent 也可以把 agent-roast 当作只读工具调用:agent-roast mcp(配置见 setup)。
当 agent 即将结束时,agent-roast 会审查它改过的一切。如果某项检查被削弱了,agent 会被退回一次,附带文件、行号以及前后对比,并要求它去修代码而不是改测试。如果它再次尝试,你会收到一条警告,而不是陷入死循环。agent-roast 检查不了的情况,会让 agent 正常结束:它绝不会中断会话。有意的改动,提交即视为接受。还不确定?npx agent-roast hook install --advisory 只警告你,从不退回 agent。各 agent 的配置方法、它不做什么、以及如何暂停它:见 docs/integrations.md。
agent hook 遵循各 agent 文档化的 hook 协议,并用模拟的 hook 输入做过测试,但尚未在所有 agent 上进行长时间的实盘会话测试。欢迎反馈你实际遇到的情况。
# .github/workflows/agent-roast.yml
name: Agent Roast
on: pull_request
permissions: { contents: read, pull-requests: write }
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: Nari-Ab/agent-roast@v1
with: { comment: true } # 只留一条评论,每次 push 更新;加上 `advisory: true` 可先试用而不让 PR 失败
另外:在任何 CI 中运行 npx agent-roast review --base origin/main;用 npx agent-roast init --hook 或 pre-commit 框架在下次提交时生效;以及用于代码扫描的 SARIF 输出。
策略从 base 分支读取,因此 pull request 无法关掉审查它自己的规则。在决定之前,npx agent-roast replay 会展示它对最近 50 次提交会怎么说。
| 规则 | 触发条件 | 默认 |
|---|---|---|
| assertion-weakened | 一个精确检查被换成更弱的检查:toBe(42) → toBeDefined()、toBe(2) → toBeLessThanOrEqual(2)、toEqual(x) → toMatchObject(x)、assert x == 1 → assert x |
阻断 |
| guard-weakened | 覆盖率阈值被调低、strict 被关闭、lint 规则被降级、CI 步骤被允许失败、工作流不再运行测试、conftest.py 钩子把报告设为通过 | 阻断 |
| test-skip | 原本就存在的测试被关掉或标记为预期失败:it.skip、@pytest.mark.skip、@pytest.mark.xfail、it.fails、test.fixme、t.Skip、#[ignore]、@Disabled、[Ignore]、xit……(新增的测试被跳过则不上报) |
阻断 |
| test-focus, trivial-assert | .only / fit;expect(true).toBe(true) 及十种语言中的同类写法;对自身 mock 的函数进行断言的测试 |
阻断 |
| assertion-removed, test-removed, expectation-rewritten | 测试丢失断言、消失,或期望值在改动生产代码的同时被修改 | 警告 |
| swallowed-error, type-escape, stub-abandonment | catch {} / except: pass、as any / # type: ignore / # pragma: no cover / istanbul ignore、throw new Error("Not implemented") |
警告 |
重命名、移动的测试、参数化、移动的断言和重新格式化都会被识别,不会上报;依赖环境的跳过同样不会上报(if testing.Short() { t.Skip() }、test.skip(isMobile, ...))。JavaScript/TypeScript、Python 和 Go 获得完整的前后对比;跳过和占位断言还覆盖 Rust、Java、Kotlin、C#、Ruby、PHP 和 Swift;CI 和配置文件无论哪种语言都会检查。每条规则的示例与限制:见 docs/rules.md。
规则、严重级别和路径配置在仓库根目录的 .agentroastrc.json 中(npx agent-roast init 会写入一个起始文件):
"failOn": ["assertion-weakened", "guard-weakened", "test-skip", "test-focus", "trivial-assert"],
"rules": { "type-escape": "off" },
"ignorePaths": ["**/generated/**"]
某处改动无法通过编辑这个文件来放宽对它自己的审查,因为该文件是从 HEAD 或 base 分支读取的。要接受某条有意的发现,请添加一个会过期的例外,而不是关掉整条规则;要接入既有项目而不被历史债务阻塞,请使用 baseline:见 docs/reference.md。
以上经过实测,分母和注意事项见 docs/benchmarks.md。被削弱的检查很少见,大多是刻意为之,也并非 AI 特有。agent-roast 是一根绊线,用来对这些改动提出疑问。在它未见过的一批仓库上,其阻断中有四分之一到三分之二属于「问了但答案是误报」的情况,具体比例取决于样本。
TDD Guard 和 Probity 通过 agent hook 强制一套流程(测试先行);aislop 在 11 种语言中扫描广义的「AI slop」;hallucinot、fakegreen 和 SlopGate 在 diff 中查找同样的取巧手法;linter 和覆盖率门禁只看到当前代码,看不出某项检查被削弱了。agent-roast 聚焦于一个问题,读取完整的前后对比,并说明它是如何测量的。这些工具大多是互补的。详见 docs/comparison.md。
离线。不发网络请求、不调用模型、不做遥测;有测试会检查打包产物中是否包含网络模块。
只读,从不执行。它读取 Git 对象(或在 --worktree 模式下读取仓库内的普通文件),从不执行任何东西。
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。