让 AI Agent 交付可信代码:为 Novu 单体仓库设计 verifier 验证工作流
【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu
导读
大型开源单体仓库(monorepo)中,由 AI 编码 Agent 提交代码的最大风险不是"写错",而是"声称写完了但实际跑不通"。Novu 在其 Cursor 协作体系中用一个名为verifier的 Agent(验证者)专门解决这个问题:它扮演"怀疑论者",在任何任务被标记为完成之后接管,通过"确认改动文件 → 运行受影响应用的真实测试 → 静态检查 → OpenAPI 规范校验 → 边缘情况扫描"这一串动作,把"声明完成"变成"证据驱动地验证完成"。本文围绕仓库中的 verifier Agent 定义,逐条拆解这套验证流程,并结合 apps/api/package.json、apps/worker/package.json、apps/dashboard/package.json 与 .cursor/rules/testing.mdc 中的真实脚本定义,说明每条命令背后的执行逻辑与适用边界。读完你既能理解如何复刻一个"验证者"角色的 Prompt 设计,也能掌握 Novu 仓库中单测、E2E 与 OpenAPI 校验的确切用法。
verifier 是什么:一个专职"唱反调"的编码 Agent
在 Cursor 中,Agent 通过 Markdown 文件顶部的 YAML frontmatter 进行声明式注册。verifier 的元数据定义了它的职责边界:
--- name: verifier description: Validates completed work. Use after tasks are marked done to confirm implementations are functional — runs tests, checks types, and verifies the OpenAPI spec where applicable. model: fast ---三个字段的含义分别是:
name:Agent 的唯一标识,供 Cursor 对话中按名调用;description:定义"何时该用我"。注意它把触发时机限定得非常精确——"after tasks are marked done"(任务被标记为完成之后),这与普通编码 Agent 形成职责互补;model: fast:指定该 Agent 默认使用轻量快速模型执行。验证任务大多是机械的 grep、跑命令、核对产物,不需要重模型推理,fast模型能在降低延迟与成本的同时完成任务。
正文第一句直接给出角色内核:You are a skeptical validator. Your job is to verify that work claimed as complete actually works.(你是一个怀疑论验证者,你的工作就是核实那些"声称已完成"的工作是否真的可用)。这是一个刻意设计的对抗性角色定位——与"写代码"的 Agent 不同,verifier 的产出不是新功能,而是"通过与不通过的判定 + 待修复问题清单"。
在 .cursor/agents/impact-checker.md 中可以看到体系化的另一半:impact-checker在"改动前"评估共享代码的爆炸半径(blast radius),而 verifier 在"改动后"做功能验收。两者共同构成"改前评估风险、改后验证结果"的闭环。
六步验证流程拆解
verifier 被调用时按固定步骤执行,下面结合仓库中真实脚本逐一还原每步的执行内容与原理。
第一步:锁定"声称完成"的具体范围
- Identify what was claimed to be completed
验证不能从"你做了什么"开始,而应从"你声称做了什么"开始。verifier 先解析任务结论中的改动声明,明确验证对象(例如"新增了某个 UseCase""修改了某个 DTO""调整了某条路由"),再进入下一步去核对物理证据。这一步的价值在于把"笼统的完工"翻译成"可检验的断言清单",防止 Agent 自述式交付绕过检验。
第二步:确认实现文件真实存在且包含预期改动
- Confirm the implementation files exist and contain the expected changes
验证者须核对文件系统事实:改动涉及的文件是否真的存在、关键符号(类/函数/DTO/枚举)是否真的按声明被添加或修改。在 Novu 仓库中这意味着验证者要能区分几个"看似相似但互不相关"的代码库区域:
apps/api:NestJS 网关,承载 REST API、UseCase 编排与 OpenAPI 生成;apps/worker:后台任务消费者(队列 worker),处理通知投递等异步逻辑;apps/ws:WebSocket 网关;apps/dashboard:Vite + React 管理面板;- 支撑库:
libs/dal(数据访问)、libs/application-generic(业务逻辑)、packages/shared(共享类型/DTO/枚举)。
文件层核对通过后,才能进入"跑测试"环节。
第三步:运行受影响应用的真实测试套件
- Run the relevant test suite for the affected app
- API/worker:
cd apps/api && pnpm testorcd apps/worker && pnpm test- Dashboard:
cd apps/dashboard && pnpm test:e2e(only if dashboard is running)
这是整个验证流程的中枢。命令选择遵循"按受影响应用匹配"原则,而非"全仓一把梭"。
API 单测:cd apps/api && pnpm test在 apps/api/package.json 中展开为:
cross-env TS_NODE_PROJECT=tsconfig.spec.json TS_NODE_TRANSPILE_ONLY=true NODE_ENV=test \ NOVU_ENTERPRISE=true CLERK_ENABLED=true NODE_OPTIONS=--no-experimental-strip-types \ mocha --timeout 15000 --require ts-node/register --exit 'src/**/*.spec.ts'细节值得注意:
- 基于Mocha + ts-node,匹配
src/**/*.spec.ts的单元测试文件(与 .cursor/rules/testing.mdc 中"Mocha for API/worker"的约定一致); - 通过
NOVU_ENTERPRISE=true、CLERK_ENABLED=true把 Enterprise 与 Clerk 能力纳入测试环境(脚本中还声明了对@novu/ee-*等 workspace 包的依赖,见 apps/api/package.json); --timeout 15000与--exit保证长任务与进程都能被妥善收尾;- 还有配套的
pretest钩子会先执行pnpm build:metadata生成元数据,再开始跑测试。
Worker 单测:cd apps/worker && pnpm test对应 apps/worker/package.json 中基于 Mocha 的命令,测试文件 glob 为src/**/**/*.spec.ts,并在环境变量中关闭了strictNullChecks以匹配现有代码风格。若涉及端到端通知链路,.cursor/rules/testing.mdc 还提示 worker 需先启动(pnpm start:worker)。
Dashboard E2E:cd apps/dashboard && pnpm test:e2e实际是playwright test(见 apps/dashboard/package.json),测试目录约定在apps/dashboard/tests/。verifier 特别标注了前置条件——仅当 dashboard 正在运行时执行,因为 Playwright 需要真实页面。仓库中已有可参考的 Playwright 用例,例如 manage-workflows.e2e.ts 与 sync-workflow.e2e.ts,它们配合 page-object-models 与 utils 组织页面对象与公共操作。
第四步:用pnpm check做 lint 与类型卫生
- Run
pnpm checkin the affected app to confirm no lint or type errors
check脚本在三个应用中都指向Biome:
- apps/api/package.json:
"check": "biome check ." - apps/worker/package.json:
"check": "biome check ." - apps/dashboard/package.json:
"check": "biome check ."
Novu 以 biome.json 为根配置(规则集分散在 .cursor/rules 与 biome-plugins 下的.grit规则中),biome check会同时覆盖 lint 与格式两类问题,因此它充当 verifier 的"代码卫生门禁",确保改动不引入 lint 违规或格式化漂移。
第五步:涉及 API 端点改动时校验 OpenAPI 规范
- For API changes that touch endpoints: run
npm run lint:openapi(requires API running)
这一步属于"按需深度验证"。Novu 的 OpenAPI 文档由运行中的 API 服务实时暴露,命令在 apps/api/package.json 中定义为:
spectral lint http://127.0.0.1:${PORT:-3000}/openapi.yaml解读:
- 使用 StoplightSpectral对线上 OpenAPI 文档做规则校验(
@stoplight/spectral-cli在 devDependencies 中声明,见 apps/api/package.json); - 目标来自正在运行的服务(默认
127.0.0.1:3000,可用PORT环境变量覆盖); - 因此前置条件必须是API 正在运行——这解释了为什么它被单独列为一步而不是放进普通测试。
仓库中 OpenAPI 的构建链路佐证了"端点改动会波及规范":build:generate会执行generate:swagger(运行 exportOpenAPIJSON.ts)再执行generate:sdk重新生成内部 SDK(见 apps/api/package.json),产物包括 swagger-spec.json。也就是说,端点签名变更不仅影响服务,还会扩散到 SDK 层,verifier 的这一校验正是为了把"规范漂移"扼杀在验收阶段。
第六步:主动寻找遗漏的边缘情况
- Look for edge cases that may have been missed
测试全绿不代表正确。verifier 被要求在收尾时主动质疑:空数组、null/undefined输入、并发竞争、权限边界、环境差异、超出主流 happy path 的输入分支……这些仓库约定都有迹可循——例如 .cursor/agents/impact-checker.md 要求评估改动对下游消费者的影响,apps/api/migrations 里大量数据迁移脚本的存在也提醒验证者:涉及 MongoDB 模型或数据形态的改动,还要考虑历史数据与迁移影响。
报告输出:只陈述验证过的证据
verifier 的报告格式被刻意压缩为三类事实:
Report: - What was verified and passed # 已验证且通过的内容 - What was claimed but is incomplete or broken # 声称完成但实际缺失/损坏的内容 - Specific issues that need to be addressed # 需要解决的具体问题这种三段式强制验证者把输出限定在"可验证事实"上:
- 通过的项必须能回溯到上文的某条命令(如"
apps/api下pnpm test通过,涉及 3 个 spec 文件"); - 未通过的项必须给出"声明 vs 现实"的落差(如"声称新增了
XxxUsecase,但对应文件不存在"); - 问题清单要具体到可执行——指向具体文件、具体断言或具体命令输出,而不是空泛的"需要改进"。
模板之外还有一条总的心理纪律:Do not accept claims at face value. Test everything you can.(不要轻信任何声明,尽可能测试一切可测的)。这保证 Agent 的输出始终以工具执行结果为准,而非以被验证者的自述为准。
这一设计模式如何落地到其他工程团队
verifier 模式并不绑定 Cursor 或 Novu,它沉淀的是一套可复制的"AI 交付质检"方法论:
- 让"验证者"与"实现者"角色分离。写代码的 Agent 倾向于为自己的产出辩护,专职的 skeptical validator 则被授权唱反调,天然规避了"自己验证自己"的盲区。
- 把验证动作绑定到真实命令而非口头检查。verifier 清单里的每一步都对应仓库里真实存在的 npm/pnpm 脚本(
test、check、test:e2e、lint:openapi),验证结果因此具有可复现性——这正是它与"看图说话式 Code Review"的本质区别。 - 按改动范围决定验证深度。不是每次改动都跑全量:普通 API 改动跑单测 +
check;动端点再加 OpenAPI 校验;动 dashboard 才跑需要起服务的 Playwright E2E。验证成本与风险面匹配,才不会被团队弃用。 - 报告结构化、证据化。固定三段式输出,让开发者扫一眼就知道"哪些已确认、哪些被证伪、下一步修什么"。
对 Novu 仓库而言,这套工作流与仓库自身的工程底座深度咬合:单测约定见 .cursor/rules/testing.mdc,共享代码的爆炸半径评估见 .cursor/agents/impact-checker.md,顶层协作约束见 AGENTS.md(如"改动packages/需重新构建""Enterprise 改动需与社区版解耦"等边界)。verifier 正是把文档化约束翻译成可执行验证步骤的那个角色——它不负责让代码变得正确,而是负责让"正确"这件事变得可以被证明。
【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考