mcpField
C
constraint-driven-development

constraint-driven-development

github.com/addyosmani测试与质量111.5k35.2k 次安装2 周前更新
代码仓库

关于 constraint-driven-development

面向使用 Agent 开发软件的团队,这项技能将项目质量标准转化为可记录、可自动检查的约束。它通过访谈制定门槛,将检查接入开发流程,并提示审查可能削弱门槛的改动。

主要功能

  1. 01盘点项目现有工具与检查
  2. 02访谈确定质量门槛
  3. 03生成 CONSTRAINTS.md
  4. 04配置各质量维度的检查工具
  5. 05按开发阶段安排检查
  6. 06审查门槛弱化与绕过

适用场景

  1. 01为新项目建立质量门槛
  2. 02约束自主编码循环的交付标准
  3. 03明确 CI 检查的阻断与警告规则

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/addyosmani/agent-skills --skill constraint-driven-development

安装后在 Claude Code 中输入/constraint-driven-development确认已加载。

附带文件references/floor-guard.md

详细介绍

约束驱动开发

概述

本套件中的其他技能介绍了什么才算做得好。code-review-and-quality 提供五个衡量维度。test-driven-development 提供一个循环。security-and-hardening 提供一份威胁清单。这些内容都以文字形式存在,由 Agent 阅读并可能遵循,而且在会话结束后都不会留存。

这项技能会产出不同的东西:一份关于这个项目质量标准的书面记录,其中包含具体数字,能够在对话结束后继续留存,并可通过机械方式检查。

原因很重要。编写代码时,阅读代码就能判断它质量如何。而 Agent 一个下午写出的代码,比你一周读得完的还多,因此判断标准必须从你的脑中转移到贯穿开发循环运行的检查中。这些检查必须真实存在,采用你实际选定的数字,并且要足够贴近工作环节触发,让 Agent 能修正自己的输出。

规范驱动开发说明要构建什么。测试驱动开发证明它能正常工作。约束驱动开发则在任何人于拉取请求中争论之前,定义好“达到发布标准”意味着什么。

适用场景

在以下情况下使用此技能:

  • 启动项目或重要功能,但尚未写下质量标准
  • 用户要求“设置约束”“添加质量门禁”“定义我们的标准”或“阻止 Agent 发布劣质代码”
  • Agent 产出的代码量大到没人会逐行阅读
  • CI 中有各种检查,但没人说得清哪些会阻止合并,哪些只是摆设
  • 覆盖率、性能或无障碍方面的数字每个 PR 都要争论,而不是一次定好
  • 即将运行 /build auto 或任何自主循环,而阻止它进入 main 的唯一关卡是由 Agent 自己编写的测试套件

不适用场景:

  • 项目已有 CONSTRAINTS.md,且用户并不想修改它——阅读并遵循即可
  • 一次性脚本、试验性项目、用完即弃的原型
  • 用户现在想要代码审查(code-review-and-quality)或构建 CI 流水线(ci-cd-and-automation)
  • 尚未达到产品与市场契合点的代码,预计只会存在两周——下文的底线仍值得采用,其余内容则不值得

加载约束

访谈需要用户在线参与。不要在非交互场景中运行访谈(CI、/loop、自主运行)。如果缺少约束且你处于其中一种场景,请采用下文的底线,说明你已这样做,并将其余事项标记给人工处理。

流程

第 1 步:先检测,再提问

能读取的内容,就不要询问。提出第一个问题前,先收集以下信息:

内容查看位置
语言和技术栈package.json、pyproject.toml、go.mod、Cargo.toml
测试运行器开发依赖、test 脚本、现有测试文件
现有代码检查工具eslint.config.*、biome.json、.ruff.toml
当前覆盖率coverage/ 输出,或运行一次测试套件
CI.github/workflows/、.gitlab-ci.yml
Agent 工具框架.claude/、.codex/、AGENTS.md

用两行报告你的发现,然后只询问尚未明确的内容。

第 2 步:四个问题,每个都有默认值

