问题:规则是文本,而读它的正是会违反它的那个智能体
仓库里大部分代码由智能体写出来之后,你会停止逐行看 diff——因为它足够快,也足够像对的。质量漂移因此变得隐蔽,而且在不同项目里长得一模一样:智能体写了一个仓库里早就存在的辅助函数;为了少写几行而跨过一层架构边界,因为那条捷径刚好能编译通过;写了一个断言「代码跑过了」而不是「行为是对的」的测试;把你在说明文件里定下的规则遵守一周,然后忘掉。
根本原因不是模型不听话,而是规则的形态。说明书、AGENTS.md、CLAUDE.md 这些都是文本,唯一会读它们的,是那个会违反它们的智能体。gap-trap 换了个思路:除了写下规则,还为每条规则配一个检查,规则被破坏时让提交或 CI 直接失败。智能体跳不过一道会失败的检查。
第一步:装技能
gap-trap 按 Agent Skills 标准分发,装到宿主的技能目录里即可。Claude Code 读 ~/.claude/skills,Codex 读 ~/.agents/skills。
# 方式一:skills CLI(推荐,一条命令)
npx skills add pliablepixels/gap-trap
# 方式二:手动装
# Claude Code:
git clone https://github.com/pliablepixels/gap-trap.git
mkdir -p ~/.claude/skills
cp -r gap-trap/gap-trap ~/.claude/skills/
# Codex 用 ~/.agents/skills:
# mkdir -p ~/.agents/skills
# cp -r gap-trap/gap-trap ~/.agents/skills/两处容易踩:技能要放到宿主的技能目录才生效,路径写错不会报错、只是不出现;另外安装它时会带上一个兄弟技能 slop-mop(让智能体写的文档、提交信息、PR 描述读起来像人写的)。harness 是在会话启动时加载技能的,所以 slop-mop 从「安装它的那次会话」的下一次会话开始生效。
第二步:在目标仓库里跑一次 setup
进入你想整理的仓库,然后调用技能:
# Claude Code
/gap-trap setup
# Codex 用 $ 前缀调用同一个技能
$gap-trap setup它会先读这个仓库,给出一个简短计划——要写哪些契约、哪些闸门、跑什么命令——然后向你确认它拿不准的地方,接着写文件、逐条证明每道闸门确实是「红的」(也就是规则被破坏时它真的会失败),最后提交。它不会推送;跑完之后还会问你要不要立刻用新框架检查一遍现有代码,选是的话它只列出违反项,不改任何东西。
第三步:读懂它写下的四类东西
setup 会在仓库里落四类内容,理解它们的区别比记住命令更重要。
- 契约(Contracts):每个「只有一种正确做法」的地方一条,例如 HTTP 请求、日志、配置读取、鉴权。每条写明该用什么、绝对不能用什么、以及哪个检查能发现绕过行为。
- Proven red:一个 CI 任务,把每个新测试拿去跑旧代码,如果测试在旧代码上也能通过就判定失败。它挡住的是「不会失败的测试」——一个永远为真的断言证明不了任何事。
- 棘轮(Ratchets):已知问题的计数,例如 lint 积压数量、超过 400 行的文件数量。它们只能下降、不能上升。存量债务不再是你和智能体互相甩锅的理由,但也不会因为一次检查而全部爆掉。
- Playbook:这个项目用血换来的经验,写下来,下一个会话不用再学一遍。
第四步:生成的契约必须人工复核
技能作者自己在文档里把这条标成了 IMPORTANT:生成出来的规则与契约要仔细审。原因很直白——契约选错了,之后每一个会话都会按错的规范写代码,纠正成本比一开始多花二十分钟高得多。
复核时重点看两类:契约写的「唯一正确做法」是不是你这个仓库真正认可的(比如日志到底该走哪个封装);闸门能不能真的失败——可以故意改坏一处代码,看 CI 是否报错,而不是只看文件生成了。这份判断需要人来下,技能不可能替你决定什么才是你项目的正确做法。
第五步:出事之后,或者大约一个月一次,跑 refine
# Claude Code
/gap-trap refine
# Codex
$gap-trap refinerefine 找三样东西:没有配闸门的规则、指向已经不存在代码的契约、以及埋在你提交历史里但没人写下来的经验。它会以一个 PR 的形式提出修改,仍然需要你来判断。另外这套框架是从作者自己维护的 zmNinjaNg 项目里长出来的——在那个项目里智能体写了绝大部分代码,规则、契约与闸门就是从它的开发者指南里提炼的。
第六步:更新与语言支持
# 走 skills CLI 的话
npx skills update gap-trap
# 手动装的话,先删旧目录再拷贝,避免上游删掉的文件留在本地
git -C gap-trap pull
rm -rf ~/.claude/skills/gap-trap # Codex: ~/.agents/skills/gap-trap
cp -r gap-trap/gap-trap ~/.claude/skills/语言支持上,Node 与 Python 仓库的闸门直接落在各自的测试体系里;其它语言(Go、Rust、Java、Ruby、.NET、PHP、Swift、C++)拿到的是 shell 版本,只需要 git、grep、awk 加上你自己的测试命令,因此不挑技术栈。
- 它需要在宿主最强的编码模型上跑(Claude Code 里的 Opus 级别、Codex 里的前沿编码模型高推理档),模型弱于这个级别它会停下来——因为契约选错的代价会一路累积。
- 它不能替代代码审查:闸门能挡住的是「可被机械检查的规则」,架构是否合理、需求是否理解正确,仍然要靠人。
- 它与你已经在用的规范驱动开发(SDD)不冲突,定位是内层循环——SDD 管做什么,gap-trap 管别写坏。
规则是文本,而唯一会读它的,是那个会违反它的智能体。所以规则必须配一道会失败的检查。