mutation-adequacy

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Mutation Adequacy Skill

Mutation Adequacy技能

Overview

概述

The
mutation-adequacy
review dimension is the adequacy backstop for the relaxed verification mix (verification ladder slice 3, R5). When the planner ships the cheaper testing strategy — strict types + inline invariants + one PBT + one acceptance test instead of granular per-behavior red-green — mutation adequacy is the machine guard against the vacuous tests (and vacuous PBT properties) that omission could otherwise admit. It scores whether the test suite actually kills injected mutants: the strongest signal that tests fail for the right reason.
This dimension gates the HIGH risk tier only, at the
/review
boundary only
— it is the review-phase counterpart to the delegation-time
check_test_adequacy
(R3) gate. The coupling to the high tier is policy data in
review-contract.ts
(the dimension name = this skill's folder name); you never decide tier membership in this skill. The gate is registered as a required D1 review dimension (
adaptLadderGate(MUTATION_GATE_NAME, 'D1', handleMutationAdequacy)
) — a structural participation fact, distinct from severity (below).
Two orthogonal axes, two default severities. The mutation score stays advisory by default: a sub-threshold
mutationScore
surfaces survivor follow-ups and warns, but does not block, unless an explicit
review.gates['mutation-adequacy']
config override raises its severity. A 100% score is neither expected nor required (equivalent mutants exist).
The NoCoverage axis is a second, independent check DR-6 added, with its own real, functioning enforcement path: under
review.mutationEnforcement: block
, the block-mode guard (
workflow/guards.ts
Check 4b) fails the
review → synthesize
transition whenever the diff-scoped
noCoverage
count exceeds the configured
maxNoCoverage
budget (default 0 — zero uncovered changed lines tolerated), independent of the score. This is live code, not a deferred plan — but it is opt-in, the same way the score's severity is:
review.mutationEnforcement
defaults to
advisory
, and
mutation-adequacy
's per-gate severity default is deliberately advisory too (so a project's dimension-level defaults never silently re-block a demoted gate). A project flips NoCoverage enforcement on with one config line (
review.mutation-enforcement: block
); honor whichever way it's resolved, never hardcode "advisory" when recording this dimension's result.
The CI side is separate and narrower: a diff-scoped mutation gate runs on pull-request events, but it runs in observe mode unconditionally — it logs its verdict and always exits 0, regardless of any
.exarchos.yml
setting. It is not yet a blocking CI backstop; the flip is deferred, pending a clean runner verdict on the full server suite. Never represent the CI gate as a merge blocker — where NoCoverage enforcement is live, it is on the review path above, not in CI.
Note: advisory (where it applies) refers to whether a sub-threshold score or an over-budget NoCoverage count actually stops the transition. The dimension entry is required on the high tier regardless of either axis's resolved severity (the
all-reviews-passed
guard fails if the
mutation-adequacy
review is absent), so run and record it every time.
mutation-adequacy
评审维度是宽松验证组合(验证阶梯第3部分,R5)的充分性保障机制。当规划者采用更轻量化的测试策略——严格类型检查+内联不变量+一项PBT(属性基测试)+一项验收测试,而非针对每个行为的细粒度红绿测试——时,变异充分性机制可作为自动化防护手段,避免因测试策略简化而引入无效测试(及无效PBT属性)。它会评估测试套件是否真正杀死注入的变异体:这是测试因正确原因失败的最有力信号。
该维度仅针对高风险层级生效,且仅在**
/review
节点触发——它是评审阶段对应委托阶段
check_test_adequacy
(R3)检查门的机制。与高风险层级的关联是
review-contract.ts
中的策略数据(维度名称等于本技能的文件夹名称);本技能不负责判定层级归属。该检查门被注册为必填的
D1**评审维度(
adaptLadderGate(MUTATION_GATE_NAME, 'D1', handleMutationAdequacy)
)——这是结构参与属性,与下文的严重程度无关。
两个独立维度,两种默认严重程度。变异分数默认仅作为参考:低于阈值的
mutationScore
会显示存活变异体的后续任务并发出警告,但不会阻塞流程,除非通过显式的
review.gates['mutation-adequacy']
配置覆盖提升其严重程度。100%的分数既非预期也非强制要求(存在等效变异体)。
NoCoverage维度是DR-6新增的第二个独立检查项,拥有独立的可生效执行路径:当
review.mutationEnforcement
设为
block
时,阻塞模式防护(
workflow/guards.ts
检查项4b)会在差异范围的
noCoverage
计数超过配置的
maxNoCoverage
预算(默认0——不允许存在未覆盖的变更行)时,阻止
review → synthesize
的流程转换,与分数无关。这是已生效的代码,而非延迟规划——但它是可选启用的,与分数的严重程度设置方式相同:
review.mutationEnforcement
默认值为
advisory
,且
mutation-adequacy
检查门的默认严重程度也特意设为参考级(因此项目的维度级默认设置不会静默重新启用已降级的检查门)。项目只需一行配置即可启用NoCoverage的阻塞模式(
review.mutation-enforcement: block
);无论配置如何,都需严格遵循,记录该维度结果时切勿硬编码为“参考级”。
CI侧的实现是独立且范围更窄的:差异范围的变异检查门会在拉取请求事件中运行,但它无条件以观察模式运行——会记录验证结果但始终返回0退出码,与
.exarchos.yml
的设置无关。目前它还不是阻塞性的CI保障机制;切换为阻塞模式的计划已推迟,需等待完整服务器套件的运行器验证结果无误后再执行。切勿将CI检查门描述为合并阻塞项——NoCoverage的阻塞模式生效于上文的评审流程,而非CI流程。
注意:参考级(适用时)指的是低于阈值的分数或超出预算的NoCoverage计数是否会实际阻止流程转换。无论两个维度的最终严重程度如何,高风险层级都必须包含该评审维度(如果缺少
mutation-adequacy
评审,
all-reviews-passed
防护会判定失败),因此每次都需运行并记录该维度。