遵循 interview-me 的一次只问一个问题的原则,但有一点不同:这里的每个问题都有默认值,因此“我不知道”也是完整答案,仍然能生成可用的配置。

Q1: Beyond the floor, which of these do you want enforced?
    (a) Test coverage on new code
    (b) Security scanning
    (c) Performance budgets
    (d) Accessibility
    (e) Architecture boundaries
GUESS: (a) and (b) — you have a test runner already and you're handling user input.
DEFAULT if unsure: (a) and (b).
Say what each pick costs: (c) and (d) need a running URL, (e) needs a rules file written.
Q2: When a check fails while the agent is mid-task, should it block or warn?
GUESS: Block. You're running agents unattended and a warning nobody reads is a warning.
DEFAULT if unsure: Block on the floor, warn on everything else for the first two weeks.
Q3: Do you have target numbers in mind, or should I measure where you are today and hold that line?
GUESS: Measure. Most teams don't have a number, and an invented one gets ignored.
DEFAULT if unsure: Measure and hold. See "Ratchets" below.
Q4: What's the slowest check you'll tolerate before the agent hands work back?
GUESS: About 90 seconds. Longer and you'll stop running it.
DEFAULT if unsure: 90 seconds at task end, unlimited in CI.

最多问四个问题。问十二个问题会产出一份没人看得懂的配置,也会让用户后悔开始这件事。

第 3 步:编写 CONSTRAINTS.md

在仓库根目录创建一个文件。任何工具框架上的 Agent 都能读取它,对它的修改也会在恰当的位置出现在评审中。

# Constraints

Last reviewed: 2026-08-08 by @addy

## Floor (always enforced, no setup required)

- No new suppression comments: `@ts-ignore`, `eslint-disable`, `# noqa`, `# type: ignore`
- No unimplemented stubs: `throw new Error("Not implemented")`, empty `catch {}`
- No skipped or deleted tests without a reason in the commit message
- No secrets in source
- This file does not get weakened to make a change pass

## Enforced with numbers

| Dimension | Rule | Checked by | Runs at |
|-----------|------|-----------|---------|
| Types | Zero type errors | `tsc --noEmit` | every edit |
| Lint | Zero errors from our config | `biome check` | every edit |
| Secrets | No secrets in source | `gitleaks detect --redact` | every edit |
| Coverage | Changed lines ≥ 80% covered | `vitest run --coverage` + git diff | task end, CI |
| Security: code | No high findings | `semgrep scan --config p/default` | CI |
| Security: deps | Nothing at high or above | `osv-scanner scan source -r .` | CI |
| Accessibility | Zero critical or serious | `axe $PREVIEW_URL --tags wcag2a,wcag2aa,wcag21aa` | preview deploy |
| Performance | LCP ≤ 2500ms, CLS ≤ 0.1 | `lighthouse $PREVIEW_URL --output=json` | preview deploy |

Every row names the command that produces the verdict. A dimension with a
number and no command in this column is an aspiration, not a constraint.

## Measured, not yet enforced

| Metric | Today | Direction |
|--------|-------|-----------|
| Project coverage | 62.4% | must not fall |
| Bundle size (main) | 184 kB | must not grow |

## Exceptions

| ID | Rule | Path | Reason | Owner | Expires |
|----|------|------|--------|-------|---------|
| W1 | `no-explicit-any` | `src/legacy/**` | Rewrite tracked in ENG-441 | @addy | 2026-11-01 |

然后在 AGENTS.md 和 CLAUDE.md 中各添加一行:Read CONSTRAINTS.md before writing code. Do not weaken it to make a change pass.

第 4 步:安装各维度所需的工具

选择一个维度就意味着要安装相应工具。别只给用户一个数字,却不给出实现机制;也别在已有事实标准工具时自己另造检查器——列出这些工具,是因为它们的规则格式和阈值是整个生态系统的目标标准,因此团队现有的配置可以继续使用。

