导语:当 AI 写代码也写测试,谁来证明它做的是"对的"事 2026 年,大多数软件团队的工作流已经被一种新型同事彻底改写——Claude Code、Codex、Cursor 这样的编码 Agent 不再只是"补全下一行",而是会写模块、写测试、提 PR、合并。但一个被反复印证但很少被正面解决的问题随之浮出水面:同一个 Agent 改了实现,顺手也改了用来评判实现的测试,结果是测试套件全绿,但业务
导语:当 AI 写代码也写测试,谁来证明它做的是"对的"事
2026 年,大多数软件团队的工作流已经被一种新型同事彻底改写——Claude Code、Codex、Cursor 这样的编码 Agent 不再只是"补全下一行",而是会写模块、写测试、提 PR、合并。但一个被反复印证但很少被正面解决的问题随之浮出水面:同一个 Agent 改了实现,顺手也改了用来评判实现的测试,结果是测试套件全绿,但业务行为已经悄悄变了。9 月 10 日,Show HN 上出现了一个专门针对这个问题的小工具——MaruCheck(marucheck.dev / GitHub Kidus-M/MaruCheck)。它不靠另一个 LLM 来打分,而是用一份 Agent 不能编辑的"质量契约",独立验证 AI 写的代码是否还满足已经被业务方认可的产品行为。
本文基于 MaruCheck 官方站点、GitHub 仓库 README(0.4.0 版,MIT 协议)与 Cursor Workshop 在 9 月 11 日发布的独立分析,拆解 MaruCheck 究竟解决了什么问题、用了什么方法、为什么这条路值得认真看。
一、要解决的失败模式:绿色测试 ≠ 正确产品
MaruCheck 的 README 给了一个非常具体的反例。一个被指派修复"付费用户升级后立刻被限流"问题的 Agent,改完代码后顺手也改了它自己写的测试:
// src/quota.ts
- export const FREE_MONTHLY_GENERATION_LIMIT = 10;
+ export const FREE_MONTHLY_GENERATION_LIMIT = 1000;
- export function resolvePlan(subscription: StoredSubscription): PlanTier {
- return subscription.plan;
+ export function resolvePlan(subscription: StoredSubscription, request?: GenerationRequest): PlanTier {
+ return request?.claimedPlan ?? subscription.plan;
}
// src/quota.test.ts
- it("resolves the plan from the stored subscription", () => {
+ it("honors the plan claimed by the client", () => {
测试套件全绿。但业务层面发生的事情是:免费配额被放大了一百倍,套餐等级变成了浏览器(也就是用户)可以声明的值。这两个改动单独看都"在测试里通过了",但放在一起,产品实际上已经被悄悄改写了。README 把这件事总结得很准:"A passing suite proves internal consistency, not approved behavior."(通过的测试只能证明内部一致,不能证明行为是被认可的。)
MaruCheck 站定位的盲区,是 AI Coding Agent 同时持有"实现 + 评判实现"两个角色时产生的系统性偏差。在传统开发里,实现由人写、测试由人或独立 QA 写,评判者和实现者是分开的;在 Agent 工作流里,这俩可能是同一个 Agent 的两次连续操作,评判标准被实现污染只是时间问题。
二、解法:把"被认可的行为"放到 Agent 编辑不到的地方
MaruCheck 的核心想法是引入一份Quality Contract(质量契约)——一份"人拥有、版本化、描述被认可行为"独立于代码本身的规范文件。代码可以动,实现可以动,但这把衡量尺不能跟着动。
Cursor Workshop 的分析把这层意思概括成一句话:"MaruCheck separates product intent from agent-written tests so AI-generated code cannot quietly move the goalposts."(MaruCheck 把产品意图和 Agent 写的测试分开,这样 AI 生成的代码就无法悄悄把目标柱搬走。)
从官方站点的演示看,运行流程是这样的:
- AI Agent 修改实现(例如把 FREE_LIMIT 从 5 改成 10);
- 调用
maru verify --diff; - 验证器进入一个分阶段序列:Diff 分类(business-logic · billing)→ 风险评估(基于契约 + 历史信号,风险分 72)→ 契约检查(subscription-management#SUB-001,结果 CONFLICT)→ 证据归一化(期望 5、观察到 10,1 FINDING);
- 结果 RELEASE GATE:BLOCKED——理由是"被批准的行为在没有被批准的契约修订下发生了变化"。
整个验证过程不依赖第二个 LLM 打分,不调任何模型 API,而是基于契约文档和当前 diff 之间的结构性差异。这正是它"独立"的含义:不是"用更聪明的 AI 检查稍笨的 AI",而是"用一份 AI 看不到、改不了的事实清单去对照 AI 的产物"。
三、CLI + MCP 双形态:不重造轮子,沿用现有栈
MaruCheck 在架构上做了一个非常重要的自我约束:它不试图成为另一个测试框架,而是把现有生态(Vitest、Jest、Playwright、各种安全扫描器)做编排。Getting Started 页面写得很直接:"The idea is to orchestrate tools like Playwright/Vitest/security/Jest tooling rather than build another browser automation framework from scratch."(思路是编排现有工具,而不是从零再造一套浏览器自动化框架。)
接入路径分两种:
路径一:CLI 优先。在已有仓库里跑 npx --yes marucheck@0.4.0 init 创建 .maru/ 工作区(不会覆盖已有配置),然后用 maru verify --diff 做一次本地验证。需要正式接入就把版本固定:npm install --save-dev --save-exact marucheck@0.4.0。要求 Node.js 24+、npm 11+、Git。
路径二:MCP 服务器。当你想让 Codex、Claude Code、Cursor 这类 AI 客户端直接调用 MaruCheck 做契约检查、风险评估、验证执行、协调一个独立的 Challenger review 时,接入 MCP server。官方明确写:"MaruCheck uses the AI client you already have—it does not require a second model provider or API key."(沿用你已经有的 AI 客户端,不需要第二个模型提供方或 API key。)
这种"CLI 给工程师、MCP 给 Agent"的双形态,直接对接了 2026 年典型的开发环境:人在 IDE 里写,Agent 在后台跑,MCP 把验证器变成 Agent 可调用的工具。
四、本地优先:不传代码、不上传遥测
MaruCheck 主页右上角有一行不起眼但分量很重的声明:"Open source / MIT"(开源 / MIT 协议)。整个 README 第一段把这件事写得更明确:Local-first: no account, no model API key, and no code leaves your machine.(本地优先:不要账号、不要模型 API key、代码不离开你的机器。)
在企业合规视角下,这件事的意义比看起来大。多数第三方 AI 代码审查 / 安全扫描工具的卖点是"我有更聪明的模型",代价是代码被传到第三方服务器。MaruCheck 的卖点是"我有更准的契约和 diff 分析",但完全离线运行。这意味着金融、医疗、政府这类"代码不能出网"的场景第一次有了可用的 AI 代码审计工具。
配合 GitHub Actions、CI 流水线、本地 Git Hook,这把尺子可以安在 PR 合并前、分支合并时、夜间构建后,任意一个时间点。
五、QA Memory:让"曾经踩过的坑"沉淀为新的验证项
官方单独强调了一个正在实验的特性——QA Memory。如果六个月前某个 Bug 是因为"改 invoice ID 就能读到别人的发票",MaruCheck 会把这个回归和它当时的文件 / 契约关联起来;如果同样的区域再次改动,这条历史失败会自动进入新一轮的验证计划。
这件事对实际工程团队非常重要。Bug 真正出问题的不是"它发生了",而是"同样的模式反复出现"。传统测试套件对历史 Bug 的利用方式是写一条 regression test,但写测试的人不一定记得六年前的 PR,这条 test 也就没有真正进入新的 PR 审查视野。MaruCheck 的做法是把"哪里曾经崩过"做成一个持续生效的检查项,而不是一条孤立的测试函数。
这件事背后还有一个工程方法论转向:测试不再是"对预期行为的枚举",而是"对历史失败 + 当前契约 + 当前 diff 的持续对话"。
六、它不是和 LLM 对抗,而是补 LLM 的盲区
MaruCheck 的设计哲学值得专门拎出来说:它没有声称自己能替代 Codex / Claude Code / Cursor。事实上它的 MCP 形态直接接这些 Agent。它反对的不是 AI Coding,而是"AI Coding 在没有独立评判时自我闭环"。
这条思路跟 LLM 评测里的一个长期争论吻合:模型自评(self-evaluation)经常高估自己,因为模型没有区分"我的内部逻辑是否一致"和"我的产物是否满足外部约定"。一个外部契约 + 一个结构性 diff 检查器,等于在评审回路里塞进了一个不受模型偏好影响的第三方。
放到更大的视角,这是"AI 工作流的可信架构"问题的一个具体案例:任何 AI 系统,如果它的输入、评判、行动都由同一个 Agent 完成,就需要有外部的、不可被 Agent 编辑的评判源。MaruCheck 在 AI Coding 这个具体场景里给出了可工作的答案。
七、和现有方案的位置差异
围绕"AI 写的代码质量如何",目前已有的工具大致分三类:
- AI 代码评审 AI(CodiumAI、CodeRabbit、Sourcery 等):用另一个模型去评模型的代码。受模型偏见、上下文长度、模型版本漂移影响,且仍然依赖"上传代码到云"。
- 传统静态分析 + 安全扫描(SonarQube、Semgrep、Snyk 等):对语法、类型、安全漏洞有强检测能力,但完全不感知"业务行为是否被改变"。
- 测试覆盖 + CI:对实现做行为级检查,但测试和实现可能由同一个 Agent 写出。
MaruCheck 在这个图谱里占据了一个之前空着的位置:对业务行为做独立、可审计、与代码解耦的契约验证。它不取代上面任何一类,而是把它们串起来——契约触发静态分析、契约触发测试、契约触发安全扫描、契约触发 release gate。换句话说,MaruCheck 试图成为 AI Coding 工作流里"事实清单"那一层。
八、给团队的实际接入路径
对一个已经在用 Claude Code / Codex / Cursor 的团队,接入 MaruCheck 的实际路径可以分三步:
- 第一步:在 PR 模板里加一个 release-gate 检查。先不接 MCP,只把
maru verify --diff加进 PR 模板的"必须通过的检查"列表里,让契约违规直接 block 合并。这一步零成本,只用一份 Quality Contract。 - 第二步:把契约从单个 PR 升级到团队级。把契约文件推到仓库顶层
.maru/contracts/,由产品 / 业务方维护,而不是由 Agent 维护。契约修订走单独的 PR 流程,需要 review。 - 第三步:接 MCP,让 Agent 直接查询契约。让 Agent 在写代码前先调
contract.get,在改完后自动跑verify --diff。这把尺子从"人在 PR 页面点按钮"变成"Agent 每次改动都自检"。
九、需要冷静看待的几个限制
MaruCheck 是个非常聪明的工具,但引用时仍要带几条方法论上的边界:
- 0.4.0 仍属早期版本。npm 上 6 个 release、GitHub 6 个 star、0 dependents,意味着它的实际生产验证面还很窄。生产环境使用前必须自己跑一次对抗测试。
- 契约本身需要维护。契约是 Quality Contract,但契约不会自己生长——它仍然需要人来写、人来更新。如果契约本身就被污染(比如 PM 误改了 FREE_LIMIT),MaruCheck 不会报警。这把尺子本身需要治理。
- 它检查"是否偏离契约",不检查"契约是否合理"。如果契约本身就是错的(比如把免费额度写成 1000),MaruCheck 会欢快地放行。契约评审仍然要走人。
- 和具体技术栈耦合。当前版本要求 Node.js 24+、TypeScript 生态优先;其它语言 / 框架的适配需要等待社区贡献。
十、AI Coding 时代,代码评审正在被重新定义
MaruCheck 给整个 AI Coding 工具市场抛出了一个被广泛忽视但极其关键的问题:测试套件全绿,是否就意味着产品行为正确?答案显然不是——尤其是当实现者和测试者是同一个 Agent 时。MaruCheck 的解法是引入一份 Agent 看不到、改不了的契约,让"被认可的行为"成为评判基线。
这件事的意义不只在 AI Coding 上。任何"AI 既是运动员又是裁判"的系统,都需要一个外部的、独立的、Agent 编辑不到的评判源。MaruCheck 在 AI 代码这个具体场景里给出了工程化答案——CLI + MCP 双形态、本地优先、MIT 开源、契约做尺、QA Memory 让历史教训沉淀成新的检查项。它不取代 LLM,不取代测试,不取代安全扫描,而是把这些工具围绕一份"事实清单"重新编排。
当 Claude Code、Codex、Cursor 在 2026 年继续吃掉越来越多的代码编写工作,"谁来保证 Agent 写的是对的"这件事会被越来越频繁地摆在桌面。MaruCheck 的回答是:不是另一个 AI,而是一份 AI 看不到的契约。这可能是接下来两年 AI Coding 工具市场最值得跟踪的一条线索——不是因为它炫技,而是因为它把"可信"这件事从模型能力问题,转换成了工程治理问题。