Triggers

触发条件

Activate this skill when:
  • review
    reaches the quality stage on a HIGH-tier feature
  • The review contract lists
    mutation-adequacy
    in the required reviews for this workflow
  • You need to assess whether the (possibly relaxed) test mix actually kills mutants
Do not activate for medium/low-tier work, or outside the
/review
boundary — those paths do not require this dimension.
在以下情况激活本技能:
  • 针对高风险层级功能的
    review
    流程进入质量阶段
  • 评审合约中列出
    mutation-adequacy
    作为该工作流的必填评审项
  • 需要评估(可能已简化的)测试组合是否真正杀死变异体
请勿为中/低风险层级的工作或
/review
节点之外的流程激活本技能——这些路径不需要该维度。

Execution

执行步骤

Step 1: Run the diff-scoped mutation gate

步骤1:运行差异范围的变异检查门

Invoke the action against the review/PR base ref. It runs the resolved mutation command diff-scoped (Stryker
--since
, cargo-mutants
--in-diff
, mutmut path restriction — resolved from the toolchains SoT, never composed by hand), so the run completes in
< minutes
, not the full-tree time budget.
typescript
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)
})
The action emits
mutation.executing_started
/
mutation.executed
(liveness, INV-10) and a foldable
gate.executed
carrying
mutationScore
(INV-1) automatically. Do not hand-emit these events.
针对评审/PR的基准引用调用该动作。它会运行解析后的变异命令(差异范围:Stryker的
--since
、cargo-mutants的
--in-diff
、mutmut的路径限制——从工具链的可信源解析,切勿手动组合),因此运行时间仅需数分钟,而非全代码树测试的耗时。
typescript
exarchos:exarchos_orchestrate({
  action: "mutation-adequacy",
  featureId: "<featureId>",
  base: "<review/PR base ref>",      // 例如 "main" — 复用评审差异使用的同一基准
  worktreePath: "<optional worktree>", // 'auto'会解析调用委托的工作树
  operationId: "<optional idempotency key>"
  // scope默认值为"diff"; 请勿在此处传递scope:"full"(参见反模式)
})
该动作会自动发送
mutation.executing_started
/
mutation.executed
(活跃度追踪,INV-10)以及包含
mutationScore
的可折叠
gate.executed
事件(INV-1)。请勿手动发送这些事件。

Step 2: Read the carrier

步骤2:读取载体数据