维度工具安装运行门槛
类型(TS)tsc已有tsc --noEmit任何错误
类型(Python)mypypip install mypymypy .任何错误
Lint现有配置已有eslint . / biome check / ruff check任何错误
覆盖率(JS)测试运行器已有vitest run --coverage(或 jest --coverage)变更行的覆盖率
覆盖率(Python)pytest-covpip install pytest-covpytest --cov --cov-report=lcov同上
安全:代码Semgreppipx install semgrepsemgrep scan --config p/default --config p/owasp-top-ten任何高危发现
安全:密钥gitleaksbrew install gitleaksgitleaks detect --redact --no-banner任何发现
安全:依赖项osv-scannerbrew install osv-scannerosv-scanner scan source -r .高危及以上
性能:页面Lighthousenpm i -D lighthouselighthouse $URL --output=json --quietLCP、CLS、性能分数
性能:包体积size-limitnpm i -D size-limitsize-limit --json每个入口的字节预算
无障碍性axe-corenpm i -D @axe-core/cliaxe $URL --tags wcag2a,wcag2aa,wcag21aa严重或高危问题为零
架构dependency-cruisernpm i -D dependency-cruiserdepcruise --validate src任何违规
断言质量Strykernpm i -D @stryker-mutator/corestryker run --mutate <changed files>变异分数

以下 5 点若忽略,会带来麻烦:

  1. gitleaks 的 --redact 不能省略。 不加它,匹配到的密钥就会出现在 Agent 的对话记录中,进而泄漏到日志、摘要或提交消息里。只报告规则和位置,绝不报告密钥值。
  2. Lighthouse 和 axe 需要 URL。 它们只能检查正在运行的应用,因此应在运行时阶段对预览部署或预先启动的本地服务器运行。如果项目没有可访问的 URL——例如 CLI、库或桌面应用——就应说明并移除该维度,而不是编造无法运行的检查。
  3. 将开销较大的检查限定在差异范围内。 对整个仓库运行 stryker run --mutate 需要数小时,最终会被关掉;只对变更涉及的文件运行则不到一分钟。Semgrep 也一样,可以传入路径列表。
  4. 覆盖率不需要再运行一次测试。 读取测试套件已经生成的 lcov,并将其与 git diff 求交集。为了获取一个数字而运行两次测试,是让大家对此感到厌烦最快的方式。
  5. Semgrep 注册表规则可免费运行;重新分发前请检查许可证。 如果法律团队有相关要求,opengrep 是一个即插即用的分支,使用相同的规则格式并生成相同的 JSON 输出。

将每项检查添加到项目自己的脚本中,这样无需 Agent 也能复现:

{
  "scripts": {
    "check:fast": "tsc --noEmit && eslint . && gitleaks detect --redact --no-banner",
    "check:task": "npm run check:fast && vitest run --coverage",
    "check:full": "npm run check:task && semgrep scan --config p/default && osv-scanner scan source -r ."
  }
}

工具本身不如这种映射重要。编辑后运行 check:fast,Agent 认为任务已完成时运行 check:task,CI 中运行 check:full。

这些命令现在位于两个地方——CONSTRAINTS.md 中的 Checked by 列,以及这些脚本。CONSTRAINTS.md 是规范来源:它会在列出每条命令的同时说明原因,也会出现在代码审查中。脚本只是便捷封装,必须与其保持一致,而不能成为第二个事实来源;如果两者不一致,以该文件为准。

第 5 步:将其融入生命周期

最大的错误,就是到处运行所有检查。一个会让 Agent 卡住的检查会被关闭;而被人关闭的门禁比没有门禁更糟,因为标准看起来仍然存在。

阶段命令运行内容耗时预算
BUILD/build类型检查、lint、密钥检查、基础检查低于 5s,仅检查已更改的文件
VERIFY/test相关测试、已更改行的覆盖率低于 90s
REVIEW/review所有检查,以及下面的防护措施数分钟
SHIP/ship方向检查、确保没有回归CI

两条规则能让这一切更容易接受:

  1. 限定为差异范围。 检查此次更改涉及的行,而不是整个仓库。已更改行的覆盖率是 Agent 可以提升的指标;项目整体覆盖率则是它继承而来的。
  2. 根据成本决定放置位置。 耗时超过几秒的检查应移出编辑循环。对整个仓库进行变异测试需要数小时;只测试此次更改涉及的文件则不到一分钟,这决定了人们会不会运行这项检查。
