关于 mutation-adequacy
用于评估测试能否捕获注入代码变异的审查技能,仅在高风险任务的审查阶段要求使用。它将存活变异和未覆盖变异转为测试跟进项,是否阻断流程取决于项目配置。
主要功能
- 01运行差异范围内的变异检查
- 02报告变异分数与未覆盖代码
- 03将变异转为测试跟进项
- 04遵循配置的强制级别
适用场景
- 01评估高风险变更的测试
- 02发现未能识别行为变化的断言
- 03定位缺少测试覆盖的变更行
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lvlup-sw/ exarchos --skill mutation-adequacy安装后在 Claude Code 中输入/mutation-adequacy确认已加载。
附带文件references/advisory-threshold.mdreferences/reading-the-carrier.md
详细介绍
突变充分性技能
概述
mutation-adequacy 审查维度是宽松验证组合(验证阶梯第 3 层,R5)的充分性保障。当规划器采用成本更低的测试策略——严格类型 + 内联不变量 + 一个 PBT + 一个验收测试,而非针对每种行为逐一执行红绿测试——突变充分性就是防止遗漏导致空洞测试(以及空洞的 PBT 属性)通过的机器保障。它会评估测试套件是否确实杀死了注入的突变体:这是测试因正确原因而失败的最有力信号。
该维度仅在 /review 边界拦截 HIGH 风险层级——它是委派时 check_test_adequacy(R3)门禁在审查阶段的对应机制。与 HIGH 层级的关联属于 review-contract.ts 中的策略数据(维度名称 = 此技能的文件夹名称);你绝不会在此技能中决定层级归属。该门禁是 HIGH 层级的必需审查维度——这是一项结构性参与要求,与严重性(见下文)不同。操作表将其注册为 [MUTATION_GATE_NAME]: adaptLadderGate(handleMutationAdequacy),严重性包装器 withGateSeverity 会从其注册项的 gate 元数据中读取门禁名称和维度。
两个相互独立的维度,各有默认严重性。 默认情况下,突变分数仅供参考:低于阈值的 mutationScore 会列出未被杀死的突变体,提示后续跟进并发出警告,但不会阻止流程;除非显式的 review.gates['mutation-adequacy'] 配置覆盖项提高了其严重性。100% 的分数既不受期待,也不是必需的(因为存在等价突变体)。
NoCoverage 维度是 DR-6 新增的第二项独立检查,它有自己真实可用的强制执行路径:在 review.mutationEnforcement: block 模式下,阻止模式守卫(workflow/guards.ts Check 4b)会在 diff 范围内的 noCoverage 数量超过已配置的 maxNoCoverage 限额时,阻止 review → synthesize 转换(默认值为 0——不容忍任何未覆盖的变更行),且不受分数影响。这是已投入使用的代码,并非延期计划——但它是可选启用的,和分数严重性一样:review.mutationEnforcement 默认为 advisory,而 mutation-adequacy 的单门禁默认严重性也刻意设为 advisory(这样项目级维度默认值就不会悄悄重新阻止已降级的门禁)。项目只需一行配置即可启用 NoCoverage 强制执行(review.mutation-enforcement: block);记录此维度结果时,必须遵循实际解析出的配置,绝不可将“advisory”硬编码为结果。
CI 侧独立且范围更窄:diff 范围内的突变门禁会在拉取请求事件时运行,但始终以观察模式运行——它会记录判定结果并始终以 0 退出,无论 .exarchos.yml 中如何设置。它目前还不是阻止 CI 通过的保障措施;切换为阻止模式的计划尚未实施,要等完整服务端套件在干净运行器上的判定结果确认无误后再推进。绝不可将 CI 门禁描述为合并阻断项——NoCoverage 强制执行已启用时,它是在上述审查流程中生效,而非在 CI 中生效。
注意:此处适用的 advisory 指低于阈值的分数或超出限额的 NoCoverage 数量是否会实际阻止流程转换。无论两个维度各自解析出的严重性如何,该维度条目对 HIGH 层级都是必需的(如果缺少 mutation-adequacy 审查,all-reviews-passed 守卫就会失败),因此每次都要运行并记录结果。
触发条件
在以下情况下启用此技能:
review在某个 HIGH 层级功能上进入质量阶段- 此工作流的审查契约将
mutation-adequacy列为必需审查项 - 你需要评估(可能已放宽的)测试组合是否确实能杀死突变体
对于中低层级工作,或 /review 边界之外的流程,不要启用此技能——这些情况不需要此维度。
执行
步骤 1:运行按差异范围限定的变异门禁
针对评审/PR 基准引用调用该操作。它会运行已解析的变异命令,
并按差异范围限定(Stryker --since、cargo-mutants --in-diff、mutmut 路径限制——从
工具链 SoT 解析,绝不手动拼接),因此运行会在 < minutes 内完成,而不是耗费完整代码树
的时间预算。
exarchos:exarchos_orchestrate({
action: "mutation-adequacy",
featureId: "<featureId>",
base: "<review/PR base ref>", // e.g. "main" — reuse the same base the review diff uses
worktreePath: "<optional worktree>", // 'auto' resolves the calling delegation's worktree
operationId: "<optional idempotency key>"
// scope defaults to "diff"; do NOT pass scope:"full" here (see Anti-Patterns)
})
该操作会自动发出 mutation.executing_started / mutation.executed(存活性,INV-10)以及携带
mutationScore(INV-1)的可折叠 gate.executed。不要手动发出这些事件。
步骤 2:读取载体
该操作会返回固定载体(data)。无论通过、失败还是降级,其结构都保持稳定:
| 字段 | 含义 |
|---|---|
passed | mutationScore >= threshold && noCoverage <= maxNoCoverage —— 两个维度、两种默认严重性(见步骤 4) |
mutationScore | killed / (total − noCoverage) —— Stryker 惯例;noCoverage 不计入分母 |
killed | 被测试捕获的变异体(良好) |
survived | 逃过检测的变异体 —— 测试已运行,但未能捕获它们 |
noCoverage | 代码中完全没有测试执行过的变异体 |
total | 在差异范围内生成的变异体总数 |
threshold | 生效的建议阈值(覆盖值 > 配置 > 软默认值) |
report | 解析后的 Stryker mutation-testing-report-schema |
next_actions | “编写一个能杀死 <file>:<line> 的测试”后续操作(步骤 3) |
请识别以下降级信号(每种情况都会返回 passed: true,因此门禁不会因错误而关闭):
skipped: true+reason—— 未解析出变异运行器。说明补救措施;不要将其视为失败。warning+ 一个warnings[]条目 —— 运行器未生成可解析的报告。请注明;不要抛出异常。deferred: true+scope: 'full'—— 你错误地请求了完整范围;使用默认差异范围重新运行。
完整的载体和报告结构请参阅 references/reading-the-carrier.md。
步骤 3:将存活变异体转化为“杀死此变异体”的后续操作(INV-12)
该操作已将每个存活变异体和 NoCoverage 变异体映射为一个 next_actions 条目,格式为
“编写一个能杀死 <file>:<line> 的测试”。将这些内容作为具体、可执行的评审发现展示出来 —
每一项都指出测试套件缺少的特定断言:
- 存活变异体表示有测试执行了该行,但断言不足以检测出变异。后续操作:添加一个能够区分变异后行为的断言。
- NoCoverage 变异体表示没有测试触及该行。后续操作:添加一个会执行该行的测试,然后断言可观察到的行为。
将这些记录为 issues,并设置 category: "test-quality",以便它们遵循与其他维度相同的评审报告约定。不要给出笼统的“提高覆盖率”建议——引用该操作指出的文件:行号。
步骤 4:应用判定 — 两个维度,各自解析严重级别
分数维度默认仍为建议性(warning)。低于阈值的 mutationScore:
- 显示幸存项后续处理建议(步骤 3),
- 在评审报告中发出警告,
- 不会阻止
review → synthesize转换——除非显式配置review.gates['mutation-adequacy']覆盖项提高了其严重级别。遵循解析后的严重级别,不要将其硬编码。关于默认采用软阈值的原因以及如何根据分数趋势进行校准,请参阅references/advisory-threshold.md。
NoCoverage 维度相互独立,并有自己的实际阻断模式强制执行路径:在
review.mutationEnforcement: block 下,如果折叠后的 noCoverage 计数超过配置的
maxNoCoverage 预算(默认值为 0),阻断模式守卫会使转换失败,无论分数如何。
与分数维度一样,此项也是选择启用的(review.mutationEnforcement 默认值为 advisory)——解析并遵循实际配置的模式,不要假设任一维度已启用或已禁用。在评审报告中如实记录两个维度——不要将 NoCoverage 阻断失败归入建议性分数的表述,也不要声称任一维度由 CI 强制执行(按 diff 范围运行的 CI 接线采用 observe 模式;参阅反模式)。
必需的输出格式
在评审状态中记录维度结果。评审键 MUST 使用 kebab-case 形式的维度名称(与此技能的文件夹名称一致):
exarchos:exarchos_workflow({ action: "update", featureId: "<id>", updates: {
reviews: { "mutation-adequacy": {
status: "pass", // "pass" is honest when both axes are advisory (the default); if either
// axis's severity is configured to block, report the real result instead
summary: "mutationScore 0.62 (threshold 0.40); 3 survivors surfaced as kill-test follow-ups",
issues: [
{ severity: "MEDIUM", category: "test-quality", file: "src/foo.ts", line: 42,
description: "surviving mutant — no assertion distinguishes the mutated branch",
required_fix: "write a test that kills src/foo.ts:42" }
]
} }
}})
status 必须是表示通过的值(pass | passed | approved | fixes-applied,不区分大小写),才能通过 all-reviews-passed 守卫——单独的字符串会被静默忽略,并阻止转换。
反模式
| 不要 | 应该 |
|---|---|
内联运行 scope: "full" | 全树变异测试是延后到 R10/v2.12 的长时间运行操作——它会返回延后的建议,不会内联运行 |
手动组合 --since / --in-diff | 让 action 根据工具链 SoT 解析 diff 范围 |
| 没有配置覆盖时因分数低于阈值而阻止合并 | 分数维度默认是建议性的——显示后续处理建议,并遵循解析后的严重级别 |
| 假设 NoCoverage 始终是建议性的,或始终会阻断 | 它有自己的实际阻断模式路径(review.mutationEnforcement: block,预算默认值为 0)——与分数严重级别一样,需选择启用;解析并遵循实际配置的模式 |
| 声称 CI 变异测试门禁会阻止合并 | 它始终按 diff 范围以 observe 模式运行(记录其判定,始终以 0 退出,无论配置如何)——绝不要将其描述为合并阻断机制 |
| 将 Skipped/Warning 承载结果视为硬性失败 | 两者均返回 passed: true——报告原因,绝不要抛出错误 |
| 为 medium/low 层级运行此维度 | 它仅在 /review 边界处对 HIGH 层级实施门禁 |
手动发出 gate.executed | action 会自动发出存活状态事件和可折叠的门禁事件 |
| 给出泛泛的“添加更多测试”建议 | 引用 action 在 next_actions 中指出的 <file>:<line> |
参考资料
references/reading-the-carrier.md— 读取承载结果和 Strykermutation-testing-report-schema。references/advisory-threshold.md— 阈值默认为软阈值的原因,以及如何进行校准。