The action returns the fixed carrier (
data
). The shape is stable regardless of pass/fail/degrade:
FieldMeaning
passed
mutationScore >= threshold && noCoverage <= maxNoCoverage
— two axes, two default severities (see Step 4)
mutationScore
killed / (total − noCoverage)
— the Stryker convention;
noCoverage
is excluded from the denominator
killed
mutants a test caught (good)
survived
mutants that escaped — tests ran but did not catch them
noCoverage
mutants in code no test exercises at all
total
total mutants generated within the diff scope
threshold
the effective advisory threshold (override > config > soft default)
report
the parsed Stryker
mutation-testing-report-schema
next_actions
"write a test that kills
<file>:<line>
" follow-ups (Step 3)
Degrade signals to recognize (each returns
passed: true
so the gate never blocks closed-with-error):
  • skipped: true
    +
    reason
    — no mutation runner resolved. Report the remediation; do not treat as a failure.
  • warning
    + a
    warnings[]
    entry — the runner produced no parseable report. Note it; do not throw.
  • deferred: true
    +
    scope: 'full'
    — you (incorrectly) requested full scope; re-run with the default diff scope.
See
references/reading-the-carrier.md
for the full carrier and report shape.
该动作返回固定格式的载体数据(
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

Step 3: Turn survivors into kill-this-mutant follow-ups (INV-12)

步骤3:将存活变异体转化为“杀死该变异体”的后续任务(INV-12)

The action already maps each surviving and
NoCoverage
mutant to a
next_actions
entry of the form "write a test that kills
<file>:<line>
"
. Surface these as concrete, actionable review findings — each one names a specific assertion the suite is missing:
  • A survived mutant means a test exercises that line but asserts nothing strong enough to detect the mutation. The follow-up: add an assertion that distinguishes the mutated behavior.
  • A NoCoverage mutant means no test touches that line at all. The follow-up: add a test that exercises it, then assert on the observable behavior.
Record these as
issues
with
category: "test-quality"
so they ride the same review-report contract as the other dimensions. Do not invent generic "improve coverage" advice — quote the file:line the action surfaced.
该动作已将每个存活和
NoCoverage
变异体映射为
next_actions
中的条目,格式为**“编写测试以杀死
<file>:<line>
”**。将这些内容作为具体、可执行的评审结果呈现——每个条目都指出了测试套件缺失的特定断言:
  • 存活变异体表示测试覆盖了该行代码,但未添加足够强的断言以检测变异。后续任务:添加可区分变异后行为的断言。
  • NoCoverage变异体表示完全没有测试触及该行代码。后续任务:添加覆盖该行的测试,然后针对可观察行为添加断言。
将这些内容记录为
category: "test-quality"
issues
,使其与其他维度遵循相同的评审报告合约。请勿给出“提升覆盖率”这类通用建议——直接引用动作返回的
<file>:<line>
信息。

Step 4: Apply the verdict — two axes, each with its own resolved severity

步骤4:应用验证结果——两个维度,各有独立的最终严重程度

The score axis stays advisory (warning) by default. A sub-threshold
mutationScore
:
  • surfaces the survivor follow-ups (Step 3),
  • warns in the review report,
  • does not block the
    review → synthesize
    transition — unless an explicit
    review.gates['mutation-adequacy']
    config override raises its severity. Honor the resolved severity, do not hardcode it. See
    references/advisory-threshold.md
    for why the default is a soft threshold and how to calibrate it from the score trend.
The NoCoverage axis is independent, with its own real block-mode enforcement path: under
review.mutationEnforcement: block
, if the folded
noCoverage
count exceeds the configured
maxNoCoverage
budget (default 0), the block-mode guard fails the transition regardless of the score. Like the score, it is opt-in (
review.mutationEnforcement
defaults to
advisory
) — resolve and honor the actual configured mode, don't assume either axis is on or off. Record both axes honestly in the review report — do not fold a blocking NoCoverage failure into advisory-score language, and do not describe either axis as enforced by CI (the diff-scoped CI wiring runs in observe mode; see Anti-Patterns).
分数维度默认是参考级(警告)。低于阈值的
mutationScore
  • 显示存活变异体的后续任务(步骤3),
  • 在评审报告中发出警告,
  • 不会阻塞
    review → synthesize
    的流程转换——除非通过显式的
    review.gates['mutation-adequacy']
    配置覆盖提升其严重程度。遵循最终生效的严重程度,切勿硬编码。关于默认阈值为何是软默认值以及如何根据分数趋势校准阈值,请参见
    references/advisory-threshold.md
NoCoverage维度是独立的,拥有自己的阻塞模式执行路径:当
review.mutationEnforcement
设为
block
时,如果汇总的
noCoverage
计数超过配置的
maxNoCoverage
预算(默认0),无论分数如何,阻塞模式防护都会阻止流程转换。与分数一样,它是可选启用的(
review.mutationEnforcement
默认值为
advisory
)——解析并遵循实际配置的模式,勿假设任一维度是启用或禁用状态。在评审报告中如实记录两个维度的结果——勿将阻塞性的NoCoverage失败转化为参考级分数的表述,也勿描述任一维度由CI强制执行(差异范围的CI流程以观察模式运行;参见反模式)。

Required Output Format

要求的输出格式

Record the dimension result on the review state. The review key MUST be the kebab-case dimension name (it equals this skill's folder name):
typescript
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" }
    ]
  } }
}})
A passing-value
status
(
pass | passed | approved | fixes-applied
, case-insensitive) is required for the
all-reviews-passed
guard — a flat string is silently ignored and blocks the transition.
在评审状态中记录维度结果。评审键必须为短横线格式的维度名称(与本技能的文件夹名称一致):
typescript
exarchos:exarchos_workflow({ action: "update", featureId: "<id>", updates: {
  reviews: { "mutation-adequacy": {
    status: "pass",            // 当两个维度均为参考级(默认设置)时,"pass"是如实记录;如果任一
                                // 维度的严重程度被配置为阻塞,则需报告实际结果
    summary: "mutationScore 0.62(阈值0.40);已将3个存活变异体转化为杀死测试的后续任务",
    issues: [
      { severity: "MEDIUM", category: "test-quality", file: "src/foo.ts", line: 42,
        description: "存活变异体——无断言可区分变异后的分支",
        required_fix: "编写测试以杀死src/foo.ts:42" }
    ]
  } }
}})
all-reviews-passed
防护要求状态为通过值(
pass | passed | approved | fixes-applied
,大小写不敏感)——纯文本字符串会被忽略并阻塞流程转换。

