BLOGE
0.9.8-RC1在线版 · 事实校验 2026-09-15 · English
第 27 章 —— 合同敏感性:Suite 能拒绝错误答案吗?
本章承诺: 把一个绿色贷款 Case 变成受控负向实验,分清
KILLED见证与SURVIVED盲区,同时不把两者误写成穷尽式 mutation coverage。
学习目标
- 解释“candidate 等于 expected”为何还不能证明合同会拒绝相邻错误答案。
- 使用
verifyContractSensitivity(Path)检查一个受治理的BUSINESS_CONTRACTExpectation。 - 分别读取
KILLED、SURVIVED、report status 和 source-bound evidence。 - 设计业务 Owner 能审阅的单变量 witness。
同一个绿色 Case 可能藏着两种完全不同的合同
低风险贷款 Case 期望 APPROVE,graph 也返回 APPROVE。现在设想两个 matcher:
- 精确合同:actual 必须等于
APPROVE; - 宽松合同:actual 只要不等于
DECLINE。
两者今天都会通过,但只有前者会拒绝 REVIEW。正向例子只能说明一个点匹配;sensitivity 问的是:选定错误答案是否越过合同边界。
改 expected,不改运行中的系统
sidecar 精确指向受治理的 Expectation,只替换它的 expected value:
schemaVersion: 1
sensitivityId: loan-decision-witnesses
scenario: loan-approval.scenario.yaml
mutants:
- mutantId: low-risk-must-not-review
requirementRef: LOAN-DECISION-001
caseId: auto-approve
expectationId: decision-approved
kind: EXPECTATION_VALUE_REPLACED
replacementExpected: REVIEW
VerificationContractSensitivityReport result =
verifier.verifyContractSensitivity(sensitivityPath);
BLOGE 只执行一次 baseline Case,再把真实 observation 投影到替代 expected value 上。它不修改生产代码、不换一个 Operator 重跑,也不发明业务规则。这个隔离很重要:如果实现和 Oracle 同时改变,失败就无法归因给合同敏感性。
观察状态转移,不只看最后徽章
RC1 测试 ContractSensitivityTest.passesOnlyWhenEveryRequiredWitnessIsKilled 使用精确 EQUALS Expectation。graph 仍观察到 APPROVE;把 expected 换成 REVIEW 后会被拒绝,因此 witness 为 KILLED、report 为 PASS,reasonCodes 为空。
配对测试 sealsASurvivingWitnessAsARecheckableSensitivityFailure 从更宽的“not review”式合同开始,再把 expected 换成 DECLINE。同一个 observation 仍被接纳;witness 变成 SURVIVED,report 变成 FAIL,发布后的 artifact 仍可作为 SOURCE_BOUND evidence 被独立复核。
| Observation | Witness outcome | Report | 含义 |
|---|---|---|---|
| 替代 expected 被拒绝 | KILLED | 只有全部必要 witness 都被杀死才 PASS | 合同能识别这个选定错误答案 |
| 替代 expected 仍被接纳 | SURVIVED | FAIL | matcher 或 Oracle 边界对这个 witness 过宽 |
| plan 指向缺失或不支 持的目标 | NOT_EVALUATED | INVALID | 实验本身无效 |
这里故意不对称:存活 witness 是有价值的失败 evidence,不能因为原业务 Case 是绿色就被隐藏。
从业务错误设计 witness
不要枚举随机字符串,先找业务能命名的错误:
- 选择一个受保护决策,例如“低风险申请不得进入人工复核”。
- 固定 Scenario、Policy、Fixture、graph、execution profile 和 source commit。
- 只把一个 expected value 换成一个可信的错误答案。
- 请业务 Owner 判断该 witness 是否代表真实决策边界。
- 通过评审后再把 witness 纳入版本控制。
贷款审批中,auto-approve Case 的 REVIEW、blacklist Case 的 APPROVE 都是有意义的 witness。胡乱填写的值可能只测到序列化,教不了读者业务合同。
实验:让一个 witness 存活
- 运行低风险 Case,记录 baseline
PASS和 actualAPPROVE。 - 加入上面的
REVIEWreplacement,确认精确 matcher 得到KILLED。 - 只把 matcher 改成也能接受
REVIEW的宽松条件。 - 再运行同一 sidecar,记录
SURVIVED和CONTRACT_MUTANT_SURVIVED:<mutantId>。 - 恢复精确 matcher并重跑,witness 必须回到
KILLED。 - 把 artifact receipt 保存到运行目录之外,再交给另一个 reader 复验。
这是两次运行、一个变量的实验:witness 固定,只改变合同严格程度,因此结果可解释。
现实迁移:折扣规则敏感性
在零售定价中,保持购物车和实现不变,只把期望的会员折扣从 10% 替换为 30%。
精确金额 matcher 应该杀死这个 witness;只检查 discount > 0 的宽松 matcher
会让它存活。这个结果只说明合同能否拒绝所选定价错误,不代表覆盖了所有促销组合。
本章边界
- Sensitivity 只覆盖作者声明的 mutation,不是全部规则和输出的 mutation coverage。
KILLED只证明拒绝一个选定错误答案,不证明输入域上的正确性。SURVIVED定位歧义,但不替业务决定应该改 matcher 还是 Oracle。- Source-bound 发布让结果可复核,不会让工具成为 业务 Owner。
实验验收卡
- 预期与观察: 同一 observation 在精确与宽松合同下得到 KILLED 与 SURVIVED。
- 失败与恢复: 放宽一条 expectation 让 witness 存活;恢复精确答案。
- 证明边界: 证明已声明 witness 的敏感性,不证明发现全部盲点。
- 练习合同: 绿色 Case 和一个 witness;只改一条 expectation;交付状态转移;错误 witness 被 KILLED 即停止。
小结
绿色例子只说“这个答案匹配”;被杀死的 witness 增加“这个选定错误答案不会匹配”;存活 witness 暴露合同过宽的位置。第 28 章会再次换问题:不再只试一个人工挑选的错误答案,而是探索有限输入域并缩小真实失败。
Coding Agent: Open the versioned task guide.