第 6 步:守住标准本身

有人会指出,如果 Agent 同时编写代码和检查,那么这些检查就毫无证明力。这个说法有一半正确,值得通过工程手段加以防范。

Agent 不会设计巧妙的漏洞。它们遇到失败的检查,就会选择让检查通过的最省力办法。在审查时,留意差异中是否出现以下五种操作:

  1. 阈值被调低。 降低了预算、下调了严重性,或从快速阶段移除了检查。将 CONSTRAINTS.md 与分支点时的状态进行比较。
  2. 测试变得更容易。 新增了 .skip、删除了测试文件,或从保留的测试中移除了断言。
  3. 检查器被屏蔽。 新增了 @ts-ignore 或 eslint-disable。有四种抑制方式尤其值得关注,因为它们会关闭你所依赖的检查:istanbul ignore 会将代码排除在覆盖率之外,而不是对其进行测试;Stryker disable 会隐藏存活的变异体;nosemgrep 和 gitleaks:allow 会屏蔽安全问题。
  4. 工作尚未完成。 会抛出异常的存根、将失败悄然吞掉的空 catch,或本应实现代码的位置上留着 TODO。
  5. 出现了例外。 在无人讨论的情况下,Exceptions 表中新增了一行。

这些检查都不需要 git diff 之外的工具。提高标准应悄无声息;降低标准则应引人注意。

与编号维度不同,基础标准没有事实上的专用工具,因此被要求执行它的 Agent 往往会从头编写检查器,两个 Agent 就会写出两套不同的检查器。本技能附带了这五项检查的参考实现,见 references/floor-guard.md(限定差异范围,退出码为 0/1/2,模式可按生态系统调整)。请在此基础上改造,而不是从头重做,理由与每个维度都指定事实上的工具相同:确保不同运行和技术栈使用相同的机制。

并非所有检查都同样具有循环性。 按照一个问题对它们排序:Agent 能否通过编写无法正常工作的代码来让这项检查通过?

  • 外部检查 — axe-core 编码了 WCAG,osv-scanner 会读取漏洞数据库,Lighthouse 会测量真实浏览器。Agent 无法与这些结果争辩。
  • 项目检查 — 你的 lint 规则、你的分层边界。文件由人负责。
  • 测试套件 — 你自己的测试。最有用,也是唯一真正具有循环性的一类。

完全由第三类检查构成的标准,不如包含外部意见的标准有价值。请确认至少存在一项外部约束。

Step 7:没有目标数值时,使用棘轮机制

在覆盖率只有 62% 的代码库上设定 80% 的覆盖率要求,只会让构建一直报红,最终让团队学会无视红色构建。

另一种做法无需做出任何决定:记录当前水平,然后禁止它变差。将其填入“已测量,尚未强制执行”表格,写明今天的数值和趋势。每次检查都与记录的数值比较,而不是与目标比较。数值提高时,就更新记录;数值下降时,这就是发现的问题。

这也回应了对训练的一个合理质疑。模型会因通过测试而得到奖励,而你几秒钟内就能评估这一点。架构腐化要数月才会显现,而且永远不会进入权重。棘轮机制正是缺失的惩罚,并且会写入构建能够读取的地方。

合理的默认值

用户没有偏好时,就采用以下设置。这些值的选择,是为了让大多数代码库从第一天起就能达到。

约束默认值该数值的理由
变更行的覆盖率≥ 80%要求编写测试的同时,也允许仅修改配置行
项目覆盖率当前值,不得下降无需争论即可采用
变异分数(如使用)起始值 ≥ 60%适用于此前从未进行变异测试的测试套件;80% 表示已较成熟
依赖项漏洞不得有高危及以上漏洞低于此级别的大多只是噪声
LCP≤ 2500 msCore Web Vitals 的“良好”阈值
CLS≤ 0.1同上
无障碍性axe 严重或危急违规为零中等和轻微违规往往有争议
例外有效期90 天足够安排修复,也短到不容易忘记
棘轮容差0.5%当无关文件变动导致数值波动时,用于吸收偏差