Anti-Patterns

反模式

Don'tDo Instead
Run
scope: "full"
inline
Full-tree mutation is the long-running op deferred to R10/v2.12 — it returns a deferred advisory, never an inline run
Compose
--since
/
--in-diff
by hand
Let the action resolve the diff scope from the toolchains SoT
Block the merge on a sub-threshold score with no config overrideThe score axis is advisory by default — surface follow-ups, honor the resolved severity
Assume NoCoverage is always advisory, or always blockingIt has its own real block-mode path (
review.mutationEnforcement: block
, default budget 0) — opt-in, just like the score's severity; resolve and honor the actual configured mode
Claim the CI mutation gate blocks mergesIt runs diff-scoped in observe mode unconditionally (logs its verdict, always exits 0, regardless of config) — never represent it as a merge blocker
Treat a Skipped/Warning carrier as a hard failureBoth return
passed: true
— report the reason, never throw
Run this dimension for medium/low tierIt gates the HIGH tier at the
/review
boundary only
Emit
gate.executed
manually
The action auto-emits liveness + the foldable gate event
Give generic "add more tests" adviceQuote the
<file>:<line>
the action surfaced in
next_actions
禁止操作正确做法
内联运行
scope: "full"
全代码树变异测试是推迟至R10/v2.12版本的长时操作——它会返回延迟的参考结果,而非内联运行
手动组合
--since
/
--in-diff
让动作从工具链的可信源解析差异范围
在无配置覆盖的情况下,因分数低于阈值而阻塞合并分数维度默认是参考级——显示后续任务,遵循最终生效的严重程度
假设NoCoverage始终是参考级或始终是阻塞级它拥有独立的阻塞模式路径(
review.mutationEnforcement: block
,默认预算0)——与分数的严重程度一样,为可选启用;解析并遵循实际配置的模式
声称CI变异检查门会阻塞合并它无条件以观察模式运行差异范围测试(记录验证结果,始终返回0退出码,与配置无关)——切勿将其描述为合并阻塞项
将Skipped/Warning载体数据视为严重失败两者均返回
passed: true
——报告原因,切勿抛出异常
为中/低风险层级运行该维度它仅在
/review
节点针对高风险层级生效
手动发送
gate.executed
事件
动作会自动发送活跃度事件和可折叠的检查门事件
给出“添加更多测试”这类通用建议引用动作在
next_actions
中返回的
<file>:<line>
信息

References

参考资料

  • references/reading-the-carrier.md
    — reading the carrier and the Stryker
    mutation-testing-report-schema
    .
  • references/advisory-threshold.md
    — why the threshold is a soft default, and how to calibrate it.
  • references/reading-the-carrier.md
    — 读取载体数据和Stryker
    mutation-testing-report-schema
    报告。
  • references/advisory-threshold.md
    — 为何阈值是软默认值,以及如何校准阈值。