把数值和理由写在一起。没有依据的阈值,会被下一个碰到它的人删掉。

升级路径

约束有三个强度级别。从第一个开始。

  1. 仅书面记录。 存在 CONSTRAINTS.md,且 Agent 会阅读。无需成本,能抓住无心之失,但依赖 Agent 遵守。
  2. 脚本化。 编写一个 npm run check(或 make check),运行快速检查,并将其接入 Agent 的编辑后钩子和 CI。结果确定,无需新增依赖。
  3. 工具支持。 使用专用运行器处理差异范围、预算、棘轮机制和守护检查。配置超出 shell 脚本的承载能力时再采用。references/floor-guard.md 中的底线守护参考,是实现这套守护检查的起点。

大多数项目做到第 2 级就应止步。只有在需要维护的检查执行 shell 代码超过约 30 行时,才升级到第 3 级。

首次运行可以只检查底线。 底线守护只检查差异,无需安装,因此你可以从第一天起就强制执行底线,并在安装每个工具时逐步添加有数值的维度,而不是等所有检查器都就位后才保护第一个提交。如果你想让笔记本电脑保持干净,也可以只在 CI 中运行需要全局安装的安全工具(gitleaks、osv-scanner);在 Runs at 列中声明每个维度的运行位置。

常见借口

借口实情
“等代码稳定后再添加约束”代码会围绕其演进过程中允许的标准逐渐稳定
“测试就是约束”你编写的测试只能证明你认同自己的做法;它们对新代码的覆盖率、依赖风险或包体积增长毫无说明
“我们无法达到 80% 的覆盖率”那就别设 80%。设定当前数值并守住它
“这会拖慢 Agent”只有把慢速检查放进快速循环时才会。那是放置不当,不是反对约束的理由
“我会记住我们的标准”Agent 不会,而大部分代码都是它在写
“约束会阻碍我们发布”指定负责人和日期的例外可以解除阻碍。删除约束则会让所有人永远不再受其约束

危险信号

如果发现以下情况,请停下来重新考虑:

  • 访谈超过了四个问题,或者生成的配置用户无法解释
  • 设定的预算在当前代码库中不达标,也没有达到目标的计划
  • 某个维度在 CONSTRAINTS.md 中写了一个数值,却没有对应的工具
  • 明明存在事实标准的检查器,却选择手动编写,导致团队现有的配置被忽略
  • 每项约束都由项目自身的测试套件检查,没有外部意见
  • 在导致问题的功能所在提交中,CONSTRAINTS.md 也发生了变化
  • 某项例外没有负责人,或者到期时间超过一年
  • Agent 提议放宽阈值,而不是修复代码
  • 耗时较长的检查被加入编辑循环,而且有人已经开始传入 --no-verify
  • 文件写好后,就再也没人打开过 CONSTRAINTS.md

验证

满足以下条件时,表示该技能已正确应用:

  • CONSTRAINTS.md 已存在,其中的每个数值都有明确的理由
  • 强制执行底线要求,且在当前代码库上无需改动即可通过
  • 用户选择的每个维度都已安装相应工具,并且有一条现在就能运行的命令
  • 每项约束都说明了运行位置,且快速阶段耗时不超过几秒
  • 至少有一项约束来自外部(不由项目自身的测试判定)
  • 仅测量的指标记录了当前数值和变化方向
  • 例外项都有负责人和到期日期
  • AGENTS.md 或 CLAUDE.md 指向该文件
  • 在当前分支上进行试运行,不会产生用户有异议的失败

另请参阅

  • interview-me — 此技能的需求收集环节所借鉴的逐次只问一个问题的做法
  • code-review-and-quality — 如何进行审查;此技能决定审查要强制执行哪些要求
  • ci-cd-and-automation — 构建运行这些约束的流水线
  • test-driven-development — 衡量覆盖率和变异测试约束的测试套件
  • security-and-hardening — 安全维度应包含哪些内容
  • performance-optimization — 性能数值的来源

评论

支持 